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