1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
|
# This module can be configure with multiple options given as flags on module load time
# 0 - not aux fields will be declared (this is the default)
# 1 << 0 - use aux_save2 api
# 1 << 1 - call aux callback before key space
# 1 << 2 - call aux callback after key space
# 1 << 3 - do not save data on aux callback
set testmodule [file normalize tests/modules/testrdb.so]
tags "modules" {
test {modules are able to persist types} {
start_server [list overrides [list loadmodule "$testmodule"]] {
r testrdb.set.key key1 value1
assert_equal "value1" [r testrdb.get.key key1]
r debug reload
assert_equal "value1" [r testrdb.get.key key1]
}
}
test {modules global are lost without aux} {
set server_path [tmpdir "server.module-testrdb"]
start_server [list overrides [list loadmodule "$testmodule" "dir" $server_path] keep_persistence true] {
r testrdb.set.before global1
assert_equal "global1" [r testrdb.get.before]
}
start_server [list overrides [list loadmodule "$testmodule" "dir" $server_path]] {
assert_equal "" [r testrdb.get.before]
}
}
test {aux that saves no data are not saved to the rdb when aux_save2 is used} {
set server_path [tmpdir "server.module-testrdb"]
puts $server_path
# 15 == 1111 - use aux_save2 before and after key space without data
start_server [list overrides [list loadmodule "$testmodule 15" "dir" $server_path] keep_persistence true] {
r set x 1
r save
}
start_server [list overrides [list "dir" $server_path] keep_persistence true] {
# make sure server started successfully without the module.
assert_equal {1} [r get x]
}
}
test {aux that saves no data are saved to the rdb when aux_save is used} {
set server_path [tmpdir "server.module-testrdb"]
puts $server_path
# 14 == 1110 - use aux_save before and after key space without data
start_server [list overrides [list loadmodule "$testmodule 14" "dir" $server_path] keep_persistence true] {
r set x 1
r save
}
start_server [list overrides [list loadmodule "$testmodule 14" "dir" $server_path] keep_persistence true] {
# make sure server started successfully and aux_save was called twice.
assert_equal {1} [r get x]
assert_equal {2} [r testrdb.get.n_aux_load_called]
}
}
foreach test_case {6 7} {
# 6 == 0110 - use aux_save before and after key space with data
# 7 == 0111 - use aux_save2 before and after key space with data
test {modules are able to persist globals before and after} {
set server_path [tmpdir "server.module-testrdb"]
start_server [list overrides [list loadmodule "$testmodule $test_case" "dir" $server_path "save" "900 1"] keep_persistence true] {
r testrdb.set.before global1
r testrdb.set.after global2
assert_equal "global1" [r testrdb.get.before]
assert_equal "global2" [r testrdb.get.after]
}
start_server [list overrides [list loadmodule "$testmodule $test_case" "dir" $server_path "save" "900 1"]] {
assert_equal "global1" [r testrdb.get.before]
assert_equal "global2" [r testrdb.get.after]
}
}
}
foreach test_case {4 5} {
# 4 == 0100 - use aux_save after key space with data
# 5 == 0101 - use aux_save2 after key space with data
test {modules are able to persist globals just after} {
set server_path [tmpdir "server.module-testrdb"]
start_server [list overrides [list loadmodule "$testmodule $test_case" "dir" $server_path "save" "900 1"] keep_persistence true] {
r testrdb.set.after global2
assert_equal "global2" [r testrdb.get.after]
}
start_server [list overrides [list loadmodule "$testmodule $test_case" "dir" $server_path "save" "900 1"]] {
assert_equal "global2" [r testrdb.get.after]
}
}
}
test {Verify module options info} {
start_server [list overrides [list loadmodule "$testmodule"]] {
assert_match "*\[handle-io-errors|handle-repl-async-load\]*" [r info modules]
}
}
tags {repl} {
test {diskless loading short read with module} {
start_server [list overrides [list loadmodule "$testmodule"]] {
set replica [srv 0 client]
set replica_host [srv 0 host]
set replica_port [srv 0 port]
start_server [list overrides [list loadmodule "$testmodule"]] {
set master [srv 0 client]
set master_host [srv 0 host]
set master_port [srv 0 port]
# Set master and replica to use diskless replication
$master config set repl-diskless-sync yes
$master config set rdbcompression no
$replica config set repl-diskless-load swapdb
$master config set hz 500
$replica config set hz 500
$master config set dynamic-hz no
$replica config set dynamic-hz no
set start [clock clicks -milliseconds]
for {set k 0} {$k < 30} {incr k} {
r testrdb.set.key key$k [string repeat A [expr {int(rand()*1000000)}]]
}
if {$::verbose} {
set end [clock clicks -milliseconds]
set duration [expr $end - $start]
puts "filling took $duration ms (TODO: use pipeline)"
set start [clock clicks -milliseconds]
}
# Start the replication process...
set loglines [count_log_lines -1]
$master config set repl-diskless-sync-delay 0
$replica replicaof $master_host $master_port
# kill the replication at various points
set attempts 100
if {$::accurate} { set attempts 500 }
for {set i 0} {$i < $attempts} {incr i} {
# wait for the replica to start reading the rdb
# using the log file since the replica only responds to INFO once in 2mb
set res [wait_for_log_messages -1 {"*Loading DB in memory*"} $loglines 2000 1]
set loglines [lindex $res 1]
# add some additional random sleep so that we kill the master on a different place each time
after [expr {int(rand()*50)}]
# kill the replica connection on the master
set killed [$master client kill type replica]
set res [wait_for_log_messages -1 {"*Internal error in RDB*" "*Finished with success*" "*Successful partial resynchronization*"} $loglines 500 10]
if {$::verbose} { puts $res }
set log_text [lindex $res 0]
set loglines [lindex $res 1]
if {![string match "*Internal error in RDB*" $log_text]} {
# force the replica to try another full sync
$master multi
$master client kill type replica
$master set asdf asdf
# fill replication backlog with new content
$master config set repl-backlog-size 16384
for {set keyid 0} {$keyid < 10} {incr keyid} {
$master set "$keyid string_$keyid" [string repeat A 16384]
}
$master exec
}
# wait for loading to stop (fail)
# After a loading successfully, next loop will enter `async_loading`
wait_for_condition 1000 1 {
[s -1 async_loading] eq 0 &&
[s -1 loading] eq 0
} else {
fail "Replica didn't disconnect"
}
}
if {$::verbose} {
set end [clock clicks -milliseconds]
set duration [expr $end - $start]
puts "test took $duration ms"
}
# enable fast shutdown
$master config set rdb-key-save-delay 0
}
}
}
# Module events for diskless load swapdb when async_loading (matching master replid)
foreach test_case {6 7} {
# 6 == 0110 - use aux_save before and after key space with data
# 7 == 0111 - use aux_save2 before and after key space with data
foreach testType {Successful Aborted} {
start_server [list overrides [list loadmodule "$testmodule $test_case"] tags [list external:skip]] {
set replica [srv 0 client]
set replica_host [srv 0 host]
set replica_port [srv 0 port]
set replica_log [srv 0 stdout]
start_server [list overrides [list loadmodule "$testmodule $test_case"]] {
set master [srv 0 client]
set master_host [srv 0 host]
set master_port [srv 0 port]
set start [clock clicks -milliseconds]
# Set master and replica to use diskless replication on swapdb mode
$master config set repl-diskless-sync yes
$master config set repl-diskless-sync-delay 0
$master config set save ""
$replica config set repl-diskless-load swapdb
$replica config set save ""
# Initial sync to have matching replids between master and replica
$replica replicaof $master_host $master_port
# Let replica finish initial sync with master
wait_for_condition 100 100 {
[s -1 master_link_status] eq "up"
} else {
fail "Master <-> Replica didn't finish sync"
}
# Set global values on module so we can check if module event callbacks will pick it up correctly
$master testrdb.set.before value1_master
$replica testrdb.set.before value1_replica
# Put different data sets on the master and replica
# We need to put large keys on the master since the replica replies to info only once in 2mb
$replica debug populate 200 slave 10
$master debug populate 1000 master 100000
$master config set rdbcompression no
# Force the replica to try another full sync (this time it will have matching master replid)
$master multi
$master client kill type replica
# Fill replication backlog with new content
$master config set repl-backlog-size 16384
for {set keyid 0} {$keyid < 10} {incr keyid} {
$master set "$keyid string_$keyid" [string repeat A 16384]
}
$master exec
switch $testType {
"Aborted" {
# Set master with a slow rdb generation, so that we can easily intercept loading
# 10ms per key, with 1000 keys is 10 seconds
$master config set rdb-key-save-delay 10000
test {Diskless load swapdb RedisModuleEvent_ReplAsyncLoad handling: during loading, can keep module variable same as before} {
# Wait for the replica to start reading the rdb and module for acknowledgement
# We wanna abort only after the temp db was populated by REDISMODULE_AUX_BEFORE_RDB
wait_for_condition 100 100 {
[s -1 async_loading] eq 1 && [$replica testrdb.async_loading.get.before] eq "value1_master"
} else {
fail "Module didn't receive or react to REDISMODULE_SUBEVENT_REPL_ASYNC_LOAD_STARTED"
}
assert_equal [$replica dbsize] 200
assert_equal value1_replica [$replica testrdb.get.before]
}
# Make sure that next sync will not start immediately so that we can catch the replica in between syncs
$master config set repl-diskless-sync-delay 5
# Kill the replica connection on the master
set killed [$master client kill type replica]
test {Diskless load swapdb RedisModuleEvent_ReplAsyncLoad handling: when loading aborted, can keep module variable same as before} {
# Wait for loading to stop (fail) and module for acknowledgement
wait_for_condition 100 100 {
[s -1 async_loading] eq 0 && [$replica testrdb.async_loading.get.before] eq ""
} else {
fail "Module didn't receive or react to REDISMODULE_SUBEVENT_REPL_ASYNC_LOAD_ABORTED"
}
assert_equal [$replica dbsize] 200
assert_equal value1_replica [$replica testrdb.get.before]
}
# Speed up shutdown
$master config set rdb-key-save-delay 0
}
"Successful" {
# Let replica finish sync with master
wait_for_condition 100 100 {
[s -1 master_link_status] eq "up"
} else {
fail "Master <-> Replica didn't finish sync"
}
test {Diskless load swapdb RedisModuleEvent_ReplAsyncLoad handling: after db loaded, can set module variable with new value} {
assert_equal [$replica dbsize] 1010
assert_equal value1_master [$replica testrdb.get.before]
}
}
}
if {$::verbose} {
set end [clock clicks -milliseconds]
set duration [expr $end - $start]
puts "test took $duration ms"
}
}
}
}
}
}
}
|