failover.tcl 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. start_server {tags {"failover external:skip"}} {
  2. start_server {} {
  3. start_server {} {
  4. set node_0 [srv 0 client]
  5. set node_0_host [srv 0 host]
  6. set node_0_port [srv 0 port]
  7. set node_0_pid [srv 0 pid]
  8. set node_1 [srv -1 client]
  9. set node_1_host [srv -1 host]
  10. set node_1_port [srv -1 port]
  11. set node_1_pid [srv -1 pid]
  12. set node_2 [srv -2 client]
  13. set node_2_host [srv -2 host]
  14. set node_2_port [srv -2 port]
  15. set node_2_pid [srv -2 pid]
  16. proc assert_digests_match {n1 n2 n3} {
  17. assert_equal [$n1 debug digest] [$n2 debug digest]
  18. assert_equal [$n2 debug digest] [$n3 debug digest]
  19. }
  20. test {failover command fails without connected replica} {
  21. catch { $node_0 failover to $node_1_host $node_1_port } err
  22. if {! [string match "ERR*" $err]} {
  23. fail "failover command succeeded when replica not connected"
  24. }
  25. }
  26. test {setup replication for following tests} {
  27. $node_1 replicaof $node_0_host $node_0_port
  28. $node_2 replicaof $node_0_host $node_0_port
  29. wait_for_sync $node_1
  30. wait_for_sync $node_2
  31. }
  32. test {failover command fails with invalid host} {
  33. catch { $node_0 failover to invalidhost $node_1_port } err
  34. assert_match "ERR*" $err
  35. }
  36. test {failover command fails with invalid port} {
  37. catch { $node_0 failover to $node_1_host invalidport } err
  38. assert_match "ERR*" $err
  39. }
  40. test {failover command fails with just force and timeout} {
  41. catch { $node_0 FAILOVER FORCE TIMEOUT 100} err
  42. assert_match "ERR*" $err
  43. }
  44. test {failover command fails when sent to a replica} {
  45. catch { $node_1 failover to $node_1_host $node_1_port } err
  46. assert_match "ERR*" $err
  47. }
  48. test {failover command fails with force without timeout} {
  49. catch { $node_0 failover to $node_1_host $node_1_port FORCE } err
  50. assert_match "ERR*" $err
  51. }
  52. test {failover command to specific replica works} {
  53. set initial_psyncs [s -1 sync_partial_ok]
  54. set initial_syncs [s -1 sync_full]
  55. # Generate a delta between primary and replica
  56. set load_handler [start_write_load $node_0_host $node_0_port 5]
  57. exec kill -SIGSTOP [srv -1 pid]
  58. wait_for_condition 50 100 {
  59. [s 0 total_commands_processed] > 100
  60. } else {
  61. fail "Node 0 did not accept writes"
  62. }
  63. exec kill -SIGCONT [srv -1 pid]
  64. # Execute the failover
  65. $node_0 failover to $node_1_host $node_1_port
  66. # Wait for failover to end
  67. wait_for_condition 50 100 {
  68. [s 0 master_failover_state] == "no-failover"
  69. } else {
  70. fail "Failover from node 0 to node 1 did not finish"
  71. }
  72. # stop the write load and make sure no more commands processed
  73. stop_write_load $load_handler
  74. wait_load_handlers_disconnected
  75. $node_2 replicaof $node_1_host $node_1_port
  76. wait_for_sync $node_0
  77. wait_for_sync $node_2
  78. assert_match *slave* [$node_0 role]
  79. assert_match *master* [$node_1 role]
  80. assert_match *slave* [$node_2 role]
  81. # We should accept psyncs from both nodes
  82. assert_equal [expr [s -1 sync_partial_ok] - $initial_psyncs] 2
  83. assert_equal [expr [s -1 sync_full] - $initial_psyncs] 0
  84. assert_digests_match $node_0 $node_1 $node_2
  85. }
  86. test {failover command to any replica works} {
  87. set initial_psyncs [s -2 sync_partial_ok]
  88. set initial_syncs [s -2 sync_full]
  89. wait_for_ofs_sync $node_1 $node_2
  90. # We stop node 0 to and make sure node 2 is selected
  91. exec kill -SIGSTOP $node_0_pid
  92. $node_1 set CASE 1
  93. $node_1 FAILOVER
  94. # Wait for failover to end
  95. wait_for_condition 50 100 {
  96. [s -1 master_failover_state] == "no-failover"
  97. } else {
  98. fail "Failover from node 1 to node 2 did not finish"
  99. }
  100. exec kill -SIGCONT $node_0_pid
  101. $node_0 replicaof $node_2_host $node_2_port
  102. wait_for_sync $node_0
  103. wait_for_sync $node_1
  104. assert_match *slave* [$node_0 role]
  105. assert_match *slave* [$node_1 role]
  106. assert_match *master* [$node_2 role]
  107. # We should accept Psyncs from both nodes
  108. assert_equal [expr [s -2 sync_partial_ok] - $initial_psyncs] 2
  109. assert_equal [expr [s -1 sync_full] - $initial_psyncs] 0
  110. assert_digests_match $node_0 $node_1 $node_2
  111. }
  112. test {failover to a replica with force works} {
  113. set initial_psyncs [s 0 sync_partial_ok]
  114. set initial_syncs [s 0 sync_full]
  115. exec kill -SIGSTOP $node_0_pid
  116. # node 0 will never acknowledge this write
  117. $node_2 set case 2
  118. $node_2 failover to $node_0_host $node_0_port TIMEOUT 100 FORCE
  119. # Wait for node 0 to give up on sync attempt and start failover
  120. wait_for_condition 50 100 {
  121. [s -2 master_failover_state] == "failover-in-progress"
  122. } else {
  123. fail "Failover from node 2 to node 0 did not timeout"
  124. }
  125. # Quick check that everyone is a replica, we never want a
  126. # state where there are two masters.
  127. assert_match *slave* [$node_1 role]
  128. assert_match *slave* [$node_2 role]
  129. exec kill -SIGCONT $node_0_pid
  130. # Wait for failover to end
  131. wait_for_condition 50 100 {
  132. [s -2 master_failover_state] == "no-failover"
  133. } else {
  134. fail "Failover from node 2 to node 0 did not finish"
  135. }
  136. $node_1 replicaof $node_0_host $node_0_port
  137. wait_for_sync $node_1
  138. wait_for_sync $node_2
  139. assert_match *master* [$node_0 role]
  140. assert_match *slave* [$node_1 role]
  141. assert_match *slave* [$node_2 role]
  142. assert_equal [count_log_message -2 "time out exceeded, failing over."] 1
  143. # We should accept both psyncs, although this is the condition we might not
  144. # since we didn't catch up.
  145. assert_equal [expr [s 0 sync_partial_ok] - $initial_psyncs] 2
  146. assert_equal [expr [s 0 sync_full] - $initial_syncs] 0
  147. assert_digests_match $node_0 $node_1 $node_2
  148. }
  149. test {failover with timeout aborts if replica never catches up} {
  150. set initial_psyncs [s 0 sync_partial_ok]
  151. set initial_syncs [s 0 sync_full]
  152. # Stop replica so it never catches up
  153. exec kill -SIGSTOP [srv -1 pid]
  154. $node_0 SET CASE 1
  155. $node_0 failover to [srv -1 host] [srv -1 port] TIMEOUT 500
  156. # Wait for failover to end
  157. wait_for_condition 50 20 {
  158. [s 0 master_failover_state] == "no-failover"
  159. } else {
  160. fail "Failover from node_0 to replica did not finish"
  161. }
  162. exec kill -SIGCONT [srv -1 pid]
  163. # We need to make sure the nodes actually sync back up
  164. wait_for_ofs_sync $node_0 $node_1
  165. wait_for_ofs_sync $node_0 $node_2
  166. assert_match *master* [$node_0 role]
  167. assert_match *slave* [$node_1 role]
  168. assert_match *slave* [$node_2 role]
  169. # Since we never caught up, there should be no syncs
  170. assert_equal [expr [s 0 sync_partial_ok] - $initial_psyncs] 0
  171. assert_equal [expr [s 0 sync_full] - $initial_syncs] 0
  172. assert_digests_match $node_0 $node_1 $node_2
  173. }
  174. test {failovers can be aborted} {
  175. set initial_psyncs [s 0 sync_partial_ok]
  176. set initial_syncs [s 0 sync_full]
  177. # Stop replica so it never catches up
  178. exec kill -SIGSTOP [srv -1 pid]
  179. $node_0 SET CASE 2
  180. $node_0 failover to [srv -1 host] [srv -1 port] TIMEOUT 60000
  181. assert_match [s 0 master_failover_state] "waiting-for-sync"
  182. # Sanity check that read commands are still accepted
  183. $node_0 GET CASE
  184. $node_0 failover abort
  185. assert_match [s 0 master_failover_state] "no-failover"
  186. exec kill -SIGCONT [srv -1 pid]
  187. # Just make sure everything is still synced
  188. wait_for_ofs_sync $node_0 $node_1
  189. wait_for_ofs_sync $node_0 $node_2
  190. assert_match *master* [$node_0 role]
  191. assert_match *slave* [$node_1 role]
  192. assert_match *slave* [$node_2 role]
  193. # Since we never caught up, there should be no syncs
  194. assert_equal [expr [s 0 sync_partial_ok] - $initial_psyncs] 0
  195. assert_equal [expr [s 0 sync_full] - $initial_syncs] 0
  196. assert_digests_match $node_0 $node_1 $node_2
  197. }
  198. test {failover aborts if target rejects sync request} {
  199. set initial_psyncs [s 0 sync_partial_ok]
  200. set initial_syncs [s 0 sync_full]
  201. # We block psync, so the failover will fail
  202. $node_1 acl setuser default -psync
  203. # We pause the target long enough to send a write command
  204. # during the pause. This write will not be interrupted.
  205. exec kill -SIGSTOP [srv -1 pid]
  206. set rd [redis_deferring_client]
  207. $rd SET FOO BAR
  208. $node_0 failover to $node_1_host $node_1_port
  209. exec kill -SIGCONT [srv -1 pid]
  210. # Wait for failover to end
  211. wait_for_condition 50 100 {
  212. [s 0 master_failover_state] == "no-failover"
  213. } else {
  214. fail "Failover from node_0 to replica did not finish"
  215. }
  216. assert_equal [$rd read] "OK"
  217. $rd close
  218. # restore access to psync
  219. $node_1 acl setuser default +psync
  220. # We need to make sure the nodes actually sync back up
  221. wait_for_sync $node_1
  222. wait_for_sync $node_2
  223. assert_match *master* [$node_0 role]
  224. assert_match *slave* [$node_1 role]
  225. assert_match *slave* [$node_2 role]
  226. # We will cycle all of our replicas here and force a psync.
  227. assert_equal [expr [s 0 sync_partial_ok] - $initial_psyncs] 2
  228. assert_equal [expr [s 0 sync_full] - $initial_syncs] 0
  229. assert_equal [count_log_message 0 "Failover target rejected psync request"] 1
  230. assert_digests_match $node_0 $node_1 $node_2
  231. }
  232. }
  233. }
  234. }