cluster.tcl 4.2 KB

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