1+ start_server {tags {" scan-consistency-on-failover external:skip" }} {
2+
3+ set fixed_seed " 00112233445566778899aabbccddeeff"
4+ set shared_overrides [list appendonly no save " " db-hash-seed $fixed_seed ]
5+
6+ start_server [list overrides $shared_overrides ] {
7+ set primary_host [srv 0 host]
8+ set primary_port [srv 0 port]
9+
10+ start_server [list overrides $shared_overrides ] {
11+ set replica_host [srv 0 host]
12+ set replica_port [srv 0 port]
13+
14+ set primary [srv -1 client]
15+ set replica [srv 0 client]
16+
17+ $primary flushall
18+ $replica replicaof $primary_host $primary_port
19+
20+ wait_for_sync $replica 200 50
21+
22+ set n 2000
23+ for {set i 0} {$i < $n } {incr i} {
24+ $primary set " k:$i " x
25+ }
26+ for {set i 0} {$i < $n } {incr i} {
27+ $primary sadd s " m:$i "
28+ $primary hset h " f:$i " v
29+ $primary zadd z $i " z:$i "
30+ }
31+
32+ wait_for_condition 200 50 {
33+ [$replica dbsize] == [$primary dbsize]
34+ } else {
35+ fail " replica did not catch up dbsize"
36+ }
37+
38+ # ensuring both instances have finished rehashing.
39+ wait_for_condition 200 50 {
40+ ![string match {Hash table 1 stats} [$primary debug htstats 0]] &&
41+ ![string match {Hash table 1 stats} [$replica debug htstats 0]]
42+ } else {
43+ fail " hash tables still rehashing"
44+ }
45+
46+ # SCAN across failover
47+ set cur 0
48+ set steps 0
49+ set seen {}
50+
51+ while {1} {
52+ set res [$primary scan $cur count 100]
53+ set cur [lindex $res 0]
54+ set keys [lindex $res 1]
55+ foreach k $keys {
56+ if {[string match " k:*" $k ]} {
57+ dict set seen $k 1
58+ }
59+ }
60+ incr steps
61+
62+ # After a few pages, promote the replica
63+ if {$steps == 5} {
64+ $replica replicaof no one
65+ wait_for_condition 100 50 {
66+ ([string match {*master*} [$replica info replication]])
67+ } else {
68+ fail " replica did not promote to master"
69+ }
70+ break
71+ }
72+ }
73+
74+ set resume_cur $cur
75+
76+ # Resume SCAN on the new primary using the SAME cursor.
77+ if {$resume_cur ne " 0" } {
78+ set res [$replica scan $resume_cur count 100]
79+ set cur [lindex $res 0]
80+ set keys [lindex $res 1]
81+ foreach k $keys {
82+ if {[string match " k:*" $k ]} {
83+ dict set seen $k 1
84+ }
85+ }
86+ }
87+
88+ # Perform a full SCAN sweep to ensure all keys are covered.
89+ set cur 0
90+ set failover_steps 0
91+ while {1} {
92+ set res [$replica scan $cur count 100]
93+ set cur [lindex $res 0]
94+ set keys [lindex $res 1]
95+ foreach k $keys {
96+ if {[string match " k:*" $k ]} {
97+ dict set seen $k 1
98+ }
99+ }
100+ incr failover_steps
101+ if {$cur eq " 0" } break
102+
103+ # safety guardrail incase scan doesn't end
104+ if {$failover_steps > 200} {
105+ fail " SCAN did not complete after failover"
106+ }
107+ }
108+
109+ set all_keys [$replica keys " k:*" ]
110+ assert_equal $n [llength $all_keys ]
111+ assert_equal [lsort $all_keys ] [lsort [dict keys $seen ]]
112+
113+ }
114+ }
115+ }
0 commit comments