xref: /redis-3.2.3/tests/cluster/cluster.tcl (revision 28343966)
1# Cluster-specific test functions.
2#
3# Copyright (C) 2014 Salvatore Sanfilippo [email protected]
4# This software is released under the BSD License. See the COPYING file for
5# more information.
6
7# Returns a parsed CLUSTER NODES output as a list of dictionaries.
8proc get_cluster_nodes id {
9    set lines [split [R $id cluster nodes] "\r\n"]
10    set nodes {}
11    foreach l $lines {
12        set l [string trim $l]
13        if {$l eq {}} continue
14        set args [split $l]
15        set node [dict create \
16            id [lindex $args 0] \
17            addr [lindex $args 1] \
18            flags [split [lindex $args 2] ,] \
19            slaveof [lindex $args 3] \
20            ping_sent [lindex $args 4] \
21            pong_recv [lindex $args 5] \
22            config_epoch [lindex $args 6] \
23            linkstate [lindex $args 7] \
24            slots [lrange $args 8 -1] \
25        ]
26        lappend nodes $node
27    }
28    return $nodes
29}
30
31# Test node for flag.
32proc has_flag {node flag} {
33    expr {[lsearch -exact [dict get $node flags] $flag] != -1}
34}
35
36# Returns the parsed myself node entry as a dictionary.
37proc get_myself id {
38    set nodes [get_cluster_nodes $id]
39    foreach n $nodes {
40        if {[has_flag $n myself]} {return $n}
41    }
42    return {}
43}
44
45# Return the value of the specified CLUSTER INFO field.
46proc CI {n field} {
47    get_info_field [R $n cluster info] $field
48}
49
50# Assuming nodes are reest, this function performs slots allocation.
51# Only the first 'n' nodes are used.
52proc cluster_allocate_slots {n} {
53    set slot 16383
54    while {$slot >= 0} {
55        # Allocate successive slots to random nodes.
56        set node [randomInt $n]
57        lappend slots_$node $slot
58        incr slot -1
59    }
60    for {set j 0} {$j < $n} {incr j} {
61        R $j cluster addslots {*}[set slots_${j}]
62    }
63}
64
65# Check that cluster nodes agree about "state", or raise an error.
66proc assert_cluster_state {state} {
67    foreach_redis_id id {
68        if {[instance_is_killed redis $id]} continue
69        wait_for_condition 1000 50 {
70            [CI $id cluster_state] eq $state
71        } else {
72            fail "Cluster node $id cluster_state:[CI $id cluster_state]"
73        }
74    }
75}
76
77# Search the first node starting from ID $first that is not
78# already configured as a slave.
79proc cluster_find_available_slave {first} {
80    foreach_redis_id id {
81        if {$id < $first} continue
82        if {[instance_is_killed redis $id]} continue
83        set me [get_myself $id]
84        if {[dict get $me slaveof] eq {-}} {return $id}
85    }
86    fail "No available slaves"
87}
88
89# Add 'slaves' slaves to a cluster composed of 'masters' masters.
90# It assumes that masters are allocated sequentially from instance ID 0
91# to N-1.
92proc cluster_allocate_slaves {masters slaves} {
93    for {set j 0} {$j < $slaves} {incr j} {
94        set master_id [expr {$j % $masters}]
95        set slave_id [cluster_find_available_slave $masters]
96        set master_myself [get_myself $master_id]
97        R $slave_id cluster replicate [dict get $master_myself id]
98    }
99}
100
101# Create a cluster composed of the specified number of masters and slaves.
102proc create_cluster {masters slaves} {
103    cluster_allocate_slots $masters
104    if {$slaves} {
105        cluster_allocate_slaves $masters $slaves
106    }
107    assert_cluster_state ok
108}
109
110# Set the cluster node-timeout to all the reachalbe nodes.
111proc set_cluster_node_timeout {to} {
112    foreach_redis_id id {
113        catch {R $id CONFIG SET cluster-node-timeout $to}
114    }
115}
116
117# Check if the cluster is writable and readable. Use node "id"
118# as a starting point to talk with the cluster.
119proc cluster_write_test {id} {
120    set prefix [randstring 20 20 alpha]
121    set port [get_instance_attrib redis $id port]
122    set cluster [redis_cluster 127.0.0.1:$port]
123    for {set j 0} {$j < 100} {incr j} {
124        $cluster set key.$j $prefix.$j
125    }
126    for {set j 0} {$j < 100} {incr j} {
127        assert {[$cluster get key.$j] eq "$prefix.$j"}
128    }
129    $cluster close
130}
131