summaryrefslogtreecommitdiff
path: root/examples/redis-unstable/tests/cluster
diff options
context:
space:
mode:
Diffstat (limited to 'examples/redis-unstable/tests/cluster')
-rw-r--r--examples/redis-unstable/tests/cluster/cluster.tcl270
-rw-r--r--examples/redis-unstable/tests/cluster/run.tcl37
-rw-r--r--examples/redis-unstable/tests/cluster/tests/00-base.tcl93
-rw-r--r--examples/redis-unstable/tests/cluster/tests/01-faildet.tcl38
-rw-r--r--examples/redis-unstable/tests/cluster/tests/02-failover.tcl65
-rw-r--r--examples/redis-unstable/tests/cluster/tests/03-failover-loop.tcl117
-rw-r--r--examples/redis-unstable/tests/cluster/tests/04-resharding.tcl196
-rw-r--r--examples/redis-unstable/tests/cluster/tests/05-slave-selection.tcl191
-rw-r--r--examples/redis-unstable/tests/cluster/tests/06-slave-stop-cond.tcl77
-rw-r--r--examples/redis-unstable/tests/cluster/tests/07-replica-migration.tcl103
-rw-r--r--examples/redis-unstable/tests/cluster/tests/08-update-msg.tcl90
-rw-r--r--examples/redis-unstable/tests/cluster/tests/09-pubsub.tcl40
-rw-r--r--examples/redis-unstable/tests/cluster/tests/10-manual-failover.tcl192
-rw-r--r--examples/redis-unstable/tests/cluster/tests/11-manual-takeover.tcl71
-rw-r--r--examples/redis-unstable/tests/cluster/tests/12-replica-migration-2.tcl75
-rw-r--r--examples/redis-unstable/tests/cluster/tests/12.1-replica-migration-3.tcl65
-rw-r--r--examples/redis-unstable/tests/cluster/tests/13-no-failover-option.tcl61
-rw-r--r--examples/redis-unstable/tests/cluster/tests/14-consistency-check.tcl141
-rw-r--r--examples/redis-unstable/tests/cluster/tests/15-cluster-slots.tcl155
-rw-r--r--examples/redis-unstable/tests/cluster/tests/16-transactions-on-replica.tcl85
-rw-r--r--examples/redis-unstable/tests/cluster/tests/17-diskless-load-swapdb.tcl93
-rw-r--r--examples/redis-unstable/tests/cluster/tests/18-info.tcl45
-rw-r--r--examples/redis-unstable/tests/cluster/tests/19-cluster-nodes-slots.tcl50
-rw-r--r--examples/redis-unstable/tests/cluster/tests/20-half-migrated-slot.tcl98
-rw-r--r--examples/redis-unstable/tests/cluster/tests/21-many-slot-migration.tcl64
-rw-r--r--examples/redis-unstable/tests/cluster/tests/22-replica-in-sync.tcl146
-rw-r--r--examples/redis-unstable/tests/cluster/tests/25-pubsubshard-slot-migration.tcl212
-rw-r--r--examples/redis-unstable/tests/cluster/tests/26-pubsubshard.tcl130
-rw-r--r--examples/redis-unstable/tests/cluster/tests/28-cluster-shards.tcl292
-rw-r--r--examples/redis-unstable/tests/cluster/tests/29-slot-migration-response.tcl50
-rw-r--r--examples/redis-unstable/tests/cluster/tests/helpers/onlydots.tcl16
-rw-r--r--examples/redis-unstable/tests/cluster/tests/includes/init-tests.tcl102
-rw-r--r--examples/redis-unstable/tests/cluster/tests/includes/utils.tcl36
-rw-r--r--examples/redis-unstable/tests/cluster/tmp/.gitignore2
34 files changed, 0 insertions, 3498 deletions
diff --git a/examples/redis-unstable/tests/cluster/cluster.tcl b/examples/redis-unstable/tests/cluster/cluster.tcl
deleted file mode 100644
index 15218d1..0000000
--- a/examples/redis-unstable/tests/cluster/cluster.tcl
+++ /dev/null
@@ -1,270 +0,0 @@
1# Cluster-specific test functions.
2#
3# Copyright (C) 2014-Present, Redis Ltd.
4# All Rights reserved.
5#
6# Licensed under your choice of (a) the Redis Source Available License 2.0
7# (RSALv2); or (b) the Server Side Public License v1 (SSPLv1); or (c) the
8# GNU Affero General Public License v3 (AGPLv3).
9
10# Track cluster configuration as created by create_cluster below
11set ::cluster_master_nodes 0
12set ::cluster_replica_nodes 0
13
14# Returns a parsed CLUSTER NODES output as a list of dictionaries. Optional status field
15# can be specified to only returns entries that match the provided status.
16proc get_cluster_nodes {id {status "*"}} {
17 set lines [split [R $id cluster nodes] "\r\n"]
18 set nodes {}
19 foreach l $lines {
20 set l [string trim $l]
21 if {$l eq {}} continue
22 set args [split $l]
23 set node [dict create \
24 id [lindex $args 0] \
25 addr [lindex $args 1] \
26 flags [split [lindex $args 2] ,] \
27 slaveof [lindex $args 3] \
28 ping_sent [lindex $args 4] \
29 pong_recv [lindex $args 5] \
30 config_epoch [lindex $args 6] \
31 linkstate [lindex $args 7] \
32 slots [lrange $args 8 end] \
33 ]
34 if {[string match $status [lindex $args 7]]} {
35 lappend nodes $node
36 }
37 }
38 return $nodes
39}
40
41# Test node for flag.
42proc has_flag {node flag} {
43 expr {[lsearch -exact [dict get $node flags] $flag] != -1}
44}
45
46# Returns the parsed myself node entry as a dictionary.
47proc get_myself id {
48 set nodes [get_cluster_nodes $id]
49 foreach n $nodes {
50 if {[has_flag $n myself]} {return $n}
51 }
52 return {}
53}
54
55# Get a specific node by ID by parsing the CLUSTER NODES output
56# of the instance Number 'instance_id'
57proc get_node_by_id {instance_id node_id} {
58 set nodes [get_cluster_nodes $instance_id]
59 foreach n $nodes {
60 if {[dict get $n id] eq $node_id} {return $n}
61 }
62 return {}
63}
64
65# Return the value of the specified CLUSTER INFO field.
66proc CI {n field} {
67 get_info_field [R $n cluster info] $field
68}
69
70# Return the value of the specified INFO field.
71proc s {n field} {
72 get_info_field [R $n info] $field
73}
74
75# Assuming nodes are reset, this function performs slots allocation.
76# Only the first 'n' nodes are used.
77proc cluster_allocate_slots {n} {
78 set slot 16383
79 while {$slot >= 0} {
80 # Allocate successive slots to random nodes.
81 set node [randomInt $n]
82 lappend slots_$node $slot
83 incr slot -1
84 }
85 for {set j 0} {$j < $n} {incr j} {
86 R $j cluster addslots {*}[set slots_${j}]
87 }
88}
89
90# Check that cluster nodes agree about "state", or raise an error.
91proc assert_cluster_state {state} {
92 foreach_redis_id id {
93 if {[instance_is_killed redis $id]} continue
94 wait_for_condition 1000 50 {
95 [CI $id cluster_state] eq $state
96 } else {
97 fail "Cluster node $id cluster_state:[CI $id cluster_state]"
98 }
99 }
100
101 wait_for_secrets_match 50 100
102}
103
104proc num_unique_secrets {} {
105 set secrets [list]
106 foreach_redis_id id {
107 if {[instance_is_killed redis $id]} continue
108 lappend secrets [R $id debug internal_secret]
109 }
110 set num_secrets [llength [lsort -unique $secrets]]
111 return $num_secrets
112}
113
114# Check that cluster nodes agree about "state", or raise an error.
115proc assert_secrets_match {} {
116 assert_equal {1} [num_unique_secrets]
117}
118
119proc wait_for_secrets_match {maxtries delay} {
120 wait_for_condition $maxtries $delay {
121 [num_unique_secrets] eq 1
122 } else {
123 fail "Failed waiting for secrets to sync"
124 }
125}
126
127# Search the first node starting from ID $first that is not
128# already configured as a slave.
129proc cluster_find_available_slave {first} {
130 foreach_redis_id id {
131 if {$id < $first} continue
132 if {[instance_is_killed redis $id]} continue
133 set me [get_myself $id]
134 if {[dict get $me slaveof] eq {-}} {return $id}
135 }
136 fail "No available slaves"
137}
138
139# Add 'slaves' slaves to a cluster composed of 'masters' masters.
140# It assumes that masters are allocated sequentially from instance ID 0
141# to N-1.
142proc cluster_allocate_slaves {masters slaves} {
143 for {set j 0} {$j < $slaves} {incr j} {
144 set master_id [expr {$j % $masters}]
145 set slave_id [cluster_find_available_slave $masters]
146 set master_myself [get_myself $master_id]
147 R $slave_id cluster replicate [dict get $master_myself id]
148 }
149}
150
151# Create a cluster composed of the specified number of masters and slaves.
152proc create_cluster {masters slaves} {
153 cluster_allocate_slots $masters
154 if {$slaves} {
155 cluster_allocate_slaves $masters $slaves
156 }
157 assert_cluster_state ok
158
159 set ::cluster_master_nodes $masters
160 set ::cluster_replica_nodes $slaves
161}
162
163proc cluster_allocate_with_continuous_slots {n} {
164 set slot 16383
165 set avg [expr ($slot+1) / $n]
166 while {$slot >= 0} {
167 set node [expr $slot/$avg >= $n ? $n-1 : $slot/$avg]
168 lappend slots_$node $slot
169 incr slot -1
170 }
171 for {set j 0} {$j < $n} {incr j} {
172 R $j cluster addslots {*}[set slots_${j}]
173 }
174}
175
176# Create a cluster composed of the specified number of masters and slaves,
177# but with a continuous slot range.
178proc cluster_create_with_continuous_slots {masters slaves} {
179 cluster_allocate_with_continuous_slots $masters
180 if {$slaves} {
181 cluster_allocate_slaves $masters $slaves
182 }
183 assert_cluster_state ok
184
185 set ::cluster_master_nodes $masters
186 set ::cluster_replica_nodes $slaves
187}
188
189
190# Set the cluster node-timeout to all the reachalbe nodes.
191proc set_cluster_node_timeout {to} {
192 foreach_redis_id id {
193 catch {R $id CONFIG SET cluster-node-timeout $to}
194 }
195}
196
197# Check if the cluster is writable and readable. Use node "id"
198# as a starting point to talk with the cluster.
199proc cluster_write_test {id} {
200 set prefix [randstring 20 20 alpha]
201 set port [get_instance_attrib redis $id port]
202 set cluster [redis_cluster 127.0.0.1:$port]
203 for {set j 0} {$j < 100} {incr j} {
204 $cluster set key.$j $prefix.$j
205 }
206 for {set j 0} {$j < 100} {incr j} {
207 assert {[$cluster get key.$j] eq "$prefix.$j"}
208 }
209 $cluster close
210}
211
212# Normalize cluster slots configuration by sorting replicas by node ID
213proc normalize_cluster_slots {slots_config} {
214 set normalized {}
215 foreach slot_range $slots_config {
216 if {[llength $slot_range] <= 3} {
217 lappend normalized $slot_range
218 } else {
219 # Sort replicas (index 3+) by node ID, keep start/end/master unchanged
220 set replicas [lrange $slot_range 3 end]
221 set sorted_replicas [lsort -index 2 $replicas]
222 lappend normalized [concat [lrange $slot_range 0 2] $sorted_replicas]
223 }
224 }
225 return $normalized
226}
227
228# Check if cluster configuration is consistent.
229proc cluster_config_consistent {} {
230 for {set j 0} {$j < $::cluster_master_nodes + $::cluster_replica_nodes} {incr j} {
231 if {$j == 0} {
232 set base_cfg [R $j cluster slots]
233 set base_secret [R $j debug internal_secret]
234 set normalized_base_cfg [normalize_cluster_slots $base_cfg]
235 } else {
236 set cfg [R $j cluster slots]
237 set secret [R $j debug internal_secret]
238 set normalized_cfg [normalize_cluster_slots $cfg]
239 if {$normalized_cfg != $normalized_base_cfg || $secret != $base_secret} {
240 return 0
241 }
242 }
243 }
244
245 return 1
246}
247
248# Wait for cluster configuration to propagate and be consistent across nodes.
249proc wait_for_cluster_propagation {} {
250 wait_for_condition 50 100 {
251 [cluster_config_consistent] eq 1
252 } else {
253 fail "cluster config did not reach a consistent state"
254 }
255}
256
257# Check if cluster's view of hostnames is consistent
258proc are_hostnames_propagated {match_string} {
259 for {set j 0} {$j < $::cluster_master_nodes + $::cluster_replica_nodes} {incr j} {
260 set cfg [R $j cluster slots]
261 foreach node $cfg {
262 for {set i 2} {$i < [llength $node]} {incr i} {
263 if {! [string match $match_string [lindex [lindex [lindex $node $i] 3] 1]] } {
264 return 0
265 }
266 }
267 }
268 }
269 return 1
270}
diff --git a/examples/redis-unstable/tests/cluster/run.tcl b/examples/redis-unstable/tests/cluster/run.tcl
deleted file mode 100644
index 1deb9e2..0000000
--- a/examples/redis-unstable/tests/cluster/run.tcl
+++ /dev/null
@@ -1,37 +0,0 @@
1# Cluster test suite.
2#
3# Copyright (C) 2014-Present, Redis Ltd.
4# All Rights reserved.
5#
6# Licensed under your choice of (a) the Redis Source Available License 2.0
7# (RSALv2); or (b) the Server Side Public License v1 (SSPLv1); or (c) the
8# GNU Affero General Public License v3 (AGPLv3).
9
10cd tests/cluster
11source cluster.tcl
12source ../instances.tcl
13source ../../support/cluster.tcl ; # Redis Cluster client.
14
15set ::instances_count 20 ; # How many instances we use at max.
16set ::tlsdir "../../tls"
17
18proc main {} {
19 parse_options
20 spawn_instance redis $::redis_base_port $::instances_count {
21 "cluster-enabled yes"
22 "appendonly yes"
23 "enable-protected-configs yes"
24 "enable-debug-command yes"
25 "save ''"
26 }
27 run_tests
28 cleanup
29 end_tests
30}
31
32if {[catch main e]} {
33 puts $::errorInfo
34 if {$::pause_on_error} pause_on_error
35 cleanup
36 exit 1
37}
diff --git a/examples/redis-unstable/tests/cluster/tests/00-base.tcl b/examples/redis-unstable/tests/cluster/tests/00-base.tcl
deleted file mode 100644
index 514087d..0000000
--- a/examples/redis-unstable/tests/cluster/tests/00-base.tcl
+++ /dev/null
@@ -1,93 +0,0 @@
1# Check the basic monitoring and failover capabilities.
2
3source "../tests/includes/init-tests.tcl"
4
5if {$::simulate_error} {
6 test "This test will fail" {
7 fail "Simulated error"
8 }
9}
10
11test "Different nodes have different IDs" {
12 set ids {}
13 set numnodes 0
14 foreach_redis_id id {
15 incr numnodes
16 # Every node should just know itself.
17 set nodeid [dict get [get_myself $id] id]
18 assert {$nodeid ne {}}
19 lappend ids $nodeid
20 }
21 set numids [llength [lsort -unique $ids]]
22 assert {$numids == $numnodes}
23}
24
25test "It is possible to perform slot allocation" {
26 cluster_allocate_slots 5
27}
28
29test "After the join, every node gets a different config epoch" {
30 set trynum 60
31 while {[incr trynum -1] != 0} {
32 # We check that this condition is true for *all* the nodes.
33 set ok 1 ; # Will be set to 0 every time a node is not ok.
34 foreach_redis_id id {
35 set epochs {}
36 foreach n [get_cluster_nodes $id] {
37 lappend epochs [dict get $n config_epoch]
38 }
39 if {[lsort $epochs] != [lsort -unique $epochs]} {
40 set ok 0 ; # At least one collision!
41 }
42 }
43 if {$ok} break
44 after 1000
45 puts -nonewline .
46 flush stdout
47 }
48 if {$trynum == 0} {
49 fail "Config epoch conflict resolution is not working."
50 }
51}
52
53test "Nodes should report cluster_state is ok now" {
54 assert_cluster_state ok
55}
56
57test "Sanity for CLUSTER COUNTKEYSINSLOT" {
58 set reply [R 0 CLUSTER COUNTKEYSINSLOT 0]
59 assert {$reply eq 0}
60}
61
62test "It is possible to write and read from the cluster" {
63 cluster_write_test 0
64}
65
66test "CLUSTER RESET SOFT test" {
67 set last_epoch_node0 [get_info_field [R 0 cluster info] cluster_current_epoch]
68 R 0 FLUSHALL
69 R 0 CLUSTER RESET
70 assert {[get_info_field [R 0 cluster info] cluster_current_epoch] eq $last_epoch_node0}
71
72 set last_epoch_node1 [get_info_field [R 1 cluster info] cluster_current_epoch]
73 R 1 FLUSHALL
74 R 1 CLUSTER RESET SOFT
75 assert {[get_info_field [R 1 cluster info] cluster_current_epoch] eq $last_epoch_node1}
76}
77
78test "Coverage: CLUSTER HELP" {
79 assert_match "*CLUSTER <subcommand> *" [R 0 CLUSTER HELP]
80}
81
82test "Coverage: ASKING" {
83 assert_equal {OK} [R 0 ASKING]
84}
85
86test "CLUSTER SLAVES and CLUSTER REPLICAS with zero replicas" {
87 assert_equal {} [R 0 cluster slaves [R 0 CLUSTER MYID]]
88 assert_equal {} [R 0 cluster replicas [R 0 CLUSTER MYID]]
89}
90
91test "CLUSTER FORGET with invalid node ID" {
92 assert_error {*ERR Unknown node*} {R 0 cluster forget 1}
93}
diff --git a/examples/redis-unstable/tests/cluster/tests/01-faildet.tcl b/examples/redis-unstable/tests/cluster/tests/01-faildet.tcl
deleted file mode 100644
index 8fe87c9..0000000
--- a/examples/redis-unstable/tests/cluster/tests/01-faildet.tcl
+++ /dev/null
@@ -1,38 +0,0 @@
1# Check the basic monitoring and failover capabilities.
2
3source "../tests/includes/init-tests.tcl"
4
5test "Create a 5 nodes cluster" {
6 create_cluster 5 5
7}
8
9test "Cluster should start ok" {
10 assert_cluster_state ok
11}
12
13test "Killing two slave nodes" {
14 kill_instance redis 5
15 kill_instance redis 6
16}
17
18test "Cluster should be still up" {
19 assert_cluster_state ok
20}
21
22test "Killing one master node" {
23 kill_instance redis 0
24}
25
26# Note: the only slave of instance 0 is already down so no
27# failover is possible, that would change the state back to ok.
28test "Cluster should be down now" {
29 assert_cluster_state fail
30}
31
32test "Restarting master node" {
33 restart_instance redis 0
34}
35
36test "Cluster should be up again" {
37 assert_cluster_state ok
38}
diff --git a/examples/redis-unstable/tests/cluster/tests/02-failover.tcl b/examples/redis-unstable/tests/cluster/tests/02-failover.tcl
deleted file mode 100644
index 6b2fd09..0000000
--- a/examples/redis-unstable/tests/cluster/tests/02-failover.tcl
+++ /dev/null
@@ -1,65 +0,0 @@
1# Check the basic monitoring and failover capabilities.
2
3source "../tests/includes/init-tests.tcl"
4
5test "Create a 5 nodes cluster" {
6 create_cluster 5 5
7}
8
9test "Cluster is up" {
10 assert_cluster_state ok
11}
12
13test "Cluster is writable" {
14 cluster_write_test 0
15}
16
17test "Instance #5 is a slave" {
18 assert {[RI 5 role] eq {slave}}
19}
20
21test "Instance #5 synced with the master" {
22 wait_for_condition 1000 50 {
23 [RI 5 master_link_status] eq {up}
24 } else {
25 fail "Instance #5 master link status is not up"
26 }
27}
28
29set current_epoch [CI 1 cluster_current_epoch]
30
31test "Killing one master node" {
32 kill_instance redis 0
33}
34
35test "Wait for failover" {
36 wait_for_condition 1000 50 {
37 [CI 1 cluster_current_epoch] > $current_epoch
38 } else {
39 fail "No failover detected"
40 }
41}
42
43test "Cluster should eventually be up again" {
44 assert_cluster_state ok
45}
46
47test "Cluster is writable" {
48 cluster_write_test 1
49}
50
51test "Instance #5 is now a master" {
52 assert {[RI 5 role] eq {master}}
53}
54
55test "Restarting the previously killed master node" {
56 restart_instance redis 0
57}
58
59test "Instance #0 gets converted into a slave" {
60 wait_for_condition 1000 50 {
61 [RI 0 role] eq {slave}
62 } else {
63 fail "Old master was not converted into slave"
64 }
65}
diff --git a/examples/redis-unstable/tests/cluster/tests/03-failover-loop.tcl b/examples/redis-unstable/tests/cluster/tests/03-failover-loop.tcl
deleted file mode 100644
index 46c22a9..0000000
--- a/examples/redis-unstable/tests/cluster/tests/03-failover-loop.tcl
+++ /dev/null
@@ -1,117 +0,0 @@
1# Failover stress test.
2# In this test a different node is killed in a loop for N
3# iterations. The test checks that certain properties
4# are preserved across iterations.
5
6source "../tests/includes/init-tests.tcl"
7
8test "Create a 5 nodes cluster" {
9 create_cluster 5 5
10}
11
12test "Cluster is up" {
13 assert_cluster_state ok
14}
15
16set iterations 20
17set cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]]
18
19while {[incr iterations -1]} {
20 set tokill [randomInt 10]
21 set other [expr {($tokill+1)%10}] ; # Some other instance.
22 set key [randstring 20 20 alpha]
23 set val [randstring 20 20 alpha]
24 set role [RI $tokill role]
25 if {$role eq {master}} {
26 set slave {}
27 set myid [dict get [get_myself $tokill] id]
28 foreach_redis_id id {
29 if {$id == $tokill} continue
30 if {[dict get [get_myself $id] slaveof] eq $myid} {
31 set slave $id
32 }
33 }
34 if {$slave eq {}} {
35 fail "Unable to retrieve slave's ID for master #$tokill"
36 }
37 }
38
39 puts "--- Iteration $iterations ---"
40
41 if {$role eq {master}} {
42 test "Wait for slave of #$tokill to sync" {
43 wait_for_condition 1000 50 {
44 [string match {*state=online*} [RI $tokill slave0]]
45 } else {
46 fail "Slave of node #$tokill is not ok"
47 }
48 }
49 set slave_config_epoch [CI $slave cluster_my_epoch]
50 }
51
52 test "Cluster is writable before failover" {
53 for {set i 0} {$i < 100} {incr i} {
54 catch {$cluster set $key:$i $val:$i} err
55 assert {$err eq {OK}}
56 }
57 # Wait for the write to propagate to the slave if we
58 # are going to kill a master.
59 if {$role eq {master}} {
60 R $tokill wait 1 20000
61 }
62 }
63
64 test "Terminating node #$tokill" {
65 # Stop AOF so that an initial AOFRW won't prevent the instance from terminating
66 R $tokill config set appendonly no
67 kill_instance redis $tokill
68 }
69
70 if {$role eq {master}} {
71 test "Wait failover by #$slave with old epoch $slave_config_epoch" {
72 wait_for_condition 1000 50 {
73 [CI $slave cluster_my_epoch] > $slave_config_epoch
74 } else {
75 fail "No failover detected, epoch is still [CI $slave cluster_my_epoch]"
76 }
77 }
78 }
79
80 test "Cluster should eventually be up again" {
81 assert_cluster_state ok
82 }
83
84 test "Cluster is writable again" {
85 for {set i 0} {$i < 100} {incr i} {
86 catch {$cluster set $key:$i:2 $val:$i:2} err
87 assert {$err eq {OK}}
88 }
89 }
90
91 test "Restarting node #$tokill" {
92 restart_instance redis $tokill
93 }
94
95 test "Instance #$tokill is now a slave" {
96 wait_for_condition 1000 50 {
97 [RI $tokill role] eq {slave}
98 } else {
99 fail "Restarted instance is not a slave"
100 }
101 }
102
103 test "We can read back the value we set before" {
104 for {set i 0} {$i < 100} {incr i} {
105 catch {$cluster get $key:$i} err
106 assert {$err eq "$val:$i"}
107 catch {$cluster get $key:$i:2} err
108 assert {$err eq "$val:$i:2"}
109 }
110 }
111}
112
113test "Post condition: current_epoch >= my_epoch everywhere" {
114 foreach_redis_id id {
115 assert {[CI $id cluster_current_epoch] >= [CI $id cluster_my_epoch]}
116 }
117}
diff --git a/examples/redis-unstable/tests/cluster/tests/04-resharding.tcl b/examples/redis-unstable/tests/cluster/tests/04-resharding.tcl
deleted file mode 100644
index 18a26bd..0000000
--- a/examples/redis-unstable/tests/cluster/tests/04-resharding.tcl
+++ /dev/null
@@ -1,196 +0,0 @@
1# Failover stress test.
2# In this test a different node is killed in a loop for N
3# iterations. The test checks that certain properties
4# are preserved across iterations.
5
6source "../tests/includes/init-tests.tcl"
7source "../../../tests/support/cli.tcl"
8
9test "Create a 5 nodes cluster" {
10 create_cluster 5 5
11}
12
13test "Cluster is up" {
14 assert_cluster_state ok
15}
16
17test "Enable AOF in all the instances" {
18 foreach_redis_id id {
19 R $id config set appendonly yes
20 # We use "appendfsync no" because it's fast but also guarantees that
21 # write(2) is performed before replying to client.
22 R $id config set appendfsync no
23 }
24
25 foreach_redis_id id {
26 wait_for_condition 1000 500 {
27 [RI $id aof_rewrite_in_progress] == 0 &&
28 [RI $id aof_enabled] == 1
29 } else {
30 fail "Failed to enable AOF on instance #$id"
31 }
32 }
33}
34
35# Return non-zero if the specified PID is about a process still in execution,
36# otherwise 0 is returned.
37proc process_is_running {pid} {
38 # PS should return with an error if PID is non existing,
39 # and catch will return non-zero. We want to return non-zero if
40 # the PID exists, so we invert the return value with expr not operator.
41 expr {![catch {exec ps -p $pid}]}
42}
43
44# Our resharding test performs the following actions:
45#
46# - N commands are sent to the cluster in the course of the test.
47# - Every command selects a random key from key:0 to key:MAX-1.
48# - The operation RPUSH key <randomvalue> is performed.
49# - Tcl remembers into an array all the values pushed to each list.
50# - After N/2 commands, the resharding process is started in background.
51# - The test continues while the resharding is in progress.
52# - At the end of the test, we wait for the resharding process to stop.
53# - Finally the keys are checked to see if they contain the value they should.
54
55set numkeys 50000
56set numops 200000
57set start_node_port [get_instance_attrib redis 0 port]
58set cluster [redis_cluster 127.0.0.1:$start_node_port]
59if {$::tls} {
60 # setup a non-TLS cluster client to the TLS cluster
61 set plaintext_port [get_instance_attrib redis 0 plaintext-port]
62 set cluster_plaintext [redis_cluster 127.0.0.1:$plaintext_port 0]
63 puts "Testing TLS cluster on start node 127.0.0.1:$start_node_port, plaintext port $plaintext_port"
64} else {
65 set cluster_plaintext $cluster
66 puts "Testing using non-TLS cluster"
67}
68catch {unset content}
69array set content {}
70set tribpid {}
71
72test "Cluster consistency during live resharding" {
73 set ele 0
74 for {set j 0} {$j < $numops} {incr j} {
75 # Trigger the resharding once we execute half the ops.
76 if {$tribpid ne {} &&
77 ($j % 10000) == 0 &&
78 ![process_is_running $tribpid]} {
79 set tribpid {}
80 }
81
82 if {$j >= $numops/2 && $tribpid eq {}} {
83 puts -nonewline "...Starting resharding..."
84 flush stdout
85 set target [dict get [get_myself [randomInt 5]] id]
86 set tribpid [lindex [exec \
87 ../../../src/redis-cli --cluster reshard \
88 127.0.0.1:[get_instance_attrib redis 0 port] \
89 --cluster-from all \
90 --cluster-to $target \
91 --cluster-slots 100 \
92 --cluster-yes \
93 {*}[rediscli_tls_config "../../../tests"] \
94 | [info nameofexecutable] \
95 ../tests/helpers/onlydots.tcl \
96 &] 0]
97 }
98
99 # Write random data to random list.
100 set listid [randomInt $numkeys]
101 set key "key:$listid"
102 incr ele
103 # We write both with Lua scripts and with plain commands.
104 # This way we are able to stress Lua -> Redis command invocation
105 # as well, that has tests to prevent Lua to write into wrong
106 # hash slots.
107 # We also use both TLS and plaintext connections.
108 if {$listid % 3 == 0} {
109 $cluster rpush $key $ele
110 } elseif {$listid % 3 == 1} {
111 $cluster_plaintext rpush $key $ele
112 } else {
113 $cluster eval {redis.call("rpush",KEYS[1],ARGV[1])} 1 $key $ele
114 }
115 lappend content($key) $ele
116
117 if {($j % 1000) == 0} {
118 puts -nonewline W; flush stdout
119 }
120 }
121
122 # Wait for the resharding process to end
123 wait_for_condition 1000 500 {
124 [process_is_running $tribpid] == 0
125 } else {
126 fail "Resharding is not terminating after some time."
127 }
128
129}
130
131test "Verify $numkeys keys for consistency with logical content" {
132 # Check that the Redis Cluster content matches our logical content.
133 foreach {key value} [array get content] {
134 if {[$cluster lrange $key 0 -1] ne $value} {
135 fail "Key $key expected to hold '$value' but actual content is [$cluster lrange $key 0 -1]"
136 }
137 }
138}
139
140test "Terminate and restart all the instances" {
141 foreach_redis_id id {
142 # Stop AOF so that an initial AOFRW won't prevent the instance from terminating
143 R $id config set appendonly no
144 kill_instance redis $id
145 restart_instance redis $id
146 }
147}
148
149test "Cluster should eventually be up again" {
150 assert_cluster_state ok
151}
152
153test "Verify $numkeys keys after the restart" {
154 # Check that the Redis Cluster content matches our logical content.
155 foreach {key value} [array get content] {
156 if {[$cluster lrange $key 0 -1] ne $value} {
157 fail "Key $key expected to hold '$value' but actual content is [$cluster lrange $key 0 -1]"
158 }
159 }
160}
161
162test "Disable AOF in all the instances" {
163 foreach_redis_id id {
164 R $id config set appendonly no
165 }
166}
167
168test "Verify slaves consistency" {
169 set verified_masters 0
170 foreach_redis_id id {
171 set role [R $id role]
172 lassign $role myrole myoffset slaves
173 if {$myrole eq {slave}} continue
174 set masterport [get_instance_attrib redis $id port]
175 set masterdigest [R $id debug digest]
176 foreach_redis_id sid {
177 set srole [R $sid role]
178 if {[lindex $srole 0] eq {master}} continue
179 if {[lindex $srole 2] != $masterport} continue
180 wait_for_condition 1000 500 {
181 [R $sid debug digest] eq $masterdigest
182 } else {
183 fail "Master and slave data digest are different"
184 }
185 incr verified_masters
186 }
187 }
188 assert {$verified_masters >= 5}
189}
190
191test "Dump sanitization was skipped for migrations" {
192 set verified_masters 0
193 foreach_redis_id id {
194 assert {[RI $id dump_payload_sanitizations] == 0}
195 }
196}
diff --git a/examples/redis-unstable/tests/cluster/tests/05-slave-selection.tcl b/examples/redis-unstable/tests/cluster/tests/05-slave-selection.tcl
deleted file mode 100644
index c6b4888..0000000
--- a/examples/redis-unstable/tests/cluster/tests/05-slave-selection.tcl
+++ /dev/null
@@ -1,191 +0,0 @@
1# Slave selection test
2# Check the algorithm trying to pick the slave with the most complete history.
3
4source "../tests/includes/init-tests.tcl"
5
6# Create a cluster with 5 master and 10 slaves, so that we have 2
7# slaves for each master.
8test "Create a 5 nodes cluster" {
9 create_cluster 5 10
10}
11
12test "Cluster is up" {
13 assert_cluster_state ok
14}
15
16test "The first master has actually two slaves" {
17 wait_for_condition 1000 50 {
18 [llength [lindex [R 0 role] 2]] == 2
19 && [llength [R 0 cluster replicas [R 0 CLUSTER MYID]]] == 2
20 } else {
21 fail "replicas didn't connect"
22 }
23}
24
25test "CLUSTER SLAVES and CLUSTER REPLICAS output is consistent" {
26 # Because we already have command output that cover CLUSTER REPLICAS elsewhere,
27 # here we simply judge whether their output is consistent to cover CLUSTER SLAVES.
28 set myid [R 0 CLUSTER MYID]
29 R 0 multi
30 R 0 cluster slaves $myid
31 R 0 cluster replicas $myid
32 lassign [R 0 exec] res res2
33 assert_equal $res $res2
34}
35
36test {Slaves of #0 are instance #5 and #10 as expected} {
37 set port0 [get_instance_attrib redis 0 port]
38 assert {[lindex [R 5 role] 2] == $port0}
39 assert {[lindex [R 10 role] 2] == $port0}
40}
41
42test "Instance #5 and #10 synced with the master" {
43 wait_for_condition 1000 50 {
44 [RI 5 master_link_status] eq {up} &&
45 [RI 10 master_link_status] eq {up}
46 } else {
47 fail "Instance #5 or #10 master link status is not up"
48 }
49}
50
51set cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]]
52
53test "Slaves are both able to receive and acknowledge writes" {
54 for {set j 0} {$j < 100} {incr j} {
55 $cluster set $j $j
56 }
57 assert {[R 0 wait 2 60000] == 2}
58}
59
60test "Write data while slave #10 is paused and can't receive it" {
61 # Stop the slave with a multi/exec transaction so that the master will
62 # be killed as soon as it can accept writes again.
63 R 10 multi
64 R 10 debug sleep 10
65 R 10 client kill 127.0.0.1:$port0
66 R 10 deferred 1
67 R 10 exec
68
69 # Write some data the slave can't receive.
70 for {set j 0} {$j < 100} {incr j} {
71 $cluster set $j $j
72 }
73
74 # Prevent the master from accepting new slaves.
75 # Use a large pause value since we'll kill it anyway.
76 R 0 CLIENT PAUSE 60000
77
78 # Wait for the slave to return available again
79 R 10 deferred 0
80 assert {[R 10 read] eq {OK OK}}
81
82 # Kill the master so that a reconnection will not be possible.
83 kill_instance redis 0
84}
85
86test "Wait for instance #5 (and not #10) to turn into a master" {
87 wait_for_condition 1000 50 {
88 [RI 5 role] eq {master}
89 } else {
90 fail "No failover detected"
91 }
92}
93
94test "Wait for the node #10 to return alive before ending the test" {
95 R 10 ping
96}
97
98test "Cluster should eventually be up again" {
99 assert_cluster_state ok
100}
101
102test "Node #10 should eventually replicate node #5" {
103 set port5 [get_instance_attrib redis 5 port]
104 wait_for_condition 1000 50 {
105 ([lindex [R 10 role] 2] == $port5) &&
106 ([lindex [R 10 role] 3] eq {connected})
107 } else {
108 fail "#10 didn't became slave of #5"
109 }
110}
111
112source "../tests/includes/init-tests.tcl"
113
114# Create a cluster with 3 master and 15 slaves, so that we have 5
115# slaves for eatch master.
116test "Create a 3 nodes cluster" {
117 create_cluster 3 15
118}
119
120test "Cluster is up" {
121 assert_cluster_state ok
122}
123
124test "The first master has actually 5 slaves" {
125 wait_for_condition 1000 50 {
126 [llength [lindex [R 0 role] 2]] == 5
127 } else {
128 fail "replicas didn't connect"
129 }
130}
131
132test {Slaves of #0 are instance #3, #6, #9, #12 and #15 as expected} {
133 set port0 [get_instance_attrib redis 0 port]
134 assert {[lindex [R 3 role] 2] == $port0}
135 assert {[lindex [R 6 role] 2] == $port0}
136 assert {[lindex [R 9 role] 2] == $port0}
137 assert {[lindex [R 12 role] 2] == $port0}
138 assert {[lindex [R 15 role] 2] == $port0}
139}
140
141test {Instance #3, #6, #9, #12 and #15 synced with the master} {
142 wait_for_condition 1000 50 {
143 [RI 3 master_link_status] eq {up} &&
144 [RI 6 master_link_status] eq {up} &&
145 [RI 9 master_link_status] eq {up} &&
146 [RI 12 master_link_status] eq {up} &&
147 [RI 15 master_link_status] eq {up}
148 } else {
149 fail "Instance #3 or #6 or #9 or #12 or #15 master link status is not up"
150 }
151}
152
153proc master_detected {instances} {
154 foreach instance [dict keys $instances] {
155 if {[RI $instance role] eq {master}} {
156 return true
157 }
158 }
159
160 return false
161}
162
163test "New Master down consecutively" {
164 set instances [dict create 0 1 3 1 6 1 9 1 12 1 15 1]
165
166 set loops [expr {[dict size $instances]-1}]
167 for {set i 0} {$i < $loops} {incr i} {
168 set master_id -1
169 foreach instance [dict keys $instances] {
170 if {[RI $instance role] eq {master}} {
171 set master_id $instance
172 break;
173 }
174 }
175
176 if {$master_id eq -1} {
177 fail "no master detected, #loop $i"
178 }
179
180 set instances [dict remove $instances $master_id]
181
182 kill_instance redis $master_id
183 wait_for_condition 1000 50 {
184 [master_detected $instances]
185 } else {
186 fail "No failover detected when master $master_id fails"
187 }
188
189 assert_cluster_state ok
190 }
191}
diff --git a/examples/redis-unstable/tests/cluster/tests/06-slave-stop-cond.tcl b/examples/redis-unstable/tests/cluster/tests/06-slave-stop-cond.tcl
deleted file mode 100644
index 80a2d17..0000000
--- a/examples/redis-unstable/tests/cluster/tests/06-slave-stop-cond.tcl
+++ /dev/null
@@ -1,77 +0,0 @@
1# Slave stop condition test
2# Check that if there is a disconnection time limit, the slave will not try
3# to failover its master.
4
5source "../tests/includes/init-tests.tcl"
6
7# Create a cluster with 5 master and 5 slaves.
8test "Create a 5 nodes cluster" {
9 create_cluster 5 5
10}
11
12test "Cluster is up" {
13 assert_cluster_state ok
14}
15
16test "The first master has actually one slave" {
17 wait_for_condition 1000 50 {
18 [llength [lindex [R 0 role] 2]] == 1
19 } else {
20 fail "replicas didn't connect"
21 }
22}
23
24test {Slaves of #0 is instance #5 as expected} {
25 set port0 [get_instance_attrib redis 0 port]
26 assert {[lindex [R 5 role] 2] == $port0}
27}
28
29test "Instance #5 synced with the master" {
30 wait_for_condition 1000 50 {
31 [RI 5 master_link_status] eq {up}
32 } else {
33 fail "Instance #5 master link status is not up"
34 }
35}
36
37test "Lower the slave validity factor of #5 to the value of 2" {
38 assert {[R 5 config set cluster-slave-validity-factor 2] eq {OK}}
39}
40
41test "Break master-slave link and prevent further reconnections" {
42 # Stop the slave with a multi/exec transaction so that the master will
43 # be killed as soon as it can accept writes again.
44 R 5 multi
45 R 5 client kill 127.0.0.1:$port0
46 # here we should sleep 6 or more seconds (node_timeout * slave_validity)
47 # but the actual validity time is actually incremented by the
48 # repl-ping-slave-period value which is 10 seconds by default. So we
49 # need to wait more than 16 seconds.
50 R 5 debug sleep 20
51 R 5 deferred 1
52 R 5 exec
53
54 # Prevent the master from accepting new slaves.
55 # Use a large pause value since we'll kill it anyway.
56 R 0 CLIENT PAUSE 60000
57
58 # Wait for the slave to return available again
59 R 5 deferred 0
60 assert {[R 5 read] eq {OK OK}}
61
62 # Kill the master so that a reconnection will not be possible.
63 kill_instance redis 0
64}
65
66test "Slave #5 is reachable and alive" {
67 assert {[R 5 ping] eq {PONG}}
68}
69
70test "Slave #5 should not be able to failover" {
71 after 10000
72 assert {[RI 5 role] eq {slave}}
73}
74
75test "Cluster should be down" {
76 assert_cluster_state fail
77}
diff --git a/examples/redis-unstable/tests/cluster/tests/07-replica-migration.tcl b/examples/redis-unstable/tests/cluster/tests/07-replica-migration.tcl
deleted file mode 100644
index c4e9985..0000000
--- a/examples/redis-unstable/tests/cluster/tests/07-replica-migration.tcl
+++ /dev/null
@@ -1,103 +0,0 @@
1# Replica migration test.
2# Check that orphaned masters are joined by replicas of masters having
3# multiple replicas attached, according to the migration barrier settings.
4
5source "../tests/includes/init-tests.tcl"
6
7# Create a cluster with 5 master and 10 slaves, so that we have 2
8# slaves for each master.
9test "Create a 5 nodes cluster" {
10 create_cluster 5 10
11}
12
13test "Cluster is up" {
14 assert_cluster_state ok
15}
16
17test "Each master should have two replicas attached" {
18 foreach_redis_id id {
19 if {$id < 5} {
20 wait_for_condition 1000 50 {
21 [llength [lindex [R $id role] 2]] == 2
22 } else {
23 fail "Master #$id does not have 2 slaves as expected"
24 }
25 }
26 }
27}
28
29test "Killing all the slaves of master #0 and #1" {
30 kill_instance redis 5
31 kill_instance redis 10
32 kill_instance redis 6
33 kill_instance redis 11
34 after 4000
35}
36
37foreach_redis_id id {
38 if {$id < 5} {
39 test "Master #$id should have at least one replica" {
40 wait_for_condition 1000 50 {
41 [llength [lindex [R $id role] 2]] >= 1
42 } else {
43 fail "Master #$id has no replicas"
44 }
45 }
46 }
47}
48
49# Now test the migration to a master which used to be a slave, after
50# a failver.
51
52source "../tests/includes/init-tests.tcl"
53
54# Create a cluster with 5 master and 10 slaves, so that we have 2
55# slaves for each master.
56test "Create a 5 nodes cluster" {
57 create_cluster 5 10
58}
59
60test "Cluster is up" {
61 assert_cluster_state ok
62}
63
64test "Kill slave #7 of master #2. Only slave left is #12 now" {
65 kill_instance redis 7
66}
67
68set current_epoch [CI 1 cluster_current_epoch]
69
70test "Killing master node #2, #12 should failover" {
71 kill_instance redis 2
72}
73
74test "Wait for failover" {
75 wait_for_condition 1000 50 {
76 [CI 1 cluster_current_epoch] > $current_epoch
77 } else {
78 fail "No failover detected"
79 }
80}
81
82test "Cluster should eventually be up again" {
83 assert_cluster_state ok
84}
85
86test "Cluster is writable" {
87 cluster_write_test 1
88}
89
90test "Instance 12 is now a master without slaves" {
91 assert {[RI 12 role] eq {master}}
92}
93
94# The remaining instance is now without slaves. Some other slave
95# should migrate to it.
96
97test "Master #12 should get at least one migrated replica" {
98 wait_for_condition 1000 50 {
99 [llength [lindex [R 12 role] 2]] >= 1
100 } else {
101 fail "Master #12 has no replicas"
102 }
103}
diff --git a/examples/redis-unstable/tests/cluster/tests/08-update-msg.tcl b/examples/redis-unstable/tests/cluster/tests/08-update-msg.tcl
deleted file mode 100644
index 9011f32..0000000
--- a/examples/redis-unstable/tests/cluster/tests/08-update-msg.tcl
+++ /dev/null
@@ -1,90 +0,0 @@
1# Test UPDATE messages sent by other nodes when the currently authorirative
2# master is unavailable. The test is performed in the following steps:
3#
4# 1) Master goes down.
5# 2) Slave failover and becomes new master.
6# 3) New master is partitioned away.
7# 4) Old master returns.
8# 5) At this point we expect the old master to turn into a slave ASAP because
9# of the UPDATE messages it will receive from the other nodes when its
10# configuration will be found to be outdated.
11
12source "../tests/includes/init-tests.tcl"
13
14test "Create a 5 nodes cluster" {
15 create_cluster 5 5
16}
17
18test "Cluster is up" {
19 assert_cluster_state ok
20}
21
22test "Cluster is writable" {
23 cluster_write_test 0
24}
25
26test "Instance #5 is a slave" {
27 assert {[RI 5 role] eq {slave}}
28}
29
30test "Instance #5 synced with the master" {
31 wait_for_condition 1000 50 {
32 [RI 5 master_link_status] eq {up}
33 } else {
34 fail "Instance #5 master link status is not up"
35 }
36}
37
38set current_epoch [CI 1 cluster_current_epoch]
39
40test "Killing one master node" {
41 kill_instance redis 0
42}
43
44test "Wait for failover" {
45 wait_for_condition 1000 50 {
46 [CI 1 cluster_current_epoch] > $current_epoch
47 } else {
48 fail "No failover detected"
49 }
50}
51
52test "Cluster should eventually be up again" {
53 assert_cluster_state ok
54}
55
56test "Cluster is writable" {
57 cluster_write_test 1
58}
59
60test "Instance #5 is now a master" {
61 assert {[RI 5 role] eq {master}}
62}
63
64test "Killing the new master #5" {
65 kill_instance redis 5
66}
67
68test "Cluster should be down now" {
69 assert_cluster_state fail
70}
71
72test "Restarting the old master node" {
73 restart_instance redis 0
74}
75
76test "Instance #0 gets converted into a slave" {
77 wait_for_condition 1000 50 {
78 [RI 0 role] eq {slave}
79 } else {
80 fail "Old master was not converted into slave"
81 }
82}
83
84test "Restarting the new master node" {
85 restart_instance redis 5
86}
87
88test "Cluster is up again" {
89 assert_cluster_state ok
90}
diff --git a/examples/redis-unstable/tests/cluster/tests/09-pubsub.tcl b/examples/redis-unstable/tests/cluster/tests/09-pubsub.tcl
deleted file mode 100644
index e62b91c..0000000
--- a/examples/redis-unstable/tests/cluster/tests/09-pubsub.tcl
+++ /dev/null
@@ -1,40 +0,0 @@
1# Test PUBLISH propagation across the cluster.
2
3source "../tests/includes/init-tests.tcl"
4
5test "Create a 5 nodes cluster" {
6 create_cluster 5 5
7}
8
9proc test_cluster_publish {instance instances} {
10 # Subscribe all the instances but the one we use to send.
11 for {set j 0} {$j < $instances} {incr j} {
12 if {$j != $instance} {
13 R $j deferred 1
14 R $j subscribe testchannel
15 R $j read; # Read the subscribe reply
16 }
17 }
18
19 set data [randomValue]
20 R $instance PUBLISH testchannel $data
21
22 # Read the message back from all the nodes.
23 for {set j 0} {$j < $instances} {incr j} {
24 if {$j != $instance} {
25 set msg [R $j read]
26 assert {$data eq [lindex $msg 2]}
27 R $j unsubscribe testchannel
28 R $j read; # Read the unsubscribe reply
29 R $j deferred 0
30 }
31 }
32}
33
34test "Test publishing to master" {
35 test_cluster_publish 0 10
36}
37
38test "Test publishing to slave" {
39 test_cluster_publish 5 10
40}
diff --git a/examples/redis-unstable/tests/cluster/tests/10-manual-failover.tcl b/examples/redis-unstable/tests/cluster/tests/10-manual-failover.tcl
deleted file mode 100644
index 5441b79..0000000
--- a/examples/redis-unstable/tests/cluster/tests/10-manual-failover.tcl
+++ /dev/null
@@ -1,192 +0,0 @@
1# Check the manual failover
2
3source "../tests/includes/init-tests.tcl"
4
5test "Create a 5 nodes cluster" {
6 create_cluster 5 5
7}
8
9test "Cluster is up" {
10 assert_cluster_state ok
11}
12
13test "Cluster is writable" {
14 cluster_write_test 0
15}
16
17test "Instance #5 is a slave" {
18 assert {[RI 5 role] eq {slave}}
19}
20
21test "Instance #5 synced with the master" {
22 wait_for_condition 1000 50 {
23 [RI 5 master_link_status] eq {up}
24 } else {
25 fail "Instance #5 master link status is not up"
26 }
27}
28
29set current_epoch [CI 1 cluster_current_epoch]
30
31set numkeys 50000
32set numops 10000
33set cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]]
34catch {unset content}
35array set content {}
36
37test "Send CLUSTER FAILOVER to #5, during load" {
38 for {set j 0} {$j < $numops} {incr j} {
39 # Write random data to random list.
40 set listid [randomInt $numkeys]
41 set key "key:$listid"
42 set ele [randomValue]
43 # We write both with Lua scripts and with plain commands.
44 # This way we are able to stress Lua -> Redis command invocation
45 # as well, that has tests to prevent Lua to write into wrong
46 # hash slots.
47 if {$listid % 2} {
48 $cluster rpush $key $ele
49 } else {
50 $cluster eval {redis.call("rpush",KEYS[1],ARGV[1])} 1 $key $ele
51 }
52 lappend content($key) $ele
53
54 if {($j % 1000) == 0} {
55 puts -nonewline W; flush stdout
56 }
57
58 if {$j == $numops/2} {R 5 cluster failover}
59 }
60}
61
62test "Wait for failover" {
63 wait_for_condition 1000 50 {
64 [CI 1 cluster_current_epoch] > $current_epoch
65 } else {
66 fail "No failover detected"
67 }
68}
69
70test "Cluster should eventually be up again" {
71 assert_cluster_state ok
72}
73
74test "Cluster is writable" {
75 cluster_write_test 1
76}
77
78test "Instance #5 is now a master" {
79 assert {[RI 5 role] eq {master}}
80}
81
82test "Verify $numkeys keys for consistency with logical content" {
83 # Check that the Redis Cluster content matches our logical content.
84 foreach {key value} [array get content] {
85 assert {[$cluster lrange $key 0 -1] eq $value}
86 }
87}
88
89test "Instance #0 gets converted into a slave" {
90 wait_for_condition 1000 50 {
91 [RI 0 role] eq {slave}
92 } else {
93 fail "Old master was not converted into slave"
94 }
95}
96
97## Check that manual failover does not happen if we can't talk with the master.
98
99source "../tests/includes/init-tests.tcl"
100
101test "Create a 5 nodes cluster" {
102 create_cluster 5 5
103}
104
105test "Cluster is up" {
106 assert_cluster_state ok
107}
108
109test "Cluster is writable" {
110 cluster_write_test 0
111}
112
113test "Instance #5 is a slave" {
114 assert {[RI 5 role] eq {slave}}
115}
116
117test "Instance #5 synced with the master" {
118 wait_for_condition 1000 50 {
119 [RI 5 master_link_status] eq {up}
120 } else {
121 fail "Instance #5 master link status is not up"
122 }
123}
124
125test "Make instance #0 unreachable without killing it" {
126 R 0 deferred 1
127 R 0 DEBUG SLEEP 10
128}
129
130test "Send CLUSTER FAILOVER to instance #5" {
131 R 5 cluster failover
132}
133
134test "Instance #5 is still a slave after some time (no failover)" {
135 after 5000
136 assert {[RI 5 role] eq {master}}
137}
138
139test "Wait for instance #0 to return back alive" {
140 R 0 deferred 0
141 assert {[R 0 read] eq {OK}}
142}
143
144## Check with "force" failover happens anyway.
145
146source "../tests/includes/init-tests.tcl"
147
148test "Create a 5 nodes cluster" {
149 create_cluster 5 5
150}
151
152test "Cluster is up" {
153 assert_cluster_state ok
154}
155
156test "Cluster is writable" {
157 cluster_write_test 0
158}
159
160test "Instance #5 is a slave" {
161 assert {[RI 5 role] eq {slave}}
162}
163
164test "Instance #5 synced with the master" {
165 wait_for_condition 1000 50 {
166 [RI 5 master_link_status] eq {up}
167 } else {
168 fail "Instance #5 master link status is not up"
169 }
170}
171
172test "Make instance #0 unreachable without killing it" {
173 R 0 deferred 1
174 R 0 DEBUG SLEEP 10
175}
176
177test "Send CLUSTER FAILOVER to instance #5" {
178 R 5 cluster failover force
179}
180
181test "Instance #5 is a master after some time" {
182 wait_for_condition 1000 50 {
183 [RI 5 role] eq {master}
184 } else {
185 fail "Instance #5 is not a master after some time regardless of FORCE"
186 }
187}
188
189test "Wait for instance #0 to return back alive" {
190 R 0 deferred 0
191 assert {[R 0 read] eq {OK}}
192}
diff --git a/examples/redis-unstable/tests/cluster/tests/11-manual-takeover.tcl b/examples/redis-unstable/tests/cluster/tests/11-manual-takeover.tcl
deleted file mode 100644
index 78a0f85..0000000
--- a/examples/redis-unstable/tests/cluster/tests/11-manual-takeover.tcl
+++ /dev/null
@@ -1,71 +0,0 @@
1# Manual takeover test
2
3source "../tests/includes/init-tests.tcl"
4
5test "Create a 5 nodes cluster" {
6 create_cluster 5 5
7}
8
9test "Cluster is up" {
10 assert_cluster_state ok
11}
12
13test "Cluster is writable" {
14 cluster_write_test 0
15}
16
17# For this test, disable replica failover until
18# all of the primaries are confirmed killed. Otherwise
19# there might be enough time to elect a replica.
20set replica_ids { 5 6 7 }
21foreach id $replica_ids {
22 R $id config set cluster-replica-no-failover yes
23}
24
25test "Killing majority of master nodes" {
26 kill_instance redis 0
27 kill_instance redis 1
28 kill_instance redis 2
29}
30
31foreach id $replica_ids {
32 R $id config set cluster-replica-no-failover no
33}
34
35test "Cluster should eventually be down" {
36 assert_cluster_state fail
37}
38
39test "Use takeover to bring slaves back" {
40 foreach id $replica_ids {
41 R $id cluster failover takeover
42 }
43}
44
45test "Cluster should eventually be up again" {
46 assert_cluster_state ok
47}
48
49test "Cluster is writable" {
50 cluster_write_test 4
51}
52
53test "Instance #5, #6, #7 are now masters" {
54 foreach id $replica_ids {
55 assert {[RI $id role] eq {master}}
56 }
57}
58
59test "Restarting the previously killed master nodes" {
60 restart_instance redis 0
61 restart_instance redis 1
62 restart_instance redis 2
63}
64
65test "Instance #0, #1, #2 gets converted into a slaves" {
66 wait_for_condition 1000 50 {
67 [RI 0 role] eq {slave} && [RI 1 role] eq {slave} && [RI 2 role] eq {slave}
68 } else {
69 fail "Old masters not converted into slaves"
70 }
71}
diff --git a/examples/redis-unstable/tests/cluster/tests/12-replica-migration-2.tcl b/examples/redis-unstable/tests/cluster/tests/12-replica-migration-2.tcl
deleted file mode 100644
index 5e2f671..0000000
--- a/examples/redis-unstable/tests/cluster/tests/12-replica-migration-2.tcl
+++ /dev/null
@@ -1,75 +0,0 @@
1# Replica migration test #2.
2#
3# Check that the status of master that can be targeted by replica migration
4# is acquired again, after being getting slots again, in a cluster where the
5# other masters have slaves.
6
7source "../tests/includes/init-tests.tcl"
8source "../../../tests/support/cli.tcl"
9
10# Create a cluster with 5 master and 15 slaves, to make sure there are no
11# empty masters and make rebalancing simpler to handle during the test.
12test "Create a 5 nodes cluster" {
13 cluster_create_with_continuous_slots 5 15
14}
15
16test "Cluster is up" {
17 assert_cluster_state ok
18}
19
20test "Each master should have at least two replicas attached" {
21 foreach_redis_id id {
22 if {$id < 5} {
23 wait_for_condition 1000 50 {
24 [llength [lindex [R $id role] 2]] >= 2
25 } else {
26 fail "Master #$id does not have 2 slaves as expected"
27 }
28 }
29 }
30}
31
32test "Set allow-replica-migration yes" {
33 foreach_redis_id id {
34 R $id CONFIG SET cluster-allow-replica-migration yes
35 }
36}
37
38set master0_id [dict get [get_myself 0] id]
39test "Resharding all the master #0 slots away from it" {
40 set output [exec \
41 ../../../src/redis-cli --cluster rebalance \
42 127.0.0.1:[get_instance_attrib redis 0 port] \
43 {*}[rediscli_tls_config "../../../tests"] \
44 --cluster-weight ${master0_id}=0 >@ stdout ]
45
46}
47
48test "Master #0 who lost all slots should turn into a replica without replicas" {
49 wait_for_condition 2000 50 {
50 [RI 0 role] == "slave" && [RI 0 connected_slaves] == 0
51 } else {
52 puts [R 0 info replication]
53 fail "Master #0 didn't turn itself into a replica"
54 }
55}
56
57test "Resharding back some slot to master #0" {
58 # Wait for the cluster config to propagate before attempting a
59 # new resharding.
60 after 10000
61 set output [exec \
62 ../../../src/redis-cli --cluster rebalance \
63 127.0.0.1:[get_instance_attrib redis 0 port] \
64 {*}[rediscli_tls_config "../../../tests"] \
65 --cluster-weight ${master0_id}=.01 \
66 --cluster-use-empty-masters >@ stdout]
67}
68
69test "Master #0 should re-acquire one or more replicas" {
70 wait_for_condition 1000 50 {
71 [llength [lindex [R 0 role] 2]] >= 1
72 } else {
73 fail "Master #0 has no has replicas"
74 }
75}
diff --git a/examples/redis-unstable/tests/cluster/tests/12.1-replica-migration-3.tcl b/examples/redis-unstable/tests/cluster/tests/12.1-replica-migration-3.tcl
deleted file mode 100644
index 790c732..0000000
--- a/examples/redis-unstable/tests/cluster/tests/12.1-replica-migration-3.tcl
+++ /dev/null
@@ -1,65 +0,0 @@
1# Replica migration test #2.
2#
3# Check that if 'cluster-allow-replica-migration' is set to 'no', slaves do not
4# migrate when master becomes empty.
5
6source "../tests/includes/init-tests.tcl"
7source "../tests/includes/utils.tcl"
8
9# Create a cluster with 5 master and 15 slaves, to make sure there are no
10# empty masters and make rebalancing simpler to handle during the test.
11test "Create a 5 nodes cluster" {
12 cluster_create_with_continuous_slots 5 15
13}
14
15test "Cluster is up" {
16 assert_cluster_state ok
17}
18
19test "Each master should have at least two replicas attached" {
20 foreach_redis_id id {
21 if {$id < 5} {
22 wait_for_condition 1000 50 {
23 [llength [lindex [R $id role] 2]] >= 2
24 } else {
25 fail "Master #$id does not have 2 slaves as expected"
26 }
27 }
28 }
29}
30
31test "Set allow-replica-migration no" {
32 foreach_redis_id id {
33 R $id CONFIG SET cluster-allow-replica-migration no
34 }
35}
36
37set master0_id [dict get [get_myself 0] id]
38test "Resharding all the master #0 slots away from it" {
39 set output [exec \
40 ../../../src/redis-cli --cluster rebalance \
41 127.0.0.1:[get_instance_attrib redis 0 port] \
42 {*}[rediscli_tls_config "../../../tests"] \
43 --cluster-weight ${master0_id}=0 >@ stdout ]
44}
45
46test "Wait cluster to be stable" {
47 wait_cluster_stable
48}
49
50test "Master #0 still should have its replicas" {
51 assert { [llength [lindex [R 0 role] 2]] >= 2 }
52}
53
54test "Each master should have at least two replicas attached" {
55 foreach_redis_id id {
56 if {$id < 5} {
57 wait_for_condition 1000 50 {
58 [llength [lindex [R $id role] 2]] >= 2
59 } else {
60 fail "Master #$id does not have 2 slaves as expected"
61 }
62 }
63 }
64}
65
diff --git a/examples/redis-unstable/tests/cluster/tests/13-no-failover-option.tcl b/examples/redis-unstable/tests/cluster/tests/13-no-failover-option.tcl
deleted file mode 100644
index befa598..0000000
--- a/examples/redis-unstable/tests/cluster/tests/13-no-failover-option.tcl
+++ /dev/null
@@ -1,61 +0,0 @@
1# Check that the no-failover option works
2
3source "../tests/includes/init-tests.tcl"
4
5test "Create a 5 nodes cluster" {
6 create_cluster 5 5
7}
8
9test "Cluster is up" {
10 assert_cluster_state ok
11}
12
13test "Cluster is writable" {
14 cluster_write_test 0
15}
16
17test "Instance #5 is a slave" {
18 assert {[RI 5 role] eq {slave}}
19
20 # Configure it to never failover the master
21 R 5 CONFIG SET cluster-slave-no-failover yes
22}
23
24test "Instance #5 synced with the master" {
25 wait_for_condition 1000 50 {
26 [RI 5 master_link_status] eq {up}
27 } else {
28 fail "Instance #5 master link status is not up"
29 }
30}
31
32test "The nofailover flag is propagated" {
33 set slave5_id [dict get [get_myself 5] id]
34
35 foreach_redis_id id {
36 wait_for_condition 1000 50 {
37 [has_flag [get_node_by_id $id $slave5_id] nofailover]
38 } else {
39 fail "Instance $id can't see the nofailover flag of slave"
40 }
41 }
42}
43
44set current_epoch [CI 1 cluster_current_epoch]
45
46test "Killing one master node" {
47 kill_instance redis 0
48}
49
50test "Cluster should be still down after some time" {
51 after 10000
52 assert_cluster_state fail
53}
54
55test "Instance #5 is still a slave" {
56 assert {[RI 5 role] eq {slave}}
57}
58
59test "Restarting the previously killed master node" {
60 restart_instance redis 0
61}
diff --git a/examples/redis-unstable/tests/cluster/tests/14-consistency-check.tcl b/examples/redis-unstable/tests/cluster/tests/14-consistency-check.tcl
deleted file mode 100644
index 53c67c9..0000000
--- a/examples/redis-unstable/tests/cluster/tests/14-consistency-check.tcl
+++ /dev/null
@@ -1,141 +0,0 @@
1source "../tests/includes/init-tests.tcl"
2source "../../../tests/support/cli.tcl"
3
4test "Create a 5 nodes cluster" {
5 create_cluster 5 5
6}
7
8test "Cluster should start ok" {
9 assert_cluster_state ok
10}
11
12test "Cluster is writable" {
13 cluster_write_test 0
14}
15
16proc find_non_empty_master {} {
17 set master_id_no {}
18 foreach_redis_id id {
19 if {[RI $id role] eq {master} && [R $id dbsize] > 0} {
20 set master_id_no $id
21 break
22 }
23 }
24 return $master_id_no
25}
26
27proc get_one_of_my_replica {id} {
28 wait_for_condition 1000 50 {
29 [llength [lindex [R $id role] 2]] > 0
30 } else {
31 fail "replicas didn't connect"
32 }
33 set replica_port [lindex [lindex [lindex [R $id role] 2] 0] 1]
34 set replica_id_num [get_instance_id_by_port redis $replica_port]
35
36 # To avoid -LOADING reply, wait until replica syncs with master.
37 wait_for_condition 1000 50 {
38 [RI $replica_id_num master_link_status] eq {up} &&
39 [R $replica_id_num dbsize] eq [R $id dbsize]
40 } else {
41 fail "Replica did not sync in time."
42 }
43 return $replica_id_num
44}
45
46proc cluster_write_keys_with_expire {id ttl} {
47 set prefix [randstring 20 20 alpha]
48 set port [get_instance_attrib redis $id port]
49 set cluster [redis_cluster 127.0.0.1:$port]
50 for {set j 100} {$j < 200} {incr j} {
51 $cluster setex key_expire.$j $ttl $prefix.$j
52 }
53 $cluster close
54}
55
56# make sure that replica who restarts from persistence will load keys
57# that have already expired, critical for correct execution of commands
58# that arrive from the master
59proc test_slave_load_expired_keys {aof} {
60 test "Slave expired keys is loaded when restarted: appendonly=$aof" {
61 set master_id [find_non_empty_master]
62 set replica_id [get_one_of_my_replica $master_id]
63
64 set master_dbsize_0 [R $master_id dbsize]
65 set replica_dbsize_0 [R $replica_id dbsize]
66 assert_equal $master_dbsize_0 $replica_dbsize_0
67
68 # config the replica persistency and rewrite the config file to survive restart
69 # note that this needs to be done before populating the volatile keys since
70 # that triggers and AOFRW, and we rather the AOF file to have 'SET PXAT' commands
71 # rather than an RDB with volatile keys
72 R $replica_id config set appendonly $aof
73 R $replica_id config rewrite
74
75 # fill with 100 keys with 3 second TTL
76 set data_ttl 3
77 cluster_write_keys_with_expire $master_id $data_ttl
78
79 # wait for replica to be in sync with master
80 wait_for_condition 500 10 {
81 [RI $replica_id master_link_status] eq {up} &&
82 [R $replica_id dbsize] eq [R $master_id dbsize]
83 } else {
84 fail "replica didn't sync"
85 }
86
87 set replica_dbsize_1 [R $replica_id dbsize]
88 assert {$replica_dbsize_1 > $replica_dbsize_0}
89
90 # make replica create persistence file
91 if {$aof == "yes"} {
92 # we need to wait for the initial AOFRW to be done, otherwise
93 # kill_instance (which now uses SIGTERM will fail ("Writing initial AOF, can't exit")
94 wait_for_condition 100 10 {
95 [RI $replica_id aof_rewrite_scheduled] eq 0 &&
96 [RI $replica_id aof_rewrite_in_progress] eq 0
97 } else {
98 fail "AOFRW didn't finish"
99 }
100 } else {
101 R $replica_id save
102 }
103
104 # kill the replica (would stay down until re-started)
105 kill_instance redis $replica_id
106
107 # Make sure the master doesn't do active expire (sending DELs to the replica)
108 R $master_id DEBUG SET-ACTIVE-EXPIRE 0
109
110 # wait for all the keys to get logically expired
111 after [expr $data_ttl*1000]
112
113 # start the replica again (loading an RDB or AOF file)
114 restart_instance redis $replica_id
115
116 # Replica may start a full sync after restart, trying in a loop to avoid
117 # -LOADING reply in that case.
118 wait_for_condition 1000 50 {
119 [catch {set replica_dbsize_3 [R $replica_id dbsize]} e] == 0
120 } else {
121 fail "Replica is not up."
122 }
123
124 # make sure the keys are still there
125 assert {$replica_dbsize_3 > $replica_dbsize_0}
126
127 # restore settings
128 R $master_id DEBUG SET-ACTIVE-EXPIRE 1
129
130 # wait for the master to expire all keys and replica to get the DELs
131 wait_for_condition 500 10 {
132 [RI $replica_id master_link_status] eq {up} &&
133 [R $replica_id dbsize] eq $master_dbsize_0
134 } else {
135 fail "keys didn't expire"
136 }
137 }
138}
139
140test_slave_load_expired_keys no
141test_slave_load_expired_keys yes
diff --git a/examples/redis-unstable/tests/cluster/tests/15-cluster-slots.tcl b/examples/redis-unstable/tests/cluster/tests/15-cluster-slots.tcl
deleted file mode 100644
index 0f82c78..0000000
--- a/examples/redis-unstable/tests/cluster/tests/15-cluster-slots.tcl
+++ /dev/null
@@ -1,155 +0,0 @@
1source "../tests/includes/init-tests.tcl"
2
3proc cluster_allocate_mixedSlots {n} {
4 set slot 16383
5 while {$slot >= 0} {
6 set node [expr {$slot % $n}]
7 lappend slots_$node $slot
8 incr slot -1
9 }
10 for {set j 0} {$j < $n} {incr j} {
11 R $j cluster addslots {*}[set slots_${j}]
12 }
13}
14
15proc create_cluster_with_mixedSlot {masters slaves} {
16 cluster_allocate_mixedSlots $masters
17 if {$slaves} {
18 cluster_allocate_slaves $masters $slaves
19 }
20 assert_cluster_state ok
21}
22
23test "Create a 5 nodes cluster" {
24 create_cluster_with_mixedSlot 5 15
25}
26
27test "Cluster is up" {
28 assert_cluster_state ok
29}
30
31test "Cluster is writable" {
32 cluster_write_test 0
33}
34
35test "Instance #5 is a slave" {
36 assert {[RI 5 role] eq {slave}}
37}
38
39test "client do not break when cluster slot" {
40 R 0 config set client-output-buffer-limit "normal 33554432 16777216 60"
41 if { [catch {R 0 cluster slots}] } {
42 fail "output overflow when cluster slots"
43 }
44}
45
46test "client can handle keys with hash tag" {
47 set cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]]
48 $cluster set foo{tag} bar
49 $cluster close
50}
51
52test "slot migration is valid from primary to another primary" {
53 set cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]]
54 set key order1
55 set slot [$cluster cluster keyslot $key]
56 array set nodefrom [$cluster masternode_for_slot $slot]
57 array set nodeto [$cluster masternode_notfor_slot $slot]
58
59 assert_equal {OK} [$nodefrom(link) cluster setslot $slot node $nodeto(id)]
60 assert_equal {OK} [$nodeto(link) cluster setslot $slot node $nodeto(id)]
61}
62
63test "Client unblocks after slot migration from one primary to another" {
64 set cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]]
65 set key mystream
66 set slot [$cluster cluster keyslot $key]
67 array set nodefrom [$cluster masternode_for_slot $slot]
68 array set nodeto [$cluster masternode_notfor_slot $slot]
69
70 # Create a stream group on the source node
71 $nodefrom(link) XGROUP CREATE $key mygroup $ MKSTREAM
72
73 # block another client on xreadgroup
74 set rd [redis_deferring_client_by_addr $nodefrom(host) $nodefrom(port)]
75 $rd XREADGROUP GROUP mygroup Alice BLOCK 0 STREAMS $key ">"
76 wait_for_condition 1000 50 {
77 [getInfoProperty [$nodefrom(link) info clients] blocked_clients] eq {1}
78 } else {
79 fail "client wasn't blocked"
80 }
81
82 # Start slot migration from the source node to the target node.
83 # Because the `unblock_on_nokey` option of xreadgroup is set to 1, the client
84 # will be unblocked when the key `mystream` is migrated.
85 assert_equal {OK} [$nodefrom(link) CLUSTER SETSLOT $slot MIGRATING $nodeto(id)]
86 assert_equal {OK} [$nodeto(link) CLUSTER SETSLOT $slot IMPORTING $nodefrom(id)]
87 $nodefrom(link) MIGRATE $nodeto(host) $nodeto(port) $key 0 5000
88}
89
90test "slot migration is invalid from primary to replica" {
91 set cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]]
92 set key order1
93 set slot [$cluster cluster keyslot $key]
94 array set nodefrom [$cluster masternode_for_slot $slot]
95
96 # Get replica node serving slot.
97 set replicanodeinfo [$cluster cluster replicas $nodefrom(id)]
98 puts $replicanodeinfo
99 set args [split $replicanodeinfo " "]
100 set replicaid [lindex [split [lindex $args 0] \{] 1]
101 puts $replicaid
102
103 catch {[$nodefrom(link) cluster setslot $slot node $replicaid]} err
104 assert_match "*Target node is not a master" $err
105}
106
107proc count_bound_slots {n} {
108 set slot_count 0
109 foreach slot_range_mapping [$n cluster slots] {
110 set start_slot [lindex $slot_range_mapping 0]
111 set end_slot [lindex $slot_range_mapping 1]
112 incr slot_count [expr $end_slot - $start_slot + 1]
113 }
114 return $slot_count
115 }
116
117 test "slot must be unbound on the owner when it is deleted" {
118 set node0 [Rn 0]
119 set node1 [Rn 1]
120 assert {[count_bound_slots $node0] eq 16384}
121 assert {[count_bound_slots $node1] eq 16384}
122
123 set slot_to_delete 0
124 # Delete
125 $node0 CLUSTER DELSLOTS $slot_to_delete
126
127 # Verify
128 # The node that owns the slot must unbind the slot that was deleted
129 wait_for_condition 1000 50 {
130 [count_bound_slots $node0] == 16383
131 } else {
132 fail "Cluster slot deletion was not recorded on the node that owns the slot"
133 }
134
135 # We don't propagate slot deletion across all nodes in the cluster.
136 # This can lead to extra redirect before the clients find out that the slot is unbound.
137 wait_for_condition 1000 50 {
138 [count_bound_slots $node1] == 16384
139 } else {
140 fail "Cluster slot deletion should not be propagated to all nodes in the cluster"
141 }
142 }
143
144if {$::tls} {
145 test {CLUSTER SLOTS from non-TLS client in TLS cluster} {
146 set slots_tls [R 0 cluster slots]
147 set host [get_instance_attrib redis 0 host]
148 set plaintext_port [get_instance_attrib redis 0 plaintext-port]
149 set client_plain [redis $host $plaintext_port 0 0]
150 set slots_plain [$client_plain cluster slots]
151 $client_plain close
152 # Compare the ports in the first row
153 assert_no_match [lindex $slots_tls 0 3 1] [lindex $slots_plain 0 3 1]
154 }
155} \ No newline at end of file
diff --git a/examples/redis-unstable/tests/cluster/tests/16-transactions-on-replica.tcl b/examples/redis-unstable/tests/cluster/tests/16-transactions-on-replica.tcl
deleted file mode 100644
index 8bec06e..0000000
--- a/examples/redis-unstable/tests/cluster/tests/16-transactions-on-replica.tcl
+++ /dev/null
@@ -1,85 +0,0 @@
1# Check basic transactions on a replica.
2
3source "../tests/includes/init-tests.tcl"
4
5test "Create a primary with a replica" {
6 create_cluster 1 1
7}
8
9test "Cluster should start ok" {
10 assert_cluster_state ok
11}
12
13set primary [Rn 0]
14set replica [Rn 1]
15
16test "Can't read from replica without READONLY" {
17 $primary SET a 1
18 wait_for_ofs_sync $primary $replica
19 catch {$replica GET a} err
20 assert {[string range $err 0 4] eq {MOVED}}
21}
22
23test "Can't read from replica after READWRITE" {
24 $replica READWRITE
25 catch {$replica GET a} err
26 assert {[string range $err 0 4] eq {MOVED}}
27}
28
29test "Can read from replica after READONLY" {
30 $replica READONLY
31 assert {[$replica GET a] eq {1}}
32}
33
34test "Can perform HSET primary and HGET from replica" {
35 $primary HSET h a 1
36 $primary HSET h b 2
37 $primary HSET h c 3
38 wait_for_ofs_sync $primary $replica
39 assert {[$replica HGET h a] eq {1}}
40 assert {[$replica HGET h b] eq {2}}
41 assert {[$replica HGET h c] eq {3}}
42}
43
44test "Can MULTI-EXEC transaction of HGET operations from replica" {
45 $replica MULTI
46 assert {[$replica HGET h a] eq {QUEUED}}
47 assert {[$replica HGET h b] eq {QUEUED}}
48 assert {[$replica HGET h c] eq {QUEUED}}
49 assert {[$replica EXEC] eq {1 2 3}}
50}
51
52test "MULTI-EXEC with write operations is MOVED" {
53 $replica MULTI
54 catch {$replica HSET h b 4} err
55 assert {[string range $err 0 4] eq {MOVED}}
56 catch {$replica exec} err
57 assert {[string range $err 0 8] eq {EXECABORT}}
58}
59
60test "read-only blocking operations from replica" {
61 set rd [redis_deferring_client redis 1]
62 $rd readonly
63 $rd read
64 $rd XREAD BLOCK 0 STREAMS k 0
65
66 wait_for_condition 1000 50 {
67 [RI 1 blocked_clients] eq {1}
68 } else {
69 fail "client wasn't blocked"
70 }
71
72 $primary XADD k * foo bar
73 set res [$rd read]
74 set res [lindex [lindex [lindex [lindex $res 0] 1] 0] 1]
75 assert {$res eq {foo bar}}
76 $rd close
77}
78
79test "reply MOVED when eval from replica for update" {
80 catch {[$replica eval {#!lua
81 return redis.call('del','a')
82 } 1 a
83 ]} err
84 assert {[string range $err 0 4] eq {MOVED}}
85} \ No newline at end of file
diff --git a/examples/redis-unstable/tests/cluster/tests/17-diskless-load-swapdb.tcl b/examples/redis-unstable/tests/cluster/tests/17-diskless-load-swapdb.tcl
deleted file mode 100644
index cb81b9f..0000000
--- a/examples/redis-unstable/tests/cluster/tests/17-diskless-load-swapdb.tcl
+++ /dev/null
@@ -1,93 +0,0 @@
1# Check that replica keys and keys to slots map are right after failing to diskless load using SWAPDB.
2
3source "../tests/includes/init-tests.tcl"
4
5test "Create a primary with a replica" {
6 create_cluster 1 1
7}
8
9test "Cluster should start ok" {
10 assert_cluster_state ok
11}
12
13test "Cluster is writable" {
14 cluster_write_test 0
15}
16
17test "Main db not affected when fail to diskless load" {
18 set master [Rn 0]
19 set replica [Rn 1]
20 set master_id 0
21 set replica_id 1
22
23 $replica READONLY
24 $replica config set repl-diskless-load swapdb
25 $replica config set appendonly no
26 $replica config set save ""
27 $replica config rewrite
28 $master config set repl-backlog-size 1024
29 $master config set repl-diskless-sync yes
30 $master config set repl-diskless-sync-delay 0
31 $master config set rdb-key-save-delay 10000
32 $master config set rdbcompression no
33 $master config set appendonly no
34 $master config set save ""
35
36 # Write a key that belongs to slot 0
37 set slot0_key "06S"
38 $master set $slot0_key 1
39 wait_for_ofs_sync $master $replica
40 assert_equal {1} [$replica get $slot0_key]
41 assert_equal $slot0_key [$replica CLUSTER GETKEYSINSLOT 0 1]
42
43 # Save an RDB and kill the replica
44 $replica save
45 kill_instance redis $replica_id
46
47 # Delete the key from master
48 $master del $slot0_key
49
50 # Replica must full sync with master when start because replication
51 # backlog size is very small, and dumping rdb will cost several seconds.
52 set num 10000
53 set value [string repeat A 1024]
54 set rd [redis_deferring_client redis $master_id]
55 for {set j 0} {$j < $num} {incr j} {
56 $rd set $j $value
57 }
58 for {set j 0} {$j < $num} {incr j} {
59 $rd read
60 }
61
62 # Start the replica again
63 restart_instance redis $replica_id
64 $replica READONLY
65
66 # Start full sync, wait till after db started loading in background
67 wait_for_condition 500 10 {
68 [s $replica_id async_loading] eq 1
69 } else {
70 fail "Fail to full sync"
71 }
72
73 # Kill master, abort full sync
74 kill_instance redis $master_id
75
76 # Start full sync, wait till the replica detects the disconnection
77 wait_for_condition 500 10 {
78 [s $replica_id async_loading] eq 0
79 } else {
80 fail "Fail to full sync"
81 }
82
83 # Replica keys and keys to slots map still both are right.
84 # CLUSTERDOWN errors are acceptable here because the cluster may be in a transient state
85 # due to the timing relationship with cluster-node-timeout.
86 if {[catch {$replica get $slot0_key} result]} {
87 assert_match "*CLUSTERDOWN*" $result
88 } else {
89 assert_equal {1} $result
90 }
91
92 assert_equal $slot0_key [$replica CLUSTER GETKEYSINSLOT 0 1]
93}
diff --git a/examples/redis-unstable/tests/cluster/tests/18-info.tcl b/examples/redis-unstable/tests/cluster/tests/18-info.tcl
deleted file mode 100644
index 68c62d3..0000000
--- a/examples/redis-unstable/tests/cluster/tests/18-info.tcl
+++ /dev/null
@@ -1,45 +0,0 @@
1# Check cluster info stats
2
3source "../tests/includes/init-tests.tcl"
4
5test "Create a primary with a replica" {
6 create_cluster 2 0
7}
8
9test "Cluster should start ok" {
10 assert_cluster_state ok
11}
12
13set primary1 [Rn 0]
14set primary2 [Rn 1]
15
16proc cmdstat {instance cmd} {
17 return [cmdrstat $cmd $instance]
18}
19
20proc errorstat {instance cmd} {
21 return [errorrstat $cmd $instance]
22}
23
24test "errorstats: rejected call due to MOVED Redirection" {
25 $primary1 config resetstat
26 $primary2 config resetstat
27 assert_match {} [errorstat $primary1 MOVED]
28 assert_match {} [errorstat $primary2 MOVED]
29 # we know that one will have a MOVED reply and one will succeed
30 catch {$primary1 set key b} replyP1
31 catch {$primary2 set key b} replyP2
32 # sort servers so we know which one failed
33 if {$replyP1 eq {OK}} {
34 assert_match {MOVED*} $replyP2
35 set pok $primary1
36 set perr $primary2
37 } else {
38 assert_match {MOVED*} $replyP1
39 set pok $primary2
40 set perr $primary1
41 }
42 assert_match {} [errorstat $pok MOVED]
43 assert_match {*count=1*} [errorstat $perr MOVED]
44 assert_match {*calls=0,*,rejected_calls=1,failed_calls=0} [cmdstat $perr set]
45}
diff --git a/examples/redis-unstable/tests/cluster/tests/19-cluster-nodes-slots.tcl b/examples/redis-unstable/tests/cluster/tests/19-cluster-nodes-slots.tcl
deleted file mode 100644
index 77faec9..0000000
--- a/examples/redis-unstable/tests/cluster/tests/19-cluster-nodes-slots.tcl
+++ /dev/null
@@ -1,50 +0,0 @@
1# Optimize CLUSTER NODES command by generating all nodes slot topology firstly
2
3source "../tests/includes/init-tests.tcl"
4
5test "Create a 2 nodes cluster" {
6 cluster_create_with_continuous_slots 2 2
7}
8
9test "Cluster should start ok" {
10 assert_cluster_state ok
11}
12
13set master1 [Rn 0]
14set master2 [Rn 1]
15
16test "Continuous slots distribution" {
17 assert_match "* 0-8191*" [$master1 CLUSTER NODES]
18 assert_match "* 8192-16383*" [$master2 CLUSTER NODES]
19 assert_match "*0 8191*" [$master1 CLUSTER SLOTS]
20 assert_match "*8192 16383*" [$master2 CLUSTER SLOTS]
21
22 $master1 CLUSTER DELSLOTS 4096
23 assert_match "* 0-4095 4097-8191*" [$master1 CLUSTER NODES]
24 assert_match "*0 4095*4097 8191*" [$master1 CLUSTER SLOTS]
25
26
27 $master2 CLUSTER DELSLOTS 12288
28 assert_match "* 8192-12287 12289-16383*" [$master2 CLUSTER NODES]
29 assert_match "*8192 12287*12289 16383*" [$master2 CLUSTER SLOTS]
30}
31
32test "Discontinuous slots distribution" {
33 # Remove middle slots
34 $master1 CLUSTER DELSLOTS 4092 4094
35 assert_match "* 0-4091 4093 4095 4097-8191*" [$master1 CLUSTER NODES]
36 assert_match "*0 4091*4093 4093*4095 4095*4097 8191*" [$master1 CLUSTER SLOTS]
37 $master2 CLUSTER DELSLOTS 12284 12286
38 assert_match "* 8192-12283 12285 12287 12289-16383*" [$master2 CLUSTER NODES]
39 assert_match "*8192 12283*12285 12285*12287 12287*12289 16383*" [$master2 CLUSTER SLOTS]
40
41 # Remove head slots
42 $master1 CLUSTER DELSLOTS 0 2
43 assert_match "* 1 3-4091 4093 4095 4097-8191*" [$master1 CLUSTER NODES]
44 assert_match "*1 1*3 4091*4093 4093*4095 4095*4097 8191*" [$master1 CLUSTER SLOTS]
45
46 # Remove tail slots
47 $master2 CLUSTER DELSLOTS 16380 16382 16383
48 assert_match "* 8192-12283 12285 12287 12289-16379 16381*" [$master2 CLUSTER NODES]
49 assert_match "*8192 12283*12285 12285*12287 12287*12289 16379*16381 16381*" [$master2 CLUSTER SLOTS]
50}
diff --git a/examples/redis-unstable/tests/cluster/tests/20-half-migrated-slot.tcl b/examples/redis-unstable/tests/cluster/tests/20-half-migrated-slot.tcl
deleted file mode 100644
index 229b3a8..0000000
--- a/examples/redis-unstable/tests/cluster/tests/20-half-migrated-slot.tcl
+++ /dev/null
@@ -1,98 +0,0 @@
1# Tests for fixing migrating slot at all stages:
2# 1. when migration is half inited on "migrating" node
3# 2. when migration is half inited on "importing" node
4# 3. migration inited, but not finished
5# 4. migration is half finished on "migrating" node
6# 5. migration is half finished on "importing" node
7
8# TODO: Test is currently disabled until it is stabilized (fixing the test
9# itself or real issues in Redis).
10
11if {false} {
12source "../tests/includes/init-tests.tcl"
13source "../tests/includes/utils.tcl"
14
15test "Create a 2 nodes cluster" {
16 create_cluster 2 0
17 config_set_all_nodes cluster-allow-replica-migration no
18}
19
20test "Cluster is up" {
21 assert_cluster_state ok
22}
23
24set cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]]
25catch {unset nodefrom}
26catch {unset nodeto}
27
28proc reset_cluster {} {
29 uplevel 1 {
30 $cluster refresh_nodes_map
31 array set nodefrom [$cluster masternode_for_slot 609]
32 array set nodeto [$cluster masternode_notfor_slot 609]
33 }
34}
35
36reset_cluster
37
38$cluster set aga xyz
39
40test "Half init migration in 'migrating' is fixable" {
41 assert_equal {OK} [$nodefrom(link) cluster setslot 609 migrating $nodeto(id)]
42 fix_cluster $nodefrom(addr)
43 assert_equal "xyz" [$cluster get aga]
44}
45
46test "Half init migration in 'importing' is fixable" {
47 assert_equal {OK} [$nodeto(link) cluster setslot 609 importing $nodefrom(id)]
48 fix_cluster $nodefrom(addr)
49 assert_equal "xyz" [$cluster get aga]
50}
51
52test "Init migration and move key" {
53 assert_equal {OK} [$nodefrom(link) cluster setslot 609 migrating $nodeto(id)]
54 assert_equal {OK} [$nodeto(link) cluster setslot 609 importing $nodefrom(id)]
55 assert_equal {OK} [$nodefrom(link) migrate $nodeto(host) $nodeto(port) aga 0 10000]
56 wait_for_cluster_propagation
57 assert_equal "xyz" [$cluster get aga]
58 fix_cluster $nodefrom(addr)
59 assert_equal "xyz" [$cluster get aga]
60}
61
62reset_cluster
63
64test "Move key again" {
65 wait_for_cluster_propagation
66 assert_equal {OK} [$nodefrom(link) cluster setslot 609 migrating $nodeto(id)]
67 assert_equal {OK} [$nodeto(link) cluster setslot 609 importing $nodefrom(id)]
68 assert_equal {OK} [$nodefrom(link) migrate $nodeto(host) $nodeto(port) aga 0 10000]
69 wait_for_cluster_propagation
70 assert_equal "xyz" [$cluster get aga]
71}
72
73test "Half-finish migration" {
74 # half finish migration on 'migrating' node
75 assert_equal {OK} [$nodefrom(link) cluster setslot 609 node $nodeto(id)]
76 fix_cluster $nodefrom(addr)
77 assert_equal "xyz" [$cluster get aga]
78}
79
80reset_cluster
81
82test "Move key back" {
83 # 'aga' key is in 609 slot
84 assert_equal {OK} [$nodefrom(link) cluster setslot 609 migrating $nodeto(id)]
85 assert_equal {OK} [$nodeto(link) cluster setslot 609 importing $nodefrom(id)]
86 assert_equal {OK} [$nodefrom(link) migrate $nodeto(host) $nodeto(port) aga 0 10000]
87 assert_equal "xyz" [$cluster get aga]
88}
89
90test "Half-finish importing" {
91 # Now we half finish 'importing' node
92 assert_equal {OK} [$nodeto(link) cluster setslot 609 node $nodeto(id)]
93 fix_cluster $nodefrom(addr)
94 assert_equal "xyz" [$cluster get aga]
95}
96
97config_set_all_nodes cluster-allow-replica-migration yes
98}
diff --git a/examples/redis-unstable/tests/cluster/tests/21-many-slot-migration.tcl b/examples/redis-unstable/tests/cluster/tests/21-many-slot-migration.tcl
deleted file mode 100644
index 1ac73dc..0000000
--- a/examples/redis-unstable/tests/cluster/tests/21-many-slot-migration.tcl
+++ /dev/null
@@ -1,64 +0,0 @@
1# Tests for many simultaneous migrations.
2
3# TODO: Test is currently disabled until it is stabilized (fixing the test
4# itself or real issues in Redis).
5
6if {false} {
7
8source "../tests/includes/init-tests.tcl"
9source "../tests/includes/utils.tcl"
10
11# TODO: This test currently runs without replicas, as failovers (which may
12# happen on lower-end CI platforms) are still not handled properly by the
13# cluster during slot migration (related to #6339).
14
15test "Create a 10 nodes cluster" {
16 create_cluster 10 0
17 config_set_all_nodes cluster-allow-replica-migration no
18}
19
20test "Cluster is up" {
21 assert_cluster_state ok
22}
23
24set cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]]
25catch {unset nodefrom}
26catch {unset nodeto}
27
28$cluster refresh_nodes_map
29
30test "Set many keys" {
31 for {set i 0} {$i < 40000} {incr i} {
32 $cluster set key:$i val:$i
33 }
34}
35
36test "Keys are accessible" {
37 for {set i 0} {$i < 40000} {incr i} {
38 assert { [$cluster get key:$i] eq "val:$i" }
39 }
40}
41
42test "Init migration of many slots" {
43 for {set slot 0} {$slot < 1000} {incr slot} {
44 array set nodefrom [$cluster masternode_for_slot $slot]
45 array set nodeto [$cluster masternode_notfor_slot $slot]
46
47 $nodefrom(link) cluster setslot $slot migrating $nodeto(id)
48 $nodeto(link) cluster setslot $slot importing $nodefrom(id)
49 }
50}
51
52test "Fix cluster" {
53 wait_for_cluster_propagation
54 fix_cluster $nodefrom(addr)
55}
56
57test "Keys are accessible" {
58 for {set i 0} {$i < 40000} {incr i} {
59 assert { [$cluster get key:$i] eq "val:$i" }
60 }
61}
62
63config_set_all_nodes cluster-allow-replica-migration yes
64}
diff --git a/examples/redis-unstable/tests/cluster/tests/22-replica-in-sync.tcl b/examples/redis-unstable/tests/cluster/tests/22-replica-in-sync.tcl
deleted file mode 100644
index b5645aa..0000000
--- a/examples/redis-unstable/tests/cluster/tests/22-replica-in-sync.tcl
+++ /dev/null
@@ -1,146 +0,0 @@
1source "../tests/includes/init-tests.tcl"
2
3test "Create a 1 node cluster" {
4 create_cluster 1 0
5}
6
7test "Cluster is up" {
8 assert_cluster_state ok
9}
10
11test "Cluster is writable" {
12 cluster_write_test 0
13}
14
15proc is_in_slots {master_id replica} {
16 set slots [R $master_id cluster slots]
17 set found_position [string first $replica $slots]
18 set result [expr {$found_position != -1}]
19 return $result
20}
21
22proc is_replica_online {info_repl} {
23 set found_position [string first "state=online" $info_repl]
24 set result [expr {$found_position != -1}]
25 return $result
26}
27
28proc get_last_pong_time {node_id target_cid} {
29 foreach item [split [R $node_id cluster nodes] \n] {
30 set args [split $item " "]
31 if {[lindex $args 0] eq $target_cid} {
32 return [lindex $args 5]
33 }
34 }
35 fail "Target node ID was not present"
36}
37
38set master_id 0
39
40test "Fill up primary with data" {
41 # Set 1 MB of data
42 R $master_id debug populate 1000 key 1000
43}
44
45test "Add new node as replica" {
46 set replica_id 1
47 set replica [R $replica_id CLUSTER MYID]
48 R $replica_id cluster replicate [R $master_id CLUSTER MYID]
49}
50
51test "Check digest and replica state" {
52 wait_for_condition 1000 50 {
53 [is_in_slots $master_id $replica]
54 } else {
55 fail "New replica didn't appear in the slots"
56 }
57
58 wait_for_condition 100 50 {
59 [is_replica_online [R $master_id info replication]]
60 } else {
61 fail "Replica is down for too long"
62 }
63 set replica_digest [R $replica_id debug digest]
64 assert {$replica_digest ne 0}
65}
66
67test "Replica in loading state is hidden" {
68 # Kill replica client for master and load new data to the primary
69 R $master_id config set repl-backlog-size 100
70
71 # Set the key load delay so that it will take at least
72 # 2 seconds to fully load the data.
73 R $replica_id config set key-load-delay 4000
74
75 # Trigger event loop processing every 1024 bytes, this trigger
76 # allows us to send and receive cluster messages, so we are setting
77 # it low so that the cluster messages are sent more frequently.
78 R $replica_id config set loading-process-events-interval-bytes 1024
79
80 R $master_id multi
81 R $master_id client kill type replica
82 set num 100
83 set value [string repeat A 1024]
84 for {set j 0} {$j < $num} {incr j} {
85 set key "{0}"
86 append key $j
87 R $master_id set $key $value
88 }
89 R $master_id exec
90
91 # The master will be the last to know the replica
92 # is loading, so we will wait on that and assert
93 # the replica is loading afterwards.
94 wait_for_condition 100 50 {
95 ![is_in_slots $master_id $replica]
96 } else {
97 fail "Replica was always present in cluster slots"
98 }
99 assert_equal 1 [s $replica_id loading]
100
101 # Wait for the replica to finish full-sync and become online
102 wait_for_condition 200 50 {
103 [s $replica_id master_link_status] eq "up"
104 } else {
105 fail "Replica didn't finish loading"
106 }
107
108 # Return configs to default values
109 R $replica_id config set loading-process-events-interval-bytes 2097152
110 R $replica_id config set key-load-delay 0
111
112 # Check replica is back in cluster slots
113 wait_for_condition 100 50 {
114 [is_in_slots $master_id $replica]
115 } else {
116 fail "Replica is not back to slots"
117 }
118 assert_equal 1 [is_in_slots $replica_id $replica]
119}
120
121test "Check disconnected replica not hidden from slots" {
122 # We want to disconnect the replica, but keep it alive so it can still gossip
123
124 # Make sure that the replica will not be able to re-connect to the master
125 R $master_id config set requirepass asdf
126
127 # Disconnect replica from primary
128 R $master_id client kill type replica
129
130 # Check master to have no replicas
131 assert {[s $master_id connected_slaves] == 0}
132
133 set replica_cid [R $replica_id cluster myid]
134 set initial_pong [get_last_pong_time $master_id $replica_cid]
135 wait_for_condition 50 100 {
136 $initial_pong != [get_last_pong_time $master_id $replica_cid]
137 } else {
138 fail "Primary never received gossip from replica"
139 }
140
141 # Check that replica is still in the cluster slots
142 assert {[is_in_slots $master_id $replica]}
143
144 # undo config
145 R $master_id config set requirepass ""
146}
diff --git a/examples/redis-unstable/tests/cluster/tests/25-pubsubshard-slot-migration.tcl b/examples/redis-unstable/tests/cluster/tests/25-pubsubshard-slot-migration.tcl
deleted file mode 100644
index fd774a8..0000000
--- a/examples/redis-unstable/tests/cluster/tests/25-pubsubshard-slot-migration.tcl
+++ /dev/null
@@ -1,212 +0,0 @@
1source "../tests/includes/init-tests.tcl"
2
3test "Create a 3 nodes cluster" {
4 cluster_create_with_continuous_slots 3 3
5}
6
7test "Cluster is up" {
8 assert_cluster_state ok
9}
10
11set cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]]
12
13proc get_addr_replica_serving_slot slot {
14 set cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]]
15 array set node [$cluster masternode_for_slot $slot]
16
17 set replicanodeinfo [$cluster cluster replicas $node(id)]
18 set args [split $replicanodeinfo " "]
19 set addr [lindex [split [lindex $args 1] @] 0]
20 set replicahost [lindex [split $addr :] 0]
21 set replicaport [lindex [split $addr :] 1]
22 return [list $replicahost $replicaport]
23}
24
25test "Migrate a slot, verify client receives sunsubscribe on primary serving the slot." {
26
27 # Setup the to and from node
28 set channelname mychannel
29 set slot [$cluster cluster keyslot $channelname]
30 array set nodefrom [$cluster masternode_for_slot $slot]
31 array set nodeto [$cluster masternode_notfor_slot $slot]
32
33 set subscribeclient [redis_deferring_client_by_addr $nodefrom(host) $nodefrom(port)]
34
35 $subscribeclient deferred 1
36 $subscribeclient ssubscribe $channelname
37 $subscribeclient read
38
39 assert_equal {OK} [$nodefrom(link) cluster setslot $slot migrating $nodeto(id)]
40 assert_equal {OK} [$nodeto(link) cluster setslot $slot importing $nodefrom(id)]
41
42 # Verify subscribe is still valid, able to receive messages.
43 $nodefrom(link) spublish $channelname hello
44 assert_equal {smessage mychannel hello} [$subscribeclient read]
45
46 assert_equal {OK} [$nodefrom(link) cluster setslot $slot node $nodeto(id)]
47
48 set msg [$subscribeclient read]
49 assert {"sunsubscribe" eq [lindex $msg 0]}
50 assert {$channelname eq [lindex $msg 1]}
51 assert {"0" eq [lindex $msg 2]}
52
53 assert_equal {OK} [$nodeto(link) cluster setslot $slot node $nodeto(id)]
54
55 $subscribeclient close
56}
57
58test "Client subscribes to multiple channels, migrate a slot, verify client receives sunsubscribe on primary serving the slot." {
59
60 # Setup the to and from node
61 set channelname ch3
62 set anotherchannelname ch7
63 set slot [$cluster cluster keyslot $channelname]
64 array set nodefrom [$cluster masternode_for_slot $slot]
65 array set nodeto [$cluster masternode_notfor_slot $slot]
66
67 set subscribeclient [redis_deferring_client_by_addr $nodefrom(host) $nodefrom(port)]
68
69 $subscribeclient deferred 1
70 $subscribeclient ssubscribe $channelname
71 $subscribeclient read
72
73 $subscribeclient ssubscribe $anotherchannelname
74 $subscribeclient read
75
76 assert_equal {OK} [$nodefrom(link) cluster setslot $slot migrating $nodeto(id)]
77 assert_equal {OK} [$nodeto(link) cluster setslot $slot importing $nodefrom(id)]
78
79 # Verify subscribe is still valid, able to receive messages.
80 $nodefrom(link) spublish $channelname hello
81 assert_equal {smessage ch3 hello} [$subscribeclient read]
82
83 assert_equal {OK} [$nodefrom(link) cluster setslot $slot node $nodeto(id)]
84
85 # Verify the client receives sunsubscribe message for the channel(slot) which got migrated.
86 set msg [$subscribeclient read]
87 assert {"sunsubscribe" eq [lindex $msg 0]}
88 assert {$channelname eq [lindex $msg 1]}
89 assert {"1" eq [lindex $msg 2]}
90
91 assert_equal {OK} [$nodeto(link) cluster setslot $slot node $nodeto(id)]
92
93 $nodefrom(link) spublish $anotherchannelname hello
94
95 # Verify the client is still connected and receives message from the other channel.
96 set msg [$subscribeclient read]
97 assert {"smessage" eq [lindex $msg 0]}
98 assert {$anotherchannelname eq [lindex $msg 1]}
99 assert {"hello" eq [lindex $msg 2]}
100
101 $subscribeclient close
102}
103
104test "Migrate a slot, verify client receives sunsubscribe on replica serving the slot." {
105
106 # Setup the to and from node
107 set channelname mychannel1
108 set slot [$cluster cluster keyslot $channelname]
109 array set nodefrom [$cluster masternode_for_slot $slot]
110 array set nodeto [$cluster masternode_notfor_slot $slot]
111
112 # Get replica node serving slot (mychannel) to connect a client.
113 set replica_addr [get_addr_replica_serving_slot $slot]
114 set replicahost [lindex $replica_addr 0]
115 set replicaport [lindex $replica_addr 1]
116 set subscribeclient [redis_deferring_client_by_addr $replicahost $replicaport]
117
118 $subscribeclient deferred 1
119 $subscribeclient ssubscribe $channelname
120 $subscribeclient read
121
122 assert_equal {OK} [$nodefrom(link) cluster setslot $slot migrating $nodeto(id)]
123 assert_equal {OK} [$nodeto(link) cluster setslot $slot importing $nodefrom(id)]
124
125 # Verify subscribe is still valid, able to receive messages.
126 $nodefrom(link) spublish $channelname hello
127 assert_equal {smessage mychannel1 hello} [$subscribeclient read]
128
129 assert_equal {OK} [$nodefrom(link) cluster setslot $slot node $nodeto(id)]
130 assert_equal {OK} [$nodeto(link) cluster setslot $slot node $nodeto(id)]
131
132 set msg [$subscribeclient read]
133 assert {"sunsubscribe" eq [lindex $msg 0]}
134 assert {$channelname eq [lindex $msg 1]}
135 assert {"0" eq [lindex $msg 2]}
136
137 $subscribeclient close
138}
139
140test "Move a replica to another primary, verify client receives sunsubscribe on replica serving the slot." {
141 # Setup the to and from node
142 set channelname mychannel2
143 set slot [$cluster cluster keyslot $channelname]
144
145 array set nodefrom [$cluster masternode_for_slot $slot]
146 array set nodeto [$cluster masternode_notfor_slot $slot]
147 set replica_addr [get_addr_replica_serving_slot $slot]
148 set replica_host [lindex $replica_addr 0]
149 set replica_port [lindex $replica_addr 1]
150 set replica_client [redis_client_by_addr $replica_host $replica_port]
151 set subscribeclient [redis_deferring_client_by_addr $replica_host $replica_port]
152
153 $subscribeclient deferred 1
154 $subscribeclient ssubscribe $channelname
155 $subscribeclient read
156
157 # Verify subscribe is still valid, able to receive messages.
158 $nodefrom(link) spublish $channelname hello
159 assert_equal {smessage mychannel2 hello} [$subscribeclient read]
160
161 assert_equal {OK} [$replica_client cluster replicate $nodeto(id)]
162
163 set msg [$subscribeclient read]
164 assert {"sunsubscribe" eq [lindex $msg 0]}
165 assert {$channelname eq [lindex $msg 1]}
166 assert {"0" eq [lindex $msg 2]}
167
168 $subscribeclient close
169}
170
171test "Delete a slot, verify sunsubscribe message" {
172 set channelname ch2
173 set slot [$cluster cluster keyslot $channelname]
174
175 array set primary_client [$cluster masternode_for_slot $slot]
176
177 set subscribeclient [redis_deferring_client_by_addr $primary_client(host) $primary_client(port)]
178 $subscribeclient deferred 1
179 $subscribeclient ssubscribe $channelname
180 $subscribeclient read
181
182 $primary_client(link) cluster DELSLOTS $slot
183
184 set msg [$subscribeclient read]
185 assert {"sunsubscribe" eq [lindex $msg 0]}
186 assert {$channelname eq [lindex $msg 1]}
187 assert {"0" eq [lindex $msg 2]}
188
189 $subscribeclient close
190}
191
192test "Reset cluster, verify sunsubscribe message" {
193 set channelname ch4
194 set slot [$cluster cluster keyslot $channelname]
195
196 array set primary_client [$cluster masternode_for_slot $slot]
197
198 set subscribeclient [redis_deferring_client_by_addr $primary_client(host) $primary_client(port)]
199 $subscribeclient deferred 1
200 $subscribeclient ssubscribe $channelname
201 $subscribeclient read
202
203 $cluster cluster reset HARD
204
205 set msg [$subscribeclient read]
206 assert {"sunsubscribe" eq [lindex $msg 0]}
207 assert {$channelname eq [lindex $msg 1]}
208 assert {"0" eq [lindex $msg 2]}
209
210 $cluster close
211 $subscribeclient close
212} \ No newline at end of file
diff --git a/examples/redis-unstable/tests/cluster/tests/26-pubsubshard.tcl b/examples/redis-unstable/tests/cluster/tests/26-pubsubshard.tcl
deleted file mode 100644
index 34939ac..0000000
--- a/examples/redis-unstable/tests/cluster/tests/26-pubsubshard.tcl
+++ /dev/null
@@ -1,130 +0,0 @@
1# Test PUBSUB shard propagation in a cluster slot.
2
3source "../tests/includes/init-tests.tcl"
4
5test "Create a 3 nodes cluster" {
6 cluster_create_with_continuous_slots 3 3
7}
8
9set cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]]
10test "Pub/Sub shard basics" {
11
12 set slot [$cluster cluster keyslot "channel.0"]
13 array set publishnode [$cluster masternode_for_slot $slot]
14 array set notshardnode [$cluster masternode_notfor_slot $slot]
15
16 set publishclient [redis_client_by_addr $publishnode(host) $publishnode(port)]
17 set subscribeclient [redis_deferring_client_by_addr $publishnode(host) $publishnode(port)]
18 set subscribeclient2 [redis_deferring_client_by_addr $publishnode(host) $publishnode(port)]
19 set anotherclient [redis_deferring_client_by_addr $notshardnode(host) $notshardnode(port)]
20
21 $subscribeclient ssubscribe channel.0
22 $subscribeclient read
23
24 $subscribeclient2 ssubscribe channel.0
25 $subscribeclient2 read
26
27 $anotherclient ssubscribe channel.0
28 catch {$anotherclient read} err
29 assert_match {MOVED *} $err
30
31 set data [randomValue]
32 $publishclient spublish channel.0 $data
33
34 set msg [$subscribeclient read]
35 assert_equal $data [lindex $msg 2]
36
37 set msg [$subscribeclient2 read]
38 assert_equal $data [lindex $msg 2]
39
40 $publishclient close
41 $subscribeclient close
42 $subscribeclient2 close
43 $anotherclient close
44}
45
46test "client can't subscribe to multiple shard channels across different slots in same call" {
47 catch {$cluster ssubscribe channel.0 channel.1} err
48 assert_match {CROSSSLOT Keys*} $err
49}
50
51test "client can subscribe to multiple shard channels across different slots in separate call" {
52 $cluster ssubscribe ch3
53 $cluster ssubscribe ch7
54
55 $cluster sunsubscribe ch3
56 $cluster sunsubscribe ch7
57}
58
59test "sunsubscribe without specifying any channel would unsubscribe all shard channels subscribed" {
60 set publishclient [redis_client_by_addr $publishnode(host) $publishnode(port)]
61 set subscribeclient [redis_deferring_client_by_addr $publishnode(host) $publishnode(port)]
62
63 set sub_res [ssubscribe $subscribeclient [list "\{channel.0\}1" "\{channel.0\}2" "\{channel.0\}3"]]
64 assert_equal [list 1 2 3] $sub_res
65 sunsubscribe $subscribeclient
66
67 assert_equal 0 [$publishclient spublish "\{channel.0\}1" hello]
68 assert_equal 0 [$publishclient spublish "\{channel.0\}2" hello]
69 assert_equal 0 [$publishclient spublish "\{channel.0\}3" hello]
70
71 $publishclient close
72 $subscribeclient close
73}
74
75test "Verify Pub/Sub and Pub/Sub shard no overlap" {
76 set slot [$cluster cluster keyslot "channel.0"]
77 array set publishnode [$cluster masternode_for_slot $slot]
78 array set notshardnode [$cluster masternode_notfor_slot $slot]
79
80 set publishshardclient [redis_client_by_addr $publishnode(host) $publishnode(port)]
81 set publishclient [redis_deferring_client_by_addr $publishnode(host) $publishnode(port)]
82 set subscribeshardclient [redis_deferring_client_by_addr $publishnode(host) $publishnode(port)]
83 set subscribeclient [redis_deferring_client_by_addr $publishnode(host) $publishnode(port)]
84
85 $subscribeshardclient deferred 1
86 $subscribeshardclient ssubscribe channel.0
87 $subscribeshardclient read
88
89 $subscribeclient deferred 1
90 $subscribeclient subscribe channel.0
91 $subscribeclient read
92
93 set sharddata "testingpubsubdata"
94 $publishshardclient spublish channel.0 $sharddata
95
96 set data "somemoredata"
97 $publishclient publish channel.0 $data
98
99 set msg [$subscribeshardclient read]
100 assert_equal $sharddata [lindex $msg 2]
101
102 set msg [$subscribeclient read]
103 assert_equal $data [lindex $msg 2]
104
105 $cluster close
106 $publishclient close
107 $subscribeclient close
108 $subscribeshardclient close
109}
110
111test "PUBSUB channels/shardchannels" {
112 set subscribeclient [redis_deferring_client_by_addr $publishnode(host) $publishnode(port)]
113 set subscribeclient2 [redis_deferring_client_by_addr $publishnode(host) $publishnode(port)]
114 set subscribeclient3 [redis_deferring_client_by_addr $publishnode(host) $publishnode(port)]
115 set publishclient [redis_client_by_addr $publishnode(host) $publishnode(port)]
116
117 ssubscribe $subscribeclient [list "\{channel.0\}1"]
118 ssubscribe $subscribeclient2 [list "\{channel.0\}2"]
119 ssubscribe $subscribeclient3 [list "\{channel.0\}3"]
120 assert_equal {3} [llength [$publishclient pubsub shardchannels]]
121
122 subscribe $subscribeclient [list "\{channel.0\}4"]
123 assert_equal {3} [llength [$publishclient pubsub shardchannels]]
124
125 sunsubscribe $subscribeclient
126 set channel_list [$publishclient pubsub shardchannels]
127 assert_equal {2} [llength $channel_list]
128 assert {[lsearch -exact $channel_list "\{channel.0\}2"] >= 0}
129 assert {[lsearch -exact $channel_list "\{channel.0\}3"] >= 0}
130}
diff --git a/examples/redis-unstable/tests/cluster/tests/28-cluster-shards.tcl b/examples/redis-unstable/tests/cluster/tests/28-cluster-shards.tcl
deleted file mode 100644
index 2271ff6..0000000
--- a/examples/redis-unstable/tests/cluster/tests/28-cluster-shards.tcl
+++ /dev/null
@@ -1,292 +0,0 @@
1source "../tests/includes/init-tests.tcl"
2
3# Initial slot distribution.
4set ::slot0 [list 0 1000 1002 5459 5461 5461 10926 10926]
5set ::slot1 [list 5460 5460 5462 10922 10925 10925]
6set ::slot2 [list 10923 10924 10927 16383]
7set ::slot3 [list 1001 1001]
8
9proc cluster_create_with_split_slots {masters replicas} {
10 for {set j 0} {$j < $masters} {incr j} {
11 R $j cluster ADDSLOTSRANGE {*}[set ::slot${j}]
12 }
13 if {$replicas} {
14 cluster_allocate_slaves $masters $replicas
15 }
16 set ::cluster_master_nodes $masters
17 set ::cluster_replica_nodes $replicas
18}
19
20# Get the node info with the specific node_id from the
21# given reference node. Valid type options are "node" and "shard"
22proc get_node_info_from_shard {id reference {type node}} {
23 set shards_response [R $reference CLUSTER SHARDS]
24 foreach shard_response $shards_response {
25 set nodes [dict get $shard_response nodes]
26 foreach node $nodes {
27 if {[dict get $node id] eq $id} {
28 if {$type eq "node"} {
29 return $node
30 } elseif {$type eq "shard"} {
31 return $shard_response
32 } else {
33 return {}
34 }
35 }
36 }
37 }
38 # No shard found, return nothing
39 return {}
40}
41
42proc cluster_ensure_master {id} {
43 if { [regexp "master" [R $id role]] == 0 } {
44 assert_equal {OK} [R $id CLUSTER FAILOVER]
45 wait_for_condition 50 100 {
46 [regexp "master" [R $id role]] == 1
47 } else {
48 fail "instance $id is not master"
49 }
50 }
51}
52
53test "Create a 8 nodes cluster with 4 shards" {
54 cluster_create_with_split_slots 4 4
55}
56
57test "Cluster should start ok" {
58 assert_cluster_state ok
59}
60
61test "Set cluster hostnames and verify they are propagated" {
62 for {set j 0} {$j < $::cluster_master_nodes + $::cluster_replica_nodes} {incr j} {
63 R $j config set cluster-announce-hostname "host-$j.com"
64 }
65
66 # Wait for everyone to agree about the state
67 wait_for_cluster_propagation
68}
69
70test "Verify information about the shards" {
71 set ids {}
72 for {set j 0} {$j < $::cluster_master_nodes + $::cluster_replica_nodes} {incr j} {
73 lappend ids [R $j CLUSTER MYID]
74 }
75 set slots [list $::slot0 $::slot1 $::slot2 $::slot3 $::slot0 $::slot1 $::slot2 $::slot3]
76
77 # Verify on each node (primary/replica), the response of the `CLUSTER SLOTS` command is consistent.
78 for {set ref 0} {$ref < $::cluster_master_nodes + $::cluster_replica_nodes} {incr ref} {
79 for {set i 0} {$i < $::cluster_master_nodes + $::cluster_replica_nodes} {incr i} {
80 assert_equal [lindex $slots $i] [dict get [get_node_info_from_shard [lindex $ids $i] $ref "shard"] slots]
81 assert_equal "host-$i.com" [dict get [get_node_info_from_shard [lindex $ids $i] $ref "node"] hostname]
82 assert_equal "127.0.0.1" [dict get [get_node_info_from_shard [lindex $ids $i] $ref "node"] ip]
83 # Default value of 'cluster-preferred-endpoint-type' is ip.
84 assert_equal "127.0.0.1" [dict get [get_node_info_from_shard [lindex $ids $i] $ref "node"] endpoint]
85
86 if {$::tls} {
87 assert_equal [get_instance_attrib redis $i plaintext-port] [dict get [get_node_info_from_shard [lindex $ids $i] $ref "node"] port]
88 assert_equal [get_instance_attrib redis $i port] [dict get [get_node_info_from_shard [lindex $ids $i] $ref "node"] tls-port]
89 } else {
90 assert_equal [get_instance_attrib redis $i port] [dict get [get_node_info_from_shard [lindex $ids $i] $ref "node"] port]
91 }
92
93 if {$i < 4} {
94 assert_equal "master" [dict get [get_node_info_from_shard [lindex $ids $i] $ref "node"] role]
95 assert_equal "online" [dict get [get_node_info_from_shard [lindex $ids $i] $ref "node"] health]
96 } else {
97 assert_equal "replica" [dict get [get_node_info_from_shard [lindex $ids $i] $ref "node"] role]
98 # Replica could be in online or loading
99 }
100 }
101 }
102}
103
104test "Verify no slot shard" {
105 # Node 8 has no slots assigned
106 set node_8_id [R 8 CLUSTER MYID]
107 assert_equal {} [dict get [get_node_info_from_shard $node_8_id 8 "shard"] slots]
108 assert_equal {} [dict get [get_node_info_from_shard $node_8_id 0 "shard"] slots]
109}
110
111set node_0_id [R 0 CLUSTER MYID]
112
113test "Kill a node and tell the replica to immediately takeover" {
114 kill_instance redis 0
115 R 4 cluster failover force
116}
117
118# Primary 0 node should report as fail, wait until the new primary acknowledges it.
119test "Verify health as fail for killed node" {
120 wait_for_condition 50 100 {
121 "fail" eq [dict get [get_node_info_from_shard $node_0_id 4 "node"] "health"]
122 } else {
123 fail "New primary never detected the node failed"
124 }
125}
126
127test "Verify that other nodes can correctly output the new master's slots" {
128 assert_not_equal {} [dict get [get_node_info_from_shard [R 4 CLUSTER MYID] 8 "shard"] slots]
129}
130
131set primary_id 4
132set replica_id 0
133
134test "Restarting primary node" {
135 restart_instance redis $replica_id
136}
137
138test "Instance #0 gets converted into a replica" {
139 wait_for_condition 1000 50 {
140 [RI $replica_id role] eq {slave} &&
141 [RI $replica_id master_link_status] eq {up}
142 } else {
143 fail "Old primary was not converted into replica"
144 }
145}
146
147test "Test the replica reports a loading state while it's loading" {
148 # Test the command is good for verifying everything moves to a happy state
149 set replica_cluster_id [R $replica_id CLUSTER MYID]
150 wait_for_condition 50 1000 {
151 [dict get [get_node_info_from_shard $replica_cluster_id $primary_id "node"] health] eq "online"
152 } else {
153 fail "Replica never transitioned to online"
154 }
155
156 # Set 1 MB of data, so there is something to load on full sync
157 R $primary_id debug populate 1000 key 1000
158
159 # Kill replica client for primary and load new data to the primary
160 R $primary_id config set repl-backlog-size 100
161
162 # Set the key load delay so that it will take at least
163 # 2 seconds to fully load the data.
164 R $replica_id config set key-load-delay 4000
165
166 # Trigger event loop processing every 1024 bytes, this trigger
167 # allows us to send and receive cluster messages, so we are setting
168 # it low so that the cluster messages are sent more frequently.
169 R $replica_id config set loading-process-events-interval-bytes 1024
170
171 R $primary_id multi
172 R $primary_id client kill type replica
173 # populate the correct data
174 set num 100
175 set value [string repeat A 1024]
176 for {set j 0} {$j < $num} {incr j} {
177 # Use hashtag valid for shard #0
178 set key "{ch3}$j"
179 R $primary_id set $key $value
180 }
181 R $primary_id exec
182
183 # The replica should reconnect and start a full sync, it will gossip about it's health to the primary.
184 wait_for_condition 50 1000 {
185 "loading" eq [dict get [get_node_info_from_shard $replica_cluster_id $primary_id "node"] health]
186 } else {
187 fail "Replica never transitioned to loading"
188 }
189
190 # Verify cluster shards and cluster slots (deprecated) API responds while the node is loading data.
191 R $replica_id CLUSTER SHARDS
192 R $replica_id CLUSTER SLOTS
193
194 # Speed up the key loading and verify everything resumes
195 R $replica_id config set key-load-delay 0
196
197 wait_for_condition 50 1000 {
198 "online" eq [dict get [get_node_info_from_shard $replica_cluster_id $primary_id "node"] health]
199 } else {
200 fail "Replica never transitioned to online"
201 }
202
203 # Final sanity, the replica agrees it is online.
204 assert_equal "online" [dict get [get_node_info_from_shard $replica_cluster_id $replica_id "node"] health]
205}
206
207test "Regression test for a crash when calling SHARDS during handshake" {
208 # Reset forget a node, so we can use it to establish handshaking connections
209 set id [R 19 CLUSTER MYID]
210 R 19 CLUSTER RESET HARD
211 for {set i 0} {$i < 19} {incr i} {
212 R $i CLUSTER FORGET $id
213 }
214 R 19 cluster meet 127.0.0.1 [get_instance_attrib redis 0 port]
215 # This should line would previously crash, since all the outbound
216 # connections were in handshake state.
217 R 19 CLUSTER SHARDS
218}
219
220test "Cluster is up" {
221 assert_cluster_state ok
222}
223test "Shard ids are unique" {
224 set shard_ids {}
225 for {set i 0} {$i < 4} {incr i} {
226 set shard_id [R $i cluster myshardid]
227 assert_equal [dict exists $shard_ids $shard_id] 0
228 dict set shard_ids $shard_id 1
229 }
230}
231
232test "CLUSTER MYSHARDID reports same id for both primary and replica" {
233 for {set i 0} {$i < 4} {incr i} {
234 assert_equal [R $i cluster myshardid] [R [expr $i+4] cluster myshardid]
235 assert_equal [string length [R $i cluster myshardid]] 40
236 }
237}
238
239test "New replica receives primary's shard id" {
240 #find a primary
241 set id 0
242 for {} {$id < 8} {incr id} {
243 if {[regexp "master" [R $id role]]} {
244 break
245 }
246 }
247 assert_not_equal [R 8 cluster myshardid] [R $id cluster myshardid]
248 assert_equal {OK} [R 8 cluster replicate [R $id cluster myid]]
249 assert_equal [R 8 cluster myshardid] [R $id cluster myshardid]
250}
251
252test "CLUSTER MYSHARDID reports same shard id after shard restart" {
253 set node_ids {}
254 for {set i 0} {$i < 8} {incr i 4} {
255 dict set node_ids $i [R $i cluster myshardid]
256 kill_instance redis $i
257 wait_for_condition 50 100 {
258 [instance_is_killed redis $i]
259 } else {
260 fail "instance $i is not killed"
261 }
262 }
263 for {set i 0} {$i < 8} {incr i 4} {
264 restart_instance redis $i
265 }
266 assert_cluster_state ok
267 for {set i 0} {$i < 8} {incr i 4} {
268 assert_equal [dict get $node_ids $i] [R $i cluster myshardid]
269 }
270}
271
272test "CLUSTER MYSHARDID reports same shard id after cluster restart" {
273 set node_ids {}
274 for {set i 0} {$i < 8} {incr i} {
275 dict set node_ids $i [R $i cluster myshardid]
276 }
277 for {set i 0} {$i < 8} {incr i} {
278 kill_instance redis $i
279 wait_for_condition 50 100 {
280 [instance_is_killed redis $i]
281 } else {
282 fail "instance $i is not killed"
283 }
284 }
285 for {set i 0} {$i < 8} {incr i} {
286 restart_instance redis $i
287 }
288 assert_cluster_state ok
289 for {set i 0} {$i < 8} {incr i} {
290 assert_equal [dict get $node_ids $i] [R $i cluster myshardid]
291 }
292}
diff --git a/examples/redis-unstable/tests/cluster/tests/29-slot-migration-response.tcl b/examples/redis-unstable/tests/cluster/tests/29-slot-migration-response.tcl
deleted file mode 100644
index 060cc8d..0000000
--- a/examples/redis-unstable/tests/cluster/tests/29-slot-migration-response.tcl
+++ /dev/null
@@ -1,50 +0,0 @@
1# Tests for the response of slot migrations.
2
3source "../tests/includes/init-tests.tcl"
4source "../tests/includes/utils.tcl"
5
6test "Create a 2 nodes cluster" {
7 create_cluster 2 0
8 config_set_all_nodes cluster-allow-replica-migration no
9}
10
11test "Cluster is up" {
12 assert_cluster_state ok
13}
14
15set cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]]
16catch {unset nodefrom}
17catch {unset nodeto}
18
19$cluster refresh_nodes_map
20
21test "Set many keys in the cluster" {
22 for {set i 0} {$i < 5000} {incr i} {
23 $cluster set $i $i
24 assert { [$cluster get $i] eq $i }
25 }
26}
27
28test "Test cluster responses during migration of slot x" {
29
30 set slot 10
31 array set nodefrom [$cluster masternode_for_slot $slot]
32 array set nodeto [$cluster masternode_notfor_slot $slot]
33
34 $nodeto(link) cluster setslot $slot importing $nodefrom(id)
35 $nodefrom(link) cluster setslot $slot migrating $nodeto(id)
36
37 # Get a key from that slot
38 set key [$nodefrom(link) cluster GETKEYSINSLOT $slot "1"]
39
40 # MOVED REPLY
41 assert_error "*MOVED*" {$nodeto(link) set $key "newVal"}
42
43 # ASK REPLY
44 assert_error "*ASK*" {$nodefrom(link) set "abc{$key}" "newVal"}
45
46 # UNSTABLE REPLY
47 assert_error "*TRYAGAIN*" {$nodefrom(link) mset "a{$key}" "newVal" $key "newVal2"}
48}
49
50config_set_all_nodes cluster-allow-replica-migration yes
diff --git a/examples/redis-unstable/tests/cluster/tests/helpers/onlydots.tcl b/examples/redis-unstable/tests/cluster/tests/helpers/onlydots.tcl
deleted file mode 100644
index 4a6d1ae..0000000
--- a/examples/redis-unstable/tests/cluster/tests/helpers/onlydots.tcl
+++ /dev/null
@@ -1,16 +0,0 @@
1# Read the standard input and only shows dots in the output, filtering out
2# all the other characters. Designed to avoid bufferization so that when
3# we get the output of redis-trib and want to show just the dots, we'll see
4# the dots as soon as redis-trib will output them.
5
6fconfigure stdin -buffering none
7
8while 1 {
9 set c [read stdin 1]
10 if {$c eq {}} {
11 exit 0; # EOF
12 } elseif {$c eq {.}} {
13 puts -nonewline .
14 flush stdout
15 }
16}
diff --git a/examples/redis-unstable/tests/cluster/tests/includes/init-tests.tcl b/examples/redis-unstable/tests/cluster/tests/includes/init-tests.tcl
deleted file mode 100644
index c0e8982..0000000
--- a/examples/redis-unstable/tests/cluster/tests/includes/init-tests.tcl
+++ /dev/null
@@ -1,102 +0,0 @@
1# Initialization tests -- most units will start including this.
2
3test "(init) Restart killed instances" {
4 foreach type {redis} {
5 foreach_${type}_id id {
6 if {[get_instance_attrib $type $id pid] == -1} {
7 puts -nonewline "$type/$id "
8 flush stdout
9 restart_instance $type $id
10 }
11 }
12 }
13}
14
15test "Cluster nodes are reachable" {
16 foreach_redis_id id {
17 # Every node should be reachable.
18 wait_for_condition 1000 50 {
19 ([catch {R $id ping} ping_reply] == 0) &&
20 ($ping_reply eq {PONG})
21 } else {
22 catch {R $id ping} err
23 fail "Node #$id keeps replying '$err' to PING."
24 }
25 }
26}
27
28test "Cluster nodes hard reset" {
29 foreach_redis_id id {
30 if {$::valgrind} {
31 set node_timeout 10000
32 } else {
33 set node_timeout 3000
34 }
35
36 # Wait until slave is synced. Otherwise, it may reply -LOADING
37 # for any commands below.
38 if {[RI $id role] eq {slave}} {
39 wait_for_condition 50 1000 {
40 [RI $id master_link_status] eq {up}
41 } else {
42 fail "Slave were not able to sync."
43 }
44 }
45
46 catch {R $id flushall} ; # May fail for readonly slaves.
47 R $id MULTI
48 R $id cluster reset hard
49 R $id cluster set-config-epoch [expr {$id+1}]
50 R $id EXEC
51 R $id config set cluster-node-timeout $node_timeout
52 R $id config set cluster-slave-validity-factor 10
53 R $id config set loading-process-events-interval-bytes 2097152
54 R $id config set key-load-delay 0
55 R $id config set repl-diskless-load disabled
56 R $id config set cluster-announce-hostname ""
57 R $id DEBUG DROP-CLUSTER-PACKET-FILTER -1
58 R $id config rewrite
59 }
60}
61
62# Helper function to attempt to have each node in a cluster
63# meet each other.
64proc join_nodes_in_cluster {} {
65 # Join node 0 with 1, 1 with 2, ... and so forth.
66 # If auto-discovery works all nodes will know every other node
67 # eventually.
68 set ids {}
69 foreach_redis_id id {lappend ids $id}
70 for {set j 0} {$j < [expr [llength $ids]-1]} {incr j} {
71 set a [lindex $ids $j]
72 set b [lindex $ids [expr $j+1]]
73 set b_port [get_instance_attrib redis $b port]
74 R $a cluster meet 127.0.0.1 $b_port
75 }
76
77 foreach_redis_id id {
78 wait_for_condition 1000 50 {
79 [llength [get_cluster_nodes $id connected]] == [llength $ids]
80 } else {
81 return 0
82 }
83 }
84 return 1
85}
86
87test "Cluster Join and auto-discovery test" {
88 # Use multiple attempts since sometimes nodes timeout
89 # while attempting to connect.
90 for {set attempts 3} {$attempts > 0} {incr attempts -1} {
91 if {[join_nodes_in_cluster] == 1} {
92 break
93 }
94 }
95 if {$attempts == 0} {
96 fail "Cluster failed to form full mesh"
97 }
98}
99
100test "Before slots allocation, all nodes report cluster failure" {
101 assert_cluster_state fail
102}
diff --git a/examples/redis-unstable/tests/cluster/tests/includes/utils.tcl b/examples/redis-unstable/tests/cluster/tests/includes/utils.tcl
deleted file mode 100644
index c1b0fe6..0000000
--- a/examples/redis-unstable/tests/cluster/tests/includes/utils.tcl
+++ /dev/null
@@ -1,36 +0,0 @@
1source "../../../tests/support/cli.tcl"
2
3proc config_set_all_nodes {keyword value} {
4 foreach_redis_id id {
5 R $id config set $keyword $value
6 }
7}
8
9proc fix_cluster {addr} {
10 set code [catch {
11 exec ../../../src/redis-cli {*}[rediscli_tls_config "../../../tests"] --cluster fix $addr << yes
12 } result]
13 if {$code != 0} {
14 puts "redis-cli --cluster fix returns non-zero exit code, output below:\n$result"
15 }
16 # Note: redis-cli --cluster fix may return a non-zero exit code if nodes don't agree,
17 # but we can ignore that and rely on the check below.
18 assert_cluster_state ok
19 wait_for_condition 100 100 {
20 [catch {exec ../../../src/redis-cli {*}[rediscli_tls_config "../../../tests"] --cluster check $addr} result] == 0
21 } else {
22 puts "redis-cli --cluster check returns non-zero exit code, output below:\n$result"
23 fail "Cluster could not settle with configuration"
24 }
25}
26
27proc wait_cluster_stable {} {
28 wait_for_condition 1000 50 {
29 [catch {exec ../../../src/redis-cli --cluster \
30 check 127.0.0.1:[get_instance_attrib redis 0 port] \
31 {*}[rediscli_tls_config "../../../tests"] \
32 }] == 0
33 } else {
34 fail "Cluster doesn't stabilize"
35 }
36} \ No newline at end of file
diff --git a/examples/redis-unstable/tests/cluster/tmp/.gitignore b/examples/redis-unstable/tests/cluster/tmp/.gitignore
deleted file mode 100644
index f581f73..0000000
--- a/examples/redis-unstable/tests/cluster/tmp/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
1redis_*
2sentinel_*