diff options
Diffstat (limited to 'examples/redis-unstable/tests/cluster')
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 | ||
| 11 | set ::cluster_master_nodes 0 | ||
| 12 | set ::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. | ||
| 16 | proc 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. | ||
| 42 | proc 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. | ||
| 47 | proc 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' | ||
| 57 | proc 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. | ||
| 66 | proc CI {n field} { | ||
| 67 | get_info_field [R $n cluster info] $field | ||
| 68 | } | ||
| 69 | |||
| 70 | # Return the value of the specified INFO field. | ||
| 71 | proc 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. | ||
| 77 | proc 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. | ||
| 91 | proc 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 | |||
| 104 | proc 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. | ||
| 115 | proc assert_secrets_match {} { | ||
| 116 | assert_equal {1} [num_unique_secrets] | ||
| 117 | } | ||
| 118 | |||
| 119 | proc 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. | ||
| 129 | proc 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. | ||
| 142 | proc 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. | ||
| 152 | proc 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 | |||
| 163 | proc 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. | ||
| 178 | proc 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. | ||
| 191 | proc 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. | ||
| 199 | proc 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 | ||
| 213 | proc 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. | ||
| 229 | proc 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. | ||
| 249 | proc 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 | ||
| 258 | proc 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 | |||
| 10 | cd tests/cluster | ||
| 11 | source cluster.tcl | ||
| 12 | source ../instances.tcl | ||
| 13 | source ../../support/cluster.tcl ; # Redis Cluster client. | ||
| 14 | |||
| 15 | set ::instances_count 20 ; # How many instances we use at max. | ||
| 16 | set ::tlsdir "../../tls" | ||
| 17 | |||
| 18 | proc 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 | |||
| 32 | if {[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 | |||
| 3 | source "../tests/includes/init-tests.tcl" | ||
| 4 | |||
| 5 | if {$::simulate_error} { | ||
| 6 | test "This test will fail" { | ||
| 7 | fail "Simulated error" | ||
| 8 | } | ||
| 9 | } | ||
| 10 | |||
| 11 | test "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 | |||
| 25 | test "It is possible to perform slot allocation" { | ||
| 26 | cluster_allocate_slots 5 | ||
| 27 | } | ||
| 28 | |||
| 29 | test "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 | |||
| 53 | test "Nodes should report cluster_state is ok now" { | ||
| 54 | assert_cluster_state ok | ||
| 55 | } | ||
| 56 | |||
| 57 | test "Sanity for CLUSTER COUNTKEYSINSLOT" { | ||
| 58 | set reply [R 0 CLUSTER COUNTKEYSINSLOT 0] | ||
| 59 | assert {$reply eq 0} | ||
| 60 | } | ||
| 61 | |||
| 62 | test "It is possible to write and read from the cluster" { | ||
| 63 | cluster_write_test 0 | ||
| 64 | } | ||
| 65 | |||
| 66 | test "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 | |||
| 78 | test "Coverage: CLUSTER HELP" { | ||
| 79 | assert_match "*CLUSTER <subcommand> *" [R 0 CLUSTER HELP] | ||
| 80 | } | ||
| 81 | |||
| 82 | test "Coverage: ASKING" { | ||
| 83 | assert_equal {OK} [R 0 ASKING] | ||
| 84 | } | ||
| 85 | |||
| 86 | test "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 | |||
| 91 | test "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 | |||
| 3 | source "../tests/includes/init-tests.tcl" | ||
| 4 | |||
| 5 | test "Create a 5 nodes cluster" { | ||
| 6 | create_cluster 5 5 | ||
| 7 | } | ||
| 8 | |||
| 9 | test "Cluster should start ok" { | ||
| 10 | assert_cluster_state ok | ||
| 11 | } | ||
| 12 | |||
| 13 | test "Killing two slave nodes" { | ||
| 14 | kill_instance redis 5 | ||
| 15 | kill_instance redis 6 | ||
| 16 | } | ||
| 17 | |||
| 18 | test "Cluster should be still up" { | ||
| 19 | assert_cluster_state ok | ||
| 20 | } | ||
| 21 | |||
| 22 | test "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. | ||
| 28 | test "Cluster should be down now" { | ||
| 29 | assert_cluster_state fail | ||
| 30 | } | ||
| 31 | |||
| 32 | test "Restarting master node" { | ||
| 33 | restart_instance redis 0 | ||
| 34 | } | ||
| 35 | |||
| 36 | test "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 | |||
| 3 | source "../tests/includes/init-tests.tcl" | ||
| 4 | |||
| 5 | test "Create a 5 nodes cluster" { | ||
| 6 | create_cluster 5 5 | ||
| 7 | } | ||
| 8 | |||
| 9 | test "Cluster is up" { | ||
| 10 | assert_cluster_state ok | ||
| 11 | } | ||
| 12 | |||
| 13 | test "Cluster is writable" { | ||
| 14 | cluster_write_test 0 | ||
| 15 | } | ||
| 16 | |||
| 17 | test "Instance #5 is a slave" { | ||
| 18 | assert {[RI 5 role] eq {slave}} | ||
| 19 | } | ||
| 20 | |||
| 21 | test "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 | |||
| 29 | set current_epoch [CI 1 cluster_current_epoch] | ||
| 30 | |||
| 31 | test "Killing one master node" { | ||
| 32 | kill_instance redis 0 | ||
| 33 | } | ||
| 34 | |||
| 35 | test "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 | |||
| 43 | test "Cluster should eventually be up again" { | ||
| 44 | assert_cluster_state ok | ||
| 45 | } | ||
| 46 | |||
| 47 | test "Cluster is writable" { | ||
| 48 | cluster_write_test 1 | ||
| 49 | } | ||
| 50 | |||
| 51 | test "Instance #5 is now a master" { | ||
| 52 | assert {[RI 5 role] eq {master}} | ||
| 53 | } | ||
| 54 | |||
| 55 | test "Restarting the previously killed master node" { | ||
| 56 | restart_instance redis 0 | ||
| 57 | } | ||
| 58 | |||
| 59 | test "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 | |||
| 6 | source "../tests/includes/init-tests.tcl" | ||
| 7 | |||
| 8 | test "Create a 5 nodes cluster" { | ||
| 9 | create_cluster 5 5 | ||
| 10 | } | ||
| 11 | |||
| 12 | test "Cluster is up" { | ||
| 13 | assert_cluster_state ok | ||
| 14 | } | ||
| 15 | |||
| 16 | set iterations 20 | ||
| 17 | set cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]] | ||
| 18 | |||
| 19 | while {[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 | |||
| 113 | test "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 | |||
| 6 | source "../tests/includes/init-tests.tcl" | ||
| 7 | source "../../../tests/support/cli.tcl" | ||
| 8 | |||
| 9 | test "Create a 5 nodes cluster" { | ||
| 10 | create_cluster 5 5 | ||
| 11 | } | ||
| 12 | |||
| 13 | test "Cluster is up" { | ||
| 14 | assert_cluster_state ok | ||
| 15 | } | ||
| 16 | |||
| 17 | test "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. | ||
| 37 | proc 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 | |||
| 55 | set numkeys 50000 | ||
| 56 | set numops 200000 | ||
| 57 | set start_node_port [get_instance_attrib redis 0 port] | ||
| 58 | set cluster [redis_cluster 127.0.0.1:$start_node_port] | ||
| 59 | if {$::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 | } | ||
| 68 | catch {unset content} | ||
| 69 | array set content {} | ||
| 70 | set tribpid {} | ||
| 71 | |||
| 72 | test "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 | |||
| 131 | test "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 | |||
| 140 | test "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 | |||
| 149 | test "Cluster should eventually be up again" { | ||
| 150 | assert_cluster_state ok | ||
| 151 | } | ||
| 152 | |||
| 153 | test "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 | |||
| 162 | test "Disable AOF in all the instances" { | ||
| 163 | foreach_redis_id id { | ||
| 164 | R $id config set appendonly no | ||
| 165 | } | ||
| 166 | } | ||
| 167 | |||
| 168 | test "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 | |||
| 191 | test "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 | |||
| 4 | source "../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. | ||
| 8 | test "Create a 5 nodes cluster" { | ||
| 9 | create_cluster 5 10 | ||
| 10 | } | ||
| 11 | |||
| 12 | test "Cluster is up" { | ||
| 13 | assert_cluster_state ok | ||
| 14 | } | ||
| 15 | |||
| 16 | test "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 | |||
| 25 | test "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 | |||
| 36 | test {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 | |||
| 42 | test "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 | |||
| 51 | set cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]] | ||
| 52 | |||
| 53 | test "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 | |||
| 60 | test "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 | |||
| 86 | test "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 | |||
| 94 | test "Wait for the node #10 to return alive before ending the test" { | ||
| 95 | R 10 ping | ||
| 96 | } | ||
| 97 | |||
| 98 | test "Cluster should eventually be up again" { | ||
| 99 | assert_cluster_state ok | ||
| 100 | } | ||
| 101 | |||
| 102 | test "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 | |||
| 112 | source "../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. | ||
| 116 | test "Create a 3 nodes cluster" { | ||
| 117 | create_cluster 3 15 | ||
| 118 | } | ||
| 119 | |||
| 120 | test "Cluster is up" { | ||
| 121 | assert_cluster_state ok | ||
| 122 | } | ||
| 123 | |||
| 124 | test "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 | |||
| 132 | test {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 | |||
| 141 | test {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 | |||
| 153 | proc 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 | |||
| 163 | test "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 | |||
| 5 | source "../tests/includes/init-tests.tcl" | ||
| 6 | |||
| 7 | # Create a cluster with 5 master and 5 slaves. | ||
| 8 | test "Create a 5 nodes cluster" { | ||
| 9 | create_cluster 5 5 | ||
| 10 | } | ||
| 11 | |||
| 12 | test "Cluster is up" { | ||
| 13 | assert_cluster_state ok | ||
| 14 | } | ||
| 15 | |||
| 16 | test "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 | |||
| 24 | test {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 | |||
| 29 | test "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 | |||
| 37 | test "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 | |||
| 41 | test "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 | |||
| 66 | test "Slave #5 is reachable and alive" { | ||
| 67 | assert {[R 5 ping] eq {PONG}} | ||
| 68 | } | ||
| 69 | |||
| 70 | test "Slave #5 should not be able to failover" { | ||
| 71 | after 10000 | ||
| 72 | assert {[RI 5 role] eq {slave}} | ||
| 73 | } | ||
| 74 | |||
| 75 | test "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 | |||
| 5 | source "../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. | ||
| 9 | test "Create a 5 nodes cluster" { | ||
| 10 | create_cluster 5 10 | ||
| 11 | } | ||
| 12 | |||
| 13 | test "Cluster is up" { | ||
| 14 | assert_cluster_state ok | ||
| 15 | } | ||
| 16 | |||
| 17 | test "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 | |||
| 29 | test "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 | |||
| 37 | foreach_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 | |||
| 52 | source "../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. | ||
| 56 | test "Create a 5 nodes cluster" { | ||
| 57 | create_cluster 5 10 | ||
| 58 | } | ||
| 59 | |||
| 60 | test "Cluster is up" { | ||
| 61 | assert_cluster_state ok | ||
| 62 | } | ||
| 63 | |||
| 64 | test "Kill slave #7 of master #2. Only slave left is #12 now" { | ||
| 65 | kill_instance redis 7 | ||
| 66 | } | ||
| 67 | |||
| 68 | set current_epoch [CI 1 cluster_current_epoch] | ||
| 69 | |||
| 70 | test "Killing master node #2, #12 should failover" { | ||
| 71 | kill_instance redis 2 | ||
| 72 | } | ||
| 73 | |||
| 74 | test "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 | |||
| 82 | test "Cluster should eventually be up again" { | ||
| 83 | assert_cluster_state ok | ||
| 84 | } | ||
| 85 | |||
| 86 | test "Cluster is writable" { | ||
| 87 | cluster_write_test 1 | ||
| 88 | } | ||
| 89 | |||
| 90 | test "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 | |||
| 97 | test "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 | |||
| 12 | source "../tests/includes/init-tests.tcl" | ||
| 13 | |||
| 14 | test "Create a 5 nodes cluster" { | ||
| 15 | create_cluster 5 5 | ||
| 16 | } | ||
| 17 | |||
| 18 | test "Cluster is up" { | ||
| 19 | assert_cluster_state ok | ||
| 20 | } | ||
| 21 | |||
| 22 | test "Cluster is writable" { | ||
| 23 | cluster_write_test 0 | ||
| 24 | } | ||
| 25 | |||
| 26 | test "Instance #5 is a slave" { | ||
| 27 | assert {[RI 5 role] eq {slave}} | ||
| 28 | } | ||
| 29 | |||
| 30 | test "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 | |||
| 38 | set current_epoch [CI 1 cluster_current_epoch] | ||
| 39 | |||
| 40 | test "Killing one master node" { | ||
| 41 | kill_instance redis 0 | ||
| 42 | } | ||
| 43 | |||
| 44 | test "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 | |||
| 52 | test "Cluster should eventually be up again" { | ||
| 53 | assert_cluster_state ok | ||
| 54 | } | ||
| 55 | |||
| 56 | test "Cluster is writable" { | ||
| 57 | cluster_write_test 1 | ||
| 58 | } | ||
| 59 | |||
| 60 | test "Instance #5 is now a master" { | ||
| 61 | assert {[RI 5 role] eq {master}} | ||
| 62 | } | ||
| 63 | |||
| 64 | test "Killing the new master #5" { | ||
| 65 | kill_instance redis 5 | ||
| 66 | } | ||
| 67 | |||
| 68 | test "Cluster should be down now" { | ||
| 69 | assert_cluster_state fail | ||
| 70 | } | ||
| 71 | |||
| 72 | test "Restarting the old master node" { | ||
| 73 | restart_instance redis 0 | ||
| 74 | } | ||
| 75 | |||
| 76 | test "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 | |||
| 84 | test "Restarting the new master node" { | ||
| 85 | restart_instance redis 5 | ||
| 86 | } | ||
| 87 | |||
| 88 | test "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 | |||
| 3 | source "../tests/includes/init-tests.tcl" | ||
| 4 | |||
| 5 | test "Create a 5 nodes cluster" { | ||
| 6 | create_cluster 5 5 | ||
| 7 | } | ||
| 8 | |||
| 9 | proc 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 | |||
| 34 | test "Test publishing to master" { | ||
| 35 | test_cluster_publish 0 10 | ||
| 36 | } | ||
| 37 | |||
| 38 | test "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 | |||
| 3 | source "../tests/includes/init-tests.tcl" | ||
| 4 | |||
| 5 | test "Create a 5 nodes cluster" { | ||
| 6 | create_cluster 5 5 | ||
| 7 | } | ||
| 8 | |||
| 9 | test "Cluster is up" { | ||
| 10 | assert_cluster_state ok | ||
| 11 | } | ||
| 12 | |||
| 13 | test "Cluster is writable" { | ||
| 14 | cluster_write_test 0 | ||
| 15 | } | ||
| 16 | |||
| 17 | test "Instance #5 is a slave" { | ||
| 18 | assert {[RI 5 role] eq {slave}} | ||
| 19 | } | ||
| 20 | |||
| 21 | test "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 | |||
| 29 | set current_epoch [CI 1 cluster_current_epoch] | ||
| 30 | |||
| 31 | set numkeys 50000 | ||
| 32 | set numops 10000 | ||
| 33 | set cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]] | ||
| 34 | catch {unset content} | ||
| 35 | array set content {} | ||
| 36 | |||
| 37 | test "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 | |||
| 62 | test "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 | |||
| 70 | test "Cluster should eventually be up again" { | ||
| 71 | assert_cluster_state ok | ||
| 72 | } | ||
| 73 | |||
| 74 | test "Cluster is writable" { | ||
| 75 | cluster_write_test 1 | ||
| 76 | } | ||
| 77 | |||
| 78 | test "Instance #5 is now a master" { | ||
| 79 | assert {[RI 5 role] eq {master}} | ||
| 80 | } | ||
| 81 | |||
| 82 | test "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 | |||
| 89 | test "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 | |||
| 99 | source "../tests/includes/init-tests.tcl" | ||
| 100 | |||
| 101 | test "Create a 5 nodes cluster" { | ||
| 102 | create_cluster 5 5 | ||
| 103 | } | ||
| 104 | |||
| 105 | test "Cluster is up" { | ||
| 106 | assert_cluster_state ok | ||
| 107 | } | ||
| 108 | |||
| 109 | test "Cluster is writable" { | ||
| 110 | cluster_write_test 0 | ||
| 111 | } | ||
| 112 | |||
| 113 | test "Instance #5 is a slave" { | ||
| 114 | assert {[RI 5 role] eq {slave}} | ||
| 115 | } | ||
| 116 | |||
| 117 | test "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 | |||
| 125 | test "Make instance #0 unreachable without killing it" { | ||
| 126 | R 0 deferred 1 | ||
| 127 | R 0 DEBUG SLEEP 10 | ||
| 128 | } | ||
| 129 | |||
| 130 | test "Send CLUSTER FAILOVER to instance #5" { | ||
| 131 | R 5 cluster failover | ||
| 132 | } | ||
| 133 | |||
| 134 | test "Instance #5 is still a slave after some time (no failover)" { | ||
| 135 | after 5000 | ||
| 136 | assert {[RI 5 role] eq {master}} | ||
| 137 | } | ||
| 138 | |||
| 139 | test "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 | |||
| 146 | source "../tests/includes/init-tests.tcl" | ||
| 147 | |||
| 148 | test "Create a 5 nodes cluster" { | ||
| 149 | create_cluster 5 5 | ||
| 150 | } | ||
| 151 | |||
| 152 | test "Cluster is up" { | ||
| 153 | assert_cluster_state ok | ||
| 154 | } | ||
| 155 | |||
| 156 | test "Cluster is writable" { | ||
| 157 | cluster_write_test 0 | ||
| 158 | } | ||
| 159 | |||
| 160 | test "Instance #5 is a slave" { | ||
| 161 | assert {[RI 5 role] eq {slave}} | ||
| 162 | } | ||
| 163 | |||
| 164 | test "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 | |||
| 172 | test "Make instance #0 unreachable without killing it" { | ||
| 173 | R 0 deferred 1 | ||
| 174 | R 0 DEBUG SLEEP 10 | ||
| 175 | } | ||
| 176 | |||
| 177 | test "Send CLUSTER FAILOVER to instance #5" { | ||
| 178 | R 5 cluster failover force | ||
| 179 | } | ||
| 180 | |||
| 181 | test "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 | |||
| 189 | test "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 | |||
| 3 | source "../tests/includes/init-tests.tcl" | ||
| 4 | |||
| 5 | test "Create a 5 nodes cluster" { | ||
| 6 | create_cluster 5 5 | ||
| 7 | } | ||
| 8 | |||
| 9 | test "Cluster is up" { | ||
| 10 | assert_cluster_state ok | ||
| 11 | } | ||
| 12 | |||
| 13 | test "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. | ||
| 20 | set replica_ids { 5 6 7 } | ||
| 21 | foreach id $replica_ids { | ||
| 22 | R $id config set cluster-replica-no-failover yes | ||
| 23 | } | ||
| 24 | |||
| 25 | test "Killing majority of master nodes" { | ||
| 26 | kill_instance redis 0 | ||
| 27 | kill_instance redis 1 | ||
| 28 | kill_instance redis 2 | ||
| 29 | } | ||
| 30 | |||
| 31 | foreach id $replica_ids { | ||
| 32 | R $id config set cluster-replica-no-failover no | ||
| 33 | } | ||
| 34 | |||
| 35 | test "Cluster should eventually be down" { | ||
| 36 | assert_cluster_state fail | ||
| 37 | } | ||
| 38 | |||
| 39 | test "Use takeover to bring slaves back" { | ||
| 40 | foreach id $replica_ids { | ||
| 41 | R $id cluster failover takeover | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | test "Cluster should eventually be up again" { | ||
| 46 | assert_cluster_state ok | ||
| 47 | } | ||
| 48 | |||
| 49 | test "Cluster is writable" { | ||
| 50 | cluster_write_test 4 | ||
| 51 | } | ||
| 52 | |||
| 53 | test "Instance #5, #6, #7 are now masters" { | ||
| 54 | foreach id $replica_ids { | ||
| 55 | assert {[RI $id role] eq {master}} | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | test "Restarting the previously killed master nodes" { | ||
| 60 | restart_instance redis 0 | ||
| 61 | restart_instance redis 1 | ||
| 62 | restart_instance redis 2 | ||
| 63 | } | ||
| 64 | |||
| 65 | test "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 | |||
| 7 | source "../tests/includes/init-tests.tcl" | ||
| 8 | source "../../../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. | ||
| 12 | test "Create a 5 nodes cluster" { | ||
| 13 | cluster_create_with_continuous_slots 5 15 | ||
| 14 | } | ||
| 15 | |||
| 16 | test "Cluster is up" { | ||
| 17 | assert_cluster_state ok | ||
| 18 | } | ||
| 19 | |||
| 20 | test "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 | |||
| 32 | test "Set allow-replica-migration yes" { | ||
| 33 | foreach_redis_id id { | ||
| 34 | R $id CONFIG SET cluster-allow-replica-migration yes | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | set master0_id [dict get [get_myself 0] id] | ||
| 39 | test "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 | |||
| 48 | test "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 | |||
| 57 | test "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 | |||
| 69 | test "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 | |||
| 6 | source "../tests/includes/init-tests.tcl" | ||
| 7 | source "../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. | ||
| 11 | test "Create a 5 nodes cluster" { | ||
| 12 | cluster_create_with_continuous_slots 5 15 | ||
| 13 | } | ||
| 14 | |||
| 15 | test "Cluster is up" { | ||
| 16 | assert_cluster_state ok | ||
| 17 | } | ||
| 18 | |||
| 19 | test "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 | |||
| 31 | test "Set allow-replica-migration no" { | ||
| 32 | foreach_redis_id id { | ||
| 33 | R $id CONFIG SET cluster-allow-replica-migration no | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | set master0_id [dict get [get_myself 0] id] | ||
| 38 | test "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 | |||
| 46 | test "Wait cluster to be stable" { | ||
| 47 | wait_cluster_stable | ||
| 48 | } | ||
| 49 | |||
| 50 | test "Master #0 still should have its replicas" { | ||
| 51 | assert { [llength [lindex [R 0 role] 2]] >= 2 } | ||
| 52 | } | ||
| 53 | |||
| 54 | test "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 | |||
| 3 | source "../tests/includes/init-tests.tcl" | ||
| 4 | |||
| 5 | test "Create a 5 nodes cluster" { | ||
| 6 | create_cluster 5 5 | ||
| 7 | } | ||
| 8 | |||
| 9 | test "Cluster is up" { | ||
| 10 | assert_cluster_state ok | ||
| 11 | } | ||
| 12 | |||
| 13 | test "Cluster is writable" { | ||
| 14 | cluster_write_test 0 | ||
| 15 | } | ||
| 16 | |||
| 17 | test "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 | |||
| 24 | test "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 | |||
| 32 | test "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 | |||
| 44 | set current_epoch [CI 1 cluster_current_epoch] | ||
| 45 | |||
| 46 | test "Killing one master node" { | ||
| 47 | kill_instance redis 0 | ||
| 48 | } | ||
| 49 | |||
| 50 | test "Cluster should be still down after some time" { | ||
| 51 | after 10000 | ||
| 52 | assert_cluster_state fail | ||
| 53 | } | ||
| 54 | |||
| 55 | test "Instance #5 is still a slave" { | ||
| 56 | assert {[RI 5 role] eq {slave}} | ||
| 57 | } | ||
| 58 | |||
| 59 | test "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 @@ | |||
| 1 | source "../tests/includes/init-tests.tcl" | ||
| 2 | source "../../../tests/support/cli.tcl" | ||
| 3 | |||
| 4 | test "Create a 5 nodes cluster" { | ||
| 5 | create_cluster 5 5 | ||
| 6 | } | ||
| 7 | |||
| 8 | test "Cluster should start ok" { | ||
| 9 | assert_cluster_state ok | ||
| 10 | } | ||
| 11 | |||
| 12 | test "Cluster is writable" { | ||
| 13 | cluster_write_test 0 | ||
| 14 | } | ||
| 15 | |||
| 16 | proc 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 | |||
| 27 | proc 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 | |||
| 46 | proc 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 | ||
| 59 | proc 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 | |||
| 140 | test_slave_load_expired_keys no | ||
| 141 | test_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 @@ | |||
| 1 | source "../tests/includes/init-tests.tcl" | ||
| 2 | |||
| 3 | proc 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 | |||
| 15 | proc 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 | |||
| 23 | test "Create a 5 nodes cluster" { | ||
| 24 | create_cluster_with_mixedSlot 5 15 | ||
| 25 | } | ||
| 26 | |||
| 27 | test "Cluster is up" { | ||
| 28 | assert_cluster_state ok | ||
| 29 | } | ||
| 30 | |||
| 31 | test "Cluster is writable" { | ||
| 32 | cluster_write_test 0 | ||
| 33 | } | ||
| 34 | |||
| 35 | test "Instance #5 is a slave" { | ||
| 36 | assert {[RI 5 role] eq {slave}} | ||
| 37 | } | ||
| 38 | |||
| 39 | test "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 | |||
| 46 | test "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 | |||
| 52 | test "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 | |||
| 63 | test "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 | |||
| 90 | test "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 | |||
| 107 | proc 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 | |||
| 144 | if {$::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 | |||
| 3 | source "../tests/includes/init-tests.tcl" | ||
| 4 | |||
| 5 | test "Create a primary with a replica" { | ||
| 6 | create_cluster 1 1 | ||
| 7 | } | ||
| 8 | |||
| 9 | test "Cluster should start ok" { | ||
| 10 | assert_cluster_state ok | ||
| 11 | } | ||
| 12 | |||
| 13 | set primary [Rn 0] | ||
| 14 | set replica [Rn 1] | ||
| 15 | |||
| 16 | test "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 | |||
| 23 | test "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 | |||
| 29 | test "Can read from replica after READONLY" { | ||
| 30 | $replica READONLY | ||
| 31 | assert {[$replica GET a] eq {1}} | ||
| 32 | } | ||
| 33 | |||
| 34 | test "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 | |||
| 44 | test "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 | |||
| 52 | test "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 | |||
| 60 | test "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 | |||
| 79 | test "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 | |||
| 3 | source "../tests/includes/init-tests.tcl" | ||
| 4 | |||
| 5 | test "Create a primary with a replica" { | ||
| 6 | create_cluster 1 1 | ||
| 7 | } | ||
| 8 | |||
| 9 | test "Cluster should start ok" { | ||
| 10 | assert_cluster_state ok | ||
| 11 | } | ||
| 12 | |||
| 13 | test "Cluster is writable" { | ||
| 14 | cluster_write_test 0 | ||
| 15 | } | ||
| 16 | |||
| 17 | test "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 | |||
| 3 | source "../tests/includes/init-tests.tcl" | ||
| 4 | |||
| 5 | test "Create a primary with a replica" { | ||
| 6 | create_cluster 2 0 | ||
| 7 | } | ||
| 8 | |||
| 9 | test "Cluster should start ok" { | ||
| 10 | assert_cluster_state ok | ||
| 11 | } | ||
| 12 | |||
| 13 | set primary1 [Rn 0] | ||
| 14 | set primary2 [Rn 1] | ||
| 15 | |||
| 16 | proc cmdstat {instance cmd} { | ||
| 17 | return [cmdrstat $cmd $instance] | ||
| 18 | } | ||
| 19 | |||
| 20 | proc errorstat {instance cmd} { | ||
| 21 | return [errorrstat $cmd $instance] | ||
| 22 | } | ||
| 23 | |||
| 24 | test "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 | |||
| 3 | source "../tests/includes/init-tests.tcl" | ||
| 4 | |||
| 5 | test "Create a 2 nodes cluster" { | ||
| 6 | cluster_create_with_continuous_slots 2 2 | ||
| 7 | } | ||
| 8 | |||
| 9 | test "Cluster should start ok" { | ||
| 10 | assert_cluster_state ok | ||
| 11 | } | ||
| 12 | |||
| 13 | set master1 [Rn 0] | ||
| 14 | set master2 [Rn 1] | ||
| 15 | |||
| 16 | test "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 | |||
| 32 | test "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 | |||
| 11 | if {false} { | ||
| 12 | source "../tests/includes/init-tests.tcl" | ||
| 13 | source "../tests/includes/utils.tcl" | ||
| 14 | |||
| 15 | test "Create a 2 nodes cluster" { | ||
| 16 | create_cluster 2 0 | ||
| 17 | config_set_all_nodes cluster-allow-replica-migration no | ||
| 18 | } | ||
| 19 | |||
| 20 | test "Cluster is up" { | ||
| 21 | assert_cluster_state ok | ||
| 22 | } | ||
| 23 | |||
| 24 | set cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]] | ||
| 25 | catch {unset nodefrom} | ||
| 26 | catch {unset nodeto} | ||
| 27 | |||
| 28 | proc 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 | |||
| 36 | reset_cluster | ||
| 37 | |||
| 38 | $cluster set aga xyz | ||
| 39 | |||
| 40 | test "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 | |||
| 46 | test "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 | |||
| 52 | test "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 | |||
| 62 | reset_cluster | ||
| 63 | |||
| 64 | test "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 | |||
| 73 | test "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 | |||
| 80 | reset_cluster | ||
| 81 | |||
| 82 | test "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 | |||
| 90 | test "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 | |||
| 97 | config_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 | |||
| 6 | if {false} { | ||
| 7 | |||
| 8 | source "../tests/includes/init-tests.tcl" | ||
| 9 | source "../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 | |||
| 15 | test "Create a 10 nodes cluster" { | ||
| 16 | create_cluster 10 0 | ||
| 17 | config_set_all_nodes cluster-allow-replica-migration no | ||
| 18 | } | ||
| 19 | |||
| 20 | test "Cluster is up" { | ||
| 21 | assert_cluster_state ok | ||
| 22 | } | ||
| 23 | |||
| 24 | set cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]] | ||
| 25 | catch {unset nodefrom} | ||
| 26 | catch {unset nodeto} | ||
| 27 | |||
| 28 | $cluster refresh_nodes_map | ||
| 29 | |||
| 30 | test "Set many keys" { | ||
| 31 | for {set i 0} {$i < 40000} {incr i} { | ||
| 32 | $cluster set key:$i val:$i | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | test "Keys are accessible" { | ||
| 37 | for {set i 0} {$i < 40000} {incr i} { | ||
| 38 | assert { [$cluster get key:$i] eq "val:$i" } | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | test "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 | |||
| 52 | test "Fix cluster" { | ||
| 53 | wait_for_cluster_propagation | ||
| 54 | fix_cluster $nodefrom(addr) | ||
| 55 | } | ||
| 56 | |||
| 57 | test "Keys are accessible" { | ||
| 58 | for {set i 0} {$i < 40000} {incr i} { | ||
| 59 | assert { [$cluster get key:$i] eq "val:$i" } | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | config_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 @@ | |||
| 1 | source "../tests/includes/init-tests.tcl" | ||
| 2 | |||
| 3 | test "Create a 1 node cluster" { | ||
| 4 | create_cluster 1 0 | ||
| 5 | } | ||
| 6 | |||
| 7 | test "Cluster is up" { | ||
| 8 | assert_cluster_state ok | ||
| 9 | } | ||
| 10 | |||
| 11 | test "Cluster is writable" { | ||
| 12 | cluster_write_test 0 | ||
| 13 | } | ||
| 14 | |||
| 15 | proc 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 | |||
| 22 | proc 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 | |||
| 28 | proc 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 | |||
| 38 | set master_id 0 | ||
| 39 | |||
| 40 | test "Fill up primary with data" { | ||
| 41 | # Set 1 MB of data | ||
| 42 | R $master_id debug populate 1000 key 1000 | ||
| 43 | } | ||
| 44 | |||
| 45 | test "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 | |||
| 51 | test "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 | |||
| 67 | test "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 | |||
| 121 | test "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 @@ | |||
| 1 | source "../tests/includes/init-tests.tcl" | ||
| 2 | |||
| 3 | test "Create a 3 nodes cluster" { | ||
| 4 | cluster_create_with_continuous_slots 3 3 | ||
| 5 | } | ||
| 6 | |||
| 7 | test "Cluster is up" { | ||
| 8 | assert_cluster_state ok | ||
| 9 | } | ||
| 10 | |||
| 11 | set cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]] | ||
| 12 | |||
| 13 | proc 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 | |||
| 25 | test "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 | |||
| 58 | test "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 | |||
| 104 | test "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 | |||
| 140 | test "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 | |||
| 171 | test "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 | |||
| 192 | test "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 | |||
| 3 | source "../tests/includes/init-tests.tcl" | ||
| 4 | |||
| 5 | test "Create a 3 nodes cluster" { | ||
| 6 | cluster_create_with_continuous_slots 3 3 | ||
| 7 | } | ||
| 8 | |||
| 9 | set cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]] | ||
| 10 | test "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 | |||
| 46 | test "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 | |||
| 51 | test "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 | |||
| 59 | test "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 | |||
| 75 | test "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 | |||
| 111 | test "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 @@ | |||
| 1 | source "../tests/includes/init-tests.tcl" | ||
| 2 | |||
| 3 | # Initial slot distribution. | ||
| 4 | set ::slot0 [list 0 1000 1002 5459 5461 5461 10926 10926] | ||
| 5 | set ::slot1 [list 5460 5460 5462 10922 10925 10925] | ||
| 6 | set ::slot2 [list 10923 10924 10927 16383] | ||
| 7 | set ::slot3 [list 1001 1001] | ||
| 8 | |||
| 9 | proc 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" | ||
| 22 | proc 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 | |||
| 42 | proc 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 | |||
| 53 | test "Create a 8 nodes cluster with 4 shards" { | ||
| 54 | cluster_create_with_split_slots 4 4 | ||
| 55 | } | ||
| 56 | |||
| 57 | test "Cluster should start ok" { | ||
| 58 | assert_cluster_state ok | ||
| 59 | } | ||
| 60 | |||
| 61 | test "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 | |||
| 70 | test "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 | |||
| 104 | test "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 | |||
| 111 | set node_0_id [R 0 CLUSTER MYID] | ||
| 112 | |||
| 113 | test "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. | ||
| 119 | test "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 | |||
| 127 | test "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 | |||
| 131 | set primary_id 4 | ||
| 132 | set replica_id 0 | ||
| 133 | |||
| 134 | test "Restarting primary node" { | ||
| 135 | restart_instance redis $replica_id | ||
| 136 | } | ||
| 137 | |||
| 138 | test "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 | |||
| 147 | test "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 | |||
| 207 | test "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 | |||
| 220 | test "Cluster is up" { | ||
| 221 | assert_cluster_state ok | ||
| 222 | } | ||
| 223 | test "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 | |||
| 232 | test "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 | |||
| 239 | test "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 | |||
| 252 | test "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 | |||
| 272 | test "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 | |||
| 3 | source "../tests/includes/init-tests.tcl" | ||
| 4 | source "../tests/includes/utils.tcl" | ||
| 5 | |||
| 6 | test "Create a 2 nodes cluster" { | ||
| 7 | create_cluster 2 0 | ||
| 8 | config_set_all_nodes cluster-allow-replica-migration no | ||
| 9 | } | ||
| 10 | |||
| 11 | test "Cluster is up" { | ||
| 12 | assert_cluster_state ok | ||
| 13 | } | ||
| 14 | |||
| 15 | set cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]] | ||
| 16 | catch {unset nodefrom} | ||
| 17 | catch {unset nodeto} | ||
| 18 | |||
| 19 | $cluster refresh_nodes_map | ||
| 20 | |||
| 21 | test "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 | |||
| 28 | test "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 | |||
| 50 | config_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 | |||
| 6 | fconfigure stdin -buffering none | ||
| 7 | |||
| 8 | while 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 | |||
| 3 | test "(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 | |||
| 15 | test "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 | |||
| 28 | test "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. | ||
| 64 | proc 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 | |||
| 87 | test "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 | |||
| 100 | test "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 @@ | |||
| 1 | source "../../../tests/support/cli.tcl" | ||
| 2 | |||
| 3 | proc config_set_all_nodes {keyword value} { | ||
| 4 | foreach_redis_id id { | ||
| 5 | R $id config set $keyword $value | ||
| 6 | } | ||
| 7 | } | ||
| 8 | |||
| 9 | proc 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 | |||
| 27 | proc 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 @@ | |||
| 1 | redis_* | ||
| 2 | sentinel_* | ||
