diff options
Diffstat (limited to 'tests')
58 files changed, 1191 insertions, 159 deletions
diff --git a/tests/assets/default.conf b/tests/assets/default.conf index 4ae420790..de460cc08 100644 --- a/tests/assets/default.conf +++ b/tests/assets/default.conf @@ -13,8 +13,9 @@ databases 16 latency-monitor-threshold 1 repl-diskless-sync-delay 0 +# Turn off RDB by default (to speedup tests) # Note the infrastructure in server.tcl uses a dict, we can't provide several save directives -save 900 1 +save '' rdbcompression yes dbfilename dump.rdb @@ -30,4 +31,7 @@ enable-protected-configs yes enable-debug-command yes enable-module-command yes -propagation-error-behavior panic
\ No newline at end of file +propagation-error-behavior panic + +# Make sure shutdown doesn't fail if there's an initial AOFRW +shutdown-on-sigterm force diff --git a/tests/assets/test_cli_hint_suite.txt b/tests/assets/test_cli_hint_suite.txt new file mode 100644 index 000000000..18c1fe07a --- /dev/null +++ b/tests/assets/test_cli_hint_suite.txt @@ -0,0 +1,111 @@ +# Test suite for redis-cli command-line hinting mechanism. +# Each test case consists of two strings: a (partial) input command line, and the expected hint string. + +# Command with one arg: GET key +"GET " "key" +"GET abc " "" + +# Command with two args: DECRBY key decrement +"DECRBY xyz 2 " "" +"DECRBY xyz " "decrement" +"DECRBY " "key decrement" + +# Command with optional arg: LPOP key [count] +"LPOP key " "[count]" +"LPOP key 3 " "" + +# Command with optional token arg: XRANGE key start end [COUNT count] +"XRANGE " "key start end [COUNT count]" +"XRANGE k 4 2 " "[COUNT count]" +"XRANGE k 4 2 COU" "[COUNT count]" +"XRANGE k 4 2 COUNT" "[COUNT count]" +"XRANGE k 4 2 COUNT " "count" + +# Command with optional token block arg: BITFIELD_RO key [GET encoding offset [GET encoding offset ...]] +"BITFIELD_RO k " "[GET encoding offset [GET encoding offset ...]]" +"BITFIELD_RO k GE" "[GET encoding offset [GET encoding offset ...]]" +"BITFIELD_RO k GET" "[GET encoding offset [GET encoding offset ...]]" +# TODO: The following hints end with an unbalanced "]" which shouldn't be there. +"BITFIELD_RO k GET " "encoding offset [GET encoding offset ...]]" +"BITFIELD_RO k GET xyz " "offset [GET encoding offset ...]]" +"BITFIELD_RO k GET xyz 12 " "[GET encoding offset ...]]" +"BITFIELD_RO k GET xyz 12 GET " "encoding offset [GET encoding offset ...]]" +"BITFIELD_RO k GET enc1 12 GET enc2 " "offset [GET encoding offset ...]]" +"BITFIELD_RO k GET enc1 12 GET enc2 34 " "[GET encoding offset ...]]" + +# Two-word command with multiple non-token block args: CONFIG SET parameter value [parameter value ...] +"CONFIG SET param " "value [parameter value ...]" +"CONFIG SET param val " "[parameter value ...]" +"CONFIG SET param val parm2 val2 " "[parameter value ...]" + +# Command with nested optional args: ZRANDMEMBER key [count [WITHSCORES]] +"ZRANDMEMBER k " "[count [WITHSCORES]]" +"ZRANDMEMBER k 3 " "[WITHSCORES]" +"ZRANDMEMBER k 3 WI" "[WITHSCORES]" +"ZRANDMEMBER k 3 WITHSCORES " "" +# Wrong data type: count must be an integer. Hinting fails. +"ZRANDMEMBER k cnt " "" + +# Command ends with repeated arg: MGET key [key ...] +"MGET " "key [key ...]" +"MGET k " "[key ...]" +"MGET k k " "[key ...]" + +# Optional args can be in any order: SCAN cursor [MATCH pattern] [COUNT count] [TYPE type] +"SCAN 2 MATCH " "pattern [COUNT count] [TYPE type]" +"SCAN 2 COUNT " "count [MATCH pattern] [TYPE type]" + +# One-of choices: BLMOVE source destination LEFT|RIGHT LEFT|RIGHT timeout +"BLMOVE src dst LEFT " "LEFT|RIGHT timeout" + +# Optional args can be in any order: ZRANGE key min max [BYSCORE|BYLEX] [REV] [LIMIT offset count] [WITHSCORES] +"ZRANGE k 1 2 " "[BYSCORE|BYLEX] [REV] [LIMIT offset count] [WITHSCORES]" +"ZRANGE k 1 2 bylex " "[REV] [LIMIT offset count] [WITHSCORES]" +"ZRANGE k 1 2 bylex rev " "[LIMIT offset count] [WITHSCORES]" +"ZRANGE k 1 2 limit 2 4 " "[BYSCORE|BYLEX] [REV] [WITHSCORES]" +"ZRANGE k 1 2 bylex rev limit 2 4 WITHSCORES " "" +"ZRANGE k 1 2 rev " "[BYSCORE|BYLEX] [LIMIT offset count] [WITHSCORES]" +"ZRANGE k 1 2 WITHSCORES " "[BYSCORE|BYLEX] [REV] [LIMIT offset count]" + +# Optional one-of args with parameters: SET key value [NX|XX] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL] +"SET key value " "[NX|XX] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]" +"SET key value EX" "[NX|XX] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]" +"SET key value EX " "seconds [NX|XX] [GET]" +"SET key value EX 23 " "[NX|XX] [GET]" +"SET key value EXAT" "[NX|XX] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]" +"SET key value EXAT " "unix-time-seconds [NX|XX] [GET]" +"SET key value PX" "[NX|XX] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]" +"SET key value PX " "milliseconds [NX|XX] [GET]" +"SET key value PXAT" "[NX|XX] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]" +"SET key value PXAT " "unix-time-milliseconds [NX|XX] [GET]" +"SET key value KEEPTTL " "[NX|XX] [GET]" +"SET key value XX " "[GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]" + +# If an input word can't be matched, stop hinting. +"SET key value FOOBAR " "" +# Incorrect type for EX 'seconds' parameter - stop hinting. +"SET key value EX sec " "" + +# Reordering partially-matched optional argument: GEORADIUS key longitude latitude radius M|KM|FT|MI [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC|DESC] [STORE key|STOREDIST key] +"GEORADIUS key " "longitude latitude radius M|KM|FT|MI [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC|DESC] [STORE key|STOREDIST key]" +"GEORADIUS key 1 2 3 M " "[WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC|DESC] [STORE key|STOREDIST key]" +"GEORADIUS key 1 2 3 M COUNT " "count [ANY] [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [STORE key|STOREDIST key]" +"GEORADIUS key 1 2 3 M COUNT 12 " "[ANY] [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [STORE key|STOREDIST key]" +"GEORADIUS key 1 2 3 M COUNT 12 " "[ANY] [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [STORE key|STOREDIST key]" +"GEORADIUS key 1 -2.345 3 M COUNT 12 " "[ANY] [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [STORE key|STOREDIST key]"" "" +# Wrong data type: latitude must be a double. Hinting fails. +"GEORADIUS key 1 X " "" +# Once the next optional argument is started, the [ANY] hint completing the COUNT argument disappears. +"GEORADIUS key 1 2 3 M COUNT 12 ASC " "[WITHCOORD] [WITHDIST] [WITHHASH] [STORE key|STOREDIST key]" + +# Incorrect argument type for double-valued token parameter. +"GEOSEARCH k FROMLONLAT " "longitude latitude BYRADIUS radius M|KM|FT|MI|BYBOX width height M|KM|FT|MI [ASC|DESC] [COUNT count [ANY]] [WITHCOORD] [WITHDIST] [WITHHASH]" +"GEOSEARCH k FROMLONLAT 2.34 4.45 BYRADIUS badvalue " "" + +# Optional parameters followed by mandatory params: ZADD key [NX|XX] [GT|LT] [CH] [INCR] score member [score member ...] +"ZADD key " "[NX|XX] [GT|LT] [CH] [INCR] score member [score member ...]" +"ZADD key CH LT " "[NX|XX] [INCR] score member [score member ...]" +"ZADD key 0 " "member [score member ...]" + +# Empty-valued token argument represented as a pair of double-quotes. +"MIGRATE " "host port key|\"\" destination-db timeout [COPY] [REPLACE] [AUTH password|AUTH2 username password] [KEYS key [key ...]]" diff --git a/tests/cluster/run.tcl b/tests/cluster/run.tcl index c81d8f39d..86c5f589b 100644 --- a/tests/cluster/run.tcl +++ b/tests/cluster/run.tcl @@ -17,6 +17,7 @@ proc main {} { "appendonly yes" "enable-protected-configs yes" "enable-debug-command yes" + "save ''" } run_tests cleanup diff --git a/tests/integration/aof-multi-part.tcl b/tests/integration/aof-multi-part.tcl index 74f6b4949..1d41a8a83 100644 --- a/tests/integration/aof-multi-part.tcl +++ b/tests/integration/aof-multi-part.tcl @@ -755,7 +755,7 @@ tags {"external:skip"} { # writing pressure, etc. - start_server {tags {"Multi Part AOF"} overrides {aof-use-rdb-preamble {yes} appendonly {no}}} { + start_server {tags {"Multi Part AOF"} overrides {aof-use-rdb-preamble {yes} appendonly {no} save {}}} { set dir [get_redis_dir] set aof_basename "appendonly.aof" set aof_dirname "appendonlydir" @@ -1173,7 +1173,7 @@ tags {"external:skip"} { assert {$d1 eq $d2} } - start_server {overrides {aof-use-rdb-preamble {yes} appendonly {no}}} { + start_server {overrides {aof-use-rdb-preamble {yes} appendonly {no} save {}}} { set dir [get_redis_dir] set aof_basename "appendonly.aof" set aof_dirname "appendonlydir" diff --git a/tests/integration/block-repl.tcl b/tests/integration/block-repl.tcl index 3f3a86ed8..52b4a53ea 100644 --- a/tests/integration/block-repl.tcl +++ b/tests/integration/block-repl.tcl @@ -12,7 +12,7 @@ proc stop_bg_block_op {handle} { } start_server {tags {"repl" "external:skip"}} { - start_server {} { + start_server {overrides {save {}}} { set master [srv -1 client] set master_host [srv -1 host] set master_port [srv -1 port] diff --git a/tests/integration/failover.tcl b/tests/integration/failover.tcl index 2cd944851..21fa3d281 100644 --- a/tests/integration/failover.tcl +++ b/tests/integration/failover.tcl @@ -1,6 +1,6 @@ -start_server {tags {"failover external:skip"}} { -start_server {} { -start_server {} { +start_server {tags {"failover external:skip"} overrides {save {}}} { +start_server {overrides {save {}}} { +start_server {overrides {save {}}} { set node_0 [srv 0 client] set node_0_host [srv 0 host] set node_0_port [srv 0 port] @@ -66,13 +66,13 @@ start_server {} { # Generate a delta between primary and replica set load_handler [start_write_load $node_0_host $node_0_port 5] - exec kill -SIGSTOP [srv -1 pid] + pause_process [srv -1 pid] wait_for_condition 50 100 { [s 0 total_commands_processed] > 100 } else { fail "Node 0 did not accept writes" } - exec kill -SIGCONT [srv -1 pid] + resume_process [srv -1 pid] # Execute the failover $node_0 failover to $node_1_host $node_1_port @@ -108,7 +108,7 @@ start_server {} { wait_for_ofs_sync $node_1 $node_2 # We stop node 0 to and make sure node 2 is selected - exec kill -SIGSTOP $node_0_pid + pause_process $node_0_pid $node_1 set CASE 1 $node_1 FAILOVER @@ -118,7 +118,7 @@ start_server {} { } else { fail "Failover from node 1 to node 2 did not finish" } - exec kill -SIGCONT $node_0_pid + resume_process $node_0_pid $node_0 replicaof $node_2_host $node_2_port wait_for_sync $node_0 @@ -138,7 +138,7 @@ start_server {} { set initial_psyncs [s 0 sync_partial_ok] set initial_syncs [s 0 sync_full] - exec kill -SIGSTOP $node_0_pid + pause_process $node_0_pid # node 0 will never acknowledge this write $node_2 set case 2 $node_2 failover to $node_0_host $node_0_port TIMEOUT 100 FORCE @@ -155,7 +155,7 @@ start_server {} { assert_match *slave* [$node_1 role] assert_match *slave* [$node_2 role] - exec kill -SIGCONT $node_0_pid + resume_process $node_0_pid # Wait for failover to end wait_for_condition 50 100 { @@ -186,7 +186,7 @@ start_server {} { set initial_syncs [s 0 sync_full] # Stop replica so it never catches up - exec kill -SIGSTOP [srv -1 pid] + pause_process [srv -1 pid] $node_0 SET CASE 1 $node_0 failover to [srv -1 host] [srv -1 port] TIMEOUT 500 @@ -197,7 +197,7 @@ start_server {} { fail "Failover from node_0 to replica did not finish" } - exec kill -SIGCONT [srv -1 pid] + resume_process [srv -1 pid] # We need to make sure the nodes actually sync back up wait_for_ofs_sync $node_0 $node_1 @@ -218,7 +218,7 @@ start_server {} { set initial_syncs [s 0 sync_full] # Stop replica so it never catches up - exec kill -SIGSTOP [srv -1 pid] + pause_process [srv -1 pid] $node_0 SET CASE 2 $node_0 failover to [srv -1 host] [srv -1 port] TIMEOUT 60000 @@ -230,7 +230,7 @@ start_server {} { $node_0 failover abort assert_match [s 0 master_failover_state] "no-failover" - exec kill -SIGCONT [srv -1 pid] + resume_process [srv -1 pid] # Just make sure everything is still synced wait_for_ofs_sync $node_0 $node_1 @@ -255,11 +255,11 @@ start_server {} { # We pause the target long enough to send a write command # during the pause. This write will not be interrupted. - exec kill -SIGSTOP [srv -1 pid] + pause_process [srv -1 pid] set rd [redis_deferring_client] $rd SET FOO BAR $node_0 failover to $node_1_host $node_1_port - exec kill -SIGCONT [srv -1 pid] + resume_process [srv -1 pid] # Wait for failover to end wait_for_condition 50 100 { diff --git a/tests/integration/psync2-master-restart.tcl b/tests/integration/psync2-master-restart.tcl index 03470bf81..a9e21d12d 100644 --- a/tests/integration/psync2-master-restart.tcl +++ b/tests/integration/psync2-master-restart.tcl @@ -11,6 +11,9 @@ start_server {} { set sub_replica [srv -2 client] + # Make sure the server saves an RDB on shutdown + $master config set save "3600 1" + # Because we will test partial resync later, we don’t want a timeout to cause # the master-replica disconnect, then the extra reconnections will break the # sync_partial_ok stat test @@ -18,6 +21,10 @@ start_server {} { $replica config set repl-timeout 3600 $sub_replica config set repl-timeout 3600 + # Avoid PINGs + $master config set repl-ping-replica-period 3600 + $master config rewrite + # Build replication chain $replica replicaof $master_host $master_port $sub_replica replicaof $replica_host $replica_port @@ -29,14 +36,43 @@ start_server {} { fail "Replication not started." } - # Avoid PINGs - $master config set repl-ping-replica-period 3600 - $master config rewrite + test "PSYNC2: Partial resync after Master restart using RDB aux fields when offset is 0" { + assert {[status $master master_repl_offset] == 0} + + set replid [status $master master_replid] + $replica config resetstat + + catch { + restart_server 0 true false true now + set master [srv 0 client] + } + wait_for_condition 50 1000 { + [status $replica master_link_status] eq {up} && + [status $sub_replica master_link_status] eq {up} + } else { + fail "Replicas didn't sync after master restart" + } + + # Make sure master restore replication info correctly + assert {[status $master master_replid] != $replid} + assert {[status $master master_repl_offset] == 0} + assert {[status $master master_replid2] eq $replid} + assert {[status $master second_repl_offset] == 1} + + # Make sure master set replication backlog correctly + assert {[status $master repl_backlog_active] == 1} + assert {[status $master repl_backlog_first_byte_offset] == 1} + assert {[status $master repl_backlog_histlen] == 0} + + # Partial resync after Master restart + assert {[status $master sync_partial_ok] == 1} + assert {[status $replica sync_partial_ok] == 1} + } # Generate some data createComplexDataset $master 1000 - test "PSYNC2: Partial resync after Master restart using RDB aux fields" { + test "PSYNC2: Partial resync after Master restart using RDB aux fields with data" { wait_for_condition 500 100 { [status $master master_repl_offset] == [status $replica master_repl_offset] && [status $master master_repl_offset] == [status $sub_replica master_repl_offset] diff --git a/tests/integration/psync2.tcl b/tests/integration/psync2.tcl index a258f1b83..4abe059b1 100644 --- a/tests/integration/psync2.tcl +++ b/tests/integration/psync2.tcl @@ -355,6 +355,8 @@ start_server {} { set sync_partial [status $R($master_id) sync_partial_ok] set sync_partial_err [status $R($master_id) sync_partial_err] catch { + # Make sure the server saves an RDB on shutdown + $R($slave_id) config set save "900 1" $R($slave_id) config rewrite restart_server [expr {0-$slave_id}] true false set R($slave_id) [srv [expr {0-$slave_id}] client] diff --git a/tests/integration/rdb.tcl b/tests/integration/rdb.tcl index 2362ef079..cce21671f 100644 --- a/tests/integration/rdb.tcl +++ b/tests/integration/rdb.tcl @@ -173,7 +173,7 @@ start_server {} { } test {client freed during loading} { - start_server [list overrides [list key-load-delay 50 loading-process-events-interval-bytes 1024 rdbcompression no]] { + start_server [list overrides [list key-load-delay 50 loading-process-events-interval-bytes 1024 rdbcompression no save "900 1"]] { # create a big rdb that will take long to load. it is important # for keys to be big since the server processes events only once in 2mb. # 100mb of rdb, 100k keys will load in more than 5 seconds @@ -370,6 +370,9 @@ start_server [list overrides [list "dir" $server_path "dbfilename" "scriptbackup start_server {} { test "failed bgsave prevents writes" { + # Make sure the server saves an RDB on shutdown + r config set save "900 1" + r config set rdb-key-save-delay 10000000 populate 1000 r set x x diff --git a/tests/integration/redis-cli.tcl b/tests/integration/redis-cli.tcl index 2c39d720a..da82dda65 100644 --- a/tests/integration/redis-cli.tcl +++ b/tests/integration/redis-cli.tcl @@ -423,6 +423,27 @@ if {!$::tls} { ;# fake_redis_node doesn't support TLS file delete $tmpfile } + test_nontty_cli "Test command-line hinting - latest server" { + # cli will connect to the running server and will use COMMAND DOCS + catch {run_cli --test_hint_file tests/assets/test_cli_hint_suite.txt} output + assert_match "*SUCCESS*" $output + } + + test_nontty_cli "Test command-line hinting - no server" { + # cli will fail to connect to the server and will use the cached commands.c + catch {run_cli -p 123 --test_hint_file tests/assets/test_cli_hint_suite.txt} output + assert_match "*SUCCESS*" $output + } + + test_nontty_cli "Test command-line hinting - old server" { + # cli will connect to the server but will not use COMMAND DOCS, + # and complete the missing info from the cached commands.c + r ACL setuser clitest on nopass +@all -command|docs + catch {run_cli --user clitest -a nopass --no-auth-warning --test_hint_file tests/assets/test_cli_hint_suite.txt} output + assert_match "*SUCCESS*" $output + r acl deluser clitest + } + proc test_redis_cli_rdb_dump {functions_only} { r flushdb r function flush diff --git a/tests/integration/replication-2.tcl b/tests/integration/replication-2.tcl index f9f259211..c18ff24fc 100644 --- a/tests/integration/replication-2.tcl +++ b/tests/integration/replication-2.tcl @@ -42,7 +42,7 @@ start_server {tags {"repl external:skip"}} { test {No write if min-slaves-max-lag is > of the slave lag} { r config set min-slaves-to-write 1 r config set min-slaves-max-lag 2 - exec kill -SIGSTOP [srv -1 pid] + pause_process [srv -1 pid] assert {[r set foo 12345] eq {OK}} wait_for_condition 100 100 { [catch {r set foo 12345}] != 0 @@ -52,7 +52,7 @@ start_server {tags {"repl external:skip"}} { catch {r set foo 12345} err assert_match {NOREPLICAS*} $err } - exec kill -SIGCONT [srv -1 pid] + resume_process [srv -1 pid] test {min-slaves-to-write is ignored by slaves} { r config set min-slaves-to-write 1 diff --git a/tests/integration/replication-4.tcl b/tests/integration/replication-4.tcl index f772eccb2..867ef364e 100644 --- a/tests/integration/replication-4.tcl +++ b/tests/integration/replication-4.tcl @@ -1,5 +1,5 @@ -start_server {tags {"repl network external:skip singledb:skip"}} { - start_server {} { +start_server {tags {"repl network external:skip singledb:skip"} overrides {save {}}} { + start_server { overrides {save {}}} { set master [srv -1 client] set master_host [srv -1 host] @@ -104,7 +104,7 @@ start_server {tags {"repl external:skip"}} { assert_equal OK [$master set foo 123] assert_equal OK [$master eval "return redis.call('set','foo',12345)" 0] # Killing a slave to make it become a lagged slave. - exec kill -SIGSTOP [srv 0 pid] + pause_process [srv 0 pid] # Waiting for slave kill. wait_for_condition 100 100 { [catch {$master set foo 123}] != 0 @@ -113,7 +113,7 @@ start_server {tags {"repl external:skip"}} { } assert_error "*NOREPLICAS*" {$master set foo 123} assert_error "*NOREPLICAS*" {$master eval "return redis.call('set','foo',12345)" 0} - exec kill -SIGCONT [srv 0 pid] + resume_process [srv 0 pid] } } } @@ -146,12 +146,12 @@ start_server {tags {"repl external:skip"}} { $master debug set-active-expire 0 $master set k 1 px $px_ms wait_for_ofs_sync $master $slave - exec kill -SIGSTOP [srv 0 pid] + pause_process [srv 0 pid] $master incr k after [expr $px_ms + 1] # Stopping the replica for one second to makes sure the INCR arrives # to the replica after the key is logically expired. - exec kill -SIGCONT [srv 0 pid] + resume_process [srv 0 pid] wait_for_ofs_sync $master $slave # Check that k is logically expired but is present in the replica. set res [$slave exists k] diff --git a/tests/integration/replication-buffer.tcl b/tests/integration/replication-buffer.tcl index 2e402480d..143dc74aa 100644 --- a/tests/integration/replication-buffer.tcl +++ b/tests/integration/replication-buffer.tcl @@ -159,7 +159,7 @@ start_server {} { assert {[s repl_backlog_histlen] > [expr 2*10000*10000]} assert_equal [s connected_slaves] {2} - exec kill -SIGSTOP $replica2_pid + pause_process $replica2_pid r config set client-output-buffer-limit "replica 128k 0 0" # trigger output buffer limit check r set key [string repeat A [expr 64*1024]] @@ -178,7 +178,7 @@ start_server {} { } else { fail "Replication backlog memory is not smaller" } - exec kill -SIGCONT $replica2_pid + resume_process $replica2_pid } # speed up termination $master config set shutdown-timeout 0 diff --git a/tests/integration/replication-psync.tcl b/tests/integration/replication-psync.tcl index 16f3b8889..dc1df0fa6 100644 --- a/tests/integration/replication-psync.tcl +++ b/tests/integration/replication-psync.tcl @@ -9,8 +9,8 @@ # reconnect with the master, otherwise just the initial synchronization is # checked for consistency. proc test_psync {descr duration backlog_size backlog_ttl delay cond mdl sdl reconnect} { - start_server {tags {"repl"}} { - start_server {} { + start_server {tags {"repl"} overrides {save {}}} { + start_server {overrides {save {}}} { set master [srv -1 client] set master_host [srv -1 host] diff --git a/tests/integration/replication.tcl b/tests/integration/replication.tcl index b4e9ee673..de4d527f4 100644 --- a/tests/integration/replication.tcl +++ b/tests/integration/replication.tcl @@ -302,7 +302,7 @@ start_server {tags {"repl external:skip"}} { foreach mdl {no yes} { foreach sdl {disabled swapdb} { - start_server {tags {"repl external:skip"}} { + start_server {tags {"repl external:skip"} overrides {save {}}} { set master [srv 0 client] $master config set repl-diskless-sync $mdl $master config set repl-diskless-sync-delay 5 @@ -310,11 +310,11 @@ foreach mdl {no yes} { set master_host [srv 0 host] set master_port [srv 0 port] set slaves {} - start_server {} { + start_server {overrides {save {}}} { lappend slaves [srv 0 client] - start_server {} { + start_server {overrides {save {}}} { lappend slaves [srv 0 client] - start_server {} { + start_server {overrides {save {}}} { lappend slaves [srv 0 client] test "Connect multiple replicas at the same time (issue #141), master diskless=$mdl, replica diskless=$sdl" { # start load handles only inside the test, so that the test can be skipped @@ -391,11 +391,11 @@ foreach mdl {no yes} { } } -start_server {tags {"repl external:skip"}} { +start_server {tags {"repl external:skip"} overrides {save {}}} { set master [srv 0 client] set master_host [srv 0 host] set master_port [srv 0 port] - start_server {} { + start_server {overrides {save {}}} { test "Master stream is correctly processed while the replica has a script in -BUSY state" { set load_handle0 [start_write_load $master_host $master_port 3] set slave [srv 0 client] @@ -705,11 +705,11 @@ foreach testType {Successful Aborted} { } test {diskless loading short read} { - start_server {tags {"repl"}} { + start_server {tags {"repl"} overrides {save ""}} { set replica [srv 0 client] set replica_host [srv 0 host] set replica_port [srv 0 port] - start_server {} { + start_server {overrides {save ""}} { set master [srv 0 client] set master_host [srv 0 host] set master_port [srv 0 port] @@ -847,7 +847,7 @@ proc compute_cpu_usage {start end} { # test diskless rdb pipe with multiple replicas, which may drop half way -start_server {tags {"repl external:skip"}} { +start_server {tags {"repl external:skip"} overrides {save ""}} { set master [srv 0 client] $master config set repl-diskless-sync yes $master config set repl-diskless-sync-delay 5 @@ -868,10 +868,10 @@ start_server {tags {"repl external:skip"}} { set replicas {} set replicas_alive {} # start one replica that will read the rdb fast, and one that will be slow - start_server {} { + start_server {overrides {save ""}} { lappend replicas [srv 0 client] lappend replicas_alive [srv 0 client] - start_server {} { + start_server {overrides {save ""}} { lappend replicas [srv 0 client] lappend replicas_alive [srv 0 client] @@ -913,7 +913,7 @@ start_server {tags {"repl external:skip"}} { if {$all_drop == "timeout"} { $master config set repl-timeout 2 # we want the slow replica to hang on a key for very long so it'll reach repl-timeout - exec kill -SIGSTOP [srv -1 pid] + pause_process [srv -1 pid] after 2000 } @@ -940,7 +940,7 @@ start_server {tags {"repl external:skip"}} { # master disconnected the slow replica, remove from array set replicas_alive [lreplace $replicas_alive 0 0] # release it - exec kill -SIGCONT [srv -1 pid] + resume_process [srv -1 pid] } # make sure we don't have a busy loop going thought epoll_wait @@ -1000,7 +1000,7 @@ test "diskless replication child being killed is collected" { # when diskless master is waiting for the replica to become writable # it removes the read event from the rdb pipe so if the child gets killed # the replica will hung. and the master may not collect the pid with waitpid - start_server {tags {"repl"}} { + start_server {tags {"repl"} overrides {save ""}} { set master [srv 0 client] set master_host [srv 0 host] set master_port [srv 0 port] @@ -1010,7 +1010,7 @@ test "diskless replication child being killed is collected" { # put enough data in the db that the rdb file will be bigger than the socket buffers $master debug populate 20000 test 10000 $master config set rdbcompression no - start_server {} { + start_server {overrides {save ""}} { set replica [srv 0 client] set loglines [count_log_lines 0] $replica config set repl-diskless-load swapdb @@ -1044,7 +1044,7 @@ test "diskless replication child being killed is collected" { foreach mdl {yes no} { test "replication child dies when parent is killed - diskless: $mdl" { # when master is killed, make sure the fork child can detect that and exit - start_server {tags {"repl"}} { + start_server {tags {"repl"} overrides {save ""}} { set master [srv 0 client] set master_host [srv 0 host] set master_port [srv 0 port] @@ -1054,7 +1054,7 @@ foreach mdl {yes no} { # create keys that will take 10 seconds to save $master config set rdb-key-save-delay 1000 $master debug populate 10000 - start_server {} { + start_server {overrides {save ""}} { set replica [srv 0 client] $replica replicaof $master_host $master_port @@ -1085,7 +1085,7 @@ test "diskless replication read pipe cleanup" { # When we close this pipe (fd), the read handler also needs to be removed from the event loop (if it still registered). # Otherwise, next time we will use the same fd, the registration will be fail (panic), because # we will use EPOLL_CTL_MOD (the fd still register in the event loop), on fd that already removed from epoll_ctl - start_server {tags {"repl"}} { + start_server {tags {"repl"} overrides {save ""}} { set master [srv 0 client] set master_host [srv 0 host] set master_port [srv 0 port] @@ -1097,7 +1097,7 @@ test "diskless replication read pipe cleanup" { $master config set rdb-key-save-delay 100000 $master debug populate 20000 test 10000 $master config set rdbcompression no - start_server {} { + start_server {overrides {save ""}} { set replica [srv 0 client] set loglines [count_log_lines 0] $replica config set repl-diskless-load swapdb @@ -1122,17 +1122,17 @@ test "diskless replication read pipe cleanup" { test {replicaof right after disconnection} { # this is a rare race condition that was reproduced sporadically by the psync2 unit. # see details in #7205 - start_server {tags {"repl"}} { + start_server {tags {"repl"} overrides {save ""}} { set replica1 [srv 0 client] set replica1_host [srv 0 host] set replica1_port [srv 0 port] set replica1_log [srv 0 stdout] - start_server {} { + start_server {overrides {save ""}} { set replica2 [srv 0 client] set replica2_host [srv 0 host] set replica2_port [srv 0 port] set replica2_log [srv 0 stdout] - start_server {} { + start_server {overrides {save ""}} { set master [srv 0 client] set master_host [srv 0 host] set master_port [srv 0 port] diff --git a/tests/integration/shutdown.tcl b/tests/integration/shutdown.tcl index 60afc5c7f..b2ec32cbd 100644 --- a/tests/integration/shutdown.tcl +++ b/tests/integration/shutdown.tcl @@ -19,8 +19,8 @@ proc fill_up_os_socket_send_buffer_for_repl {idx} { foreach how {sigterm shutdown} { test "Shutting down master waits for replica to catch up ($how)" { - start_server {} { - start_server {} { + start_server {overrides {save ""}} { + start_server {overrides {save ""}} { set master [srv -1 client] set master_host [srv -1 host] set master_port [srv -1 port] @@ -42,8 +42,7 @@ foreach how {sigterm shutdown} { wait_for_ofs_sync $master $replica # Pause the replica. - exec kill -SIGSTOP $replica_pid - after 10 + pause_process $replica_pid # Fill up the OS socket send buffer for the replica connection # to prevent the following INCR from reaching the replica via @@ -69,7 +68,7 @@ foreach how {sigterm shutdown} { # Wake up replica and check if master has waited for it. after 20; # 2 cron intervals - exec kill -SIGCONT $replica_pid + resume_process $replica_pid wait_for_condition 300 1000 { [$replica get k] eq 2 } else { @@ -86,8 +85,8 @@ foreach how {sigterm shutdown} { } test {Shutting down master waits for replica timeout} { - start_server {} { - start_server {} { + start_server {overrides {save ""}} { + start_server {overrides {save ""}} { set master [srv -1 client] set master_host [srv -1 host] set master_port [srv -1 port] @@ -107,8 +106,7 @@ test {Shutting down master waits for replica timeout} { wait_for_ofs_sync $master $replica # Pause the replica. - exec kill -SIGSTOP $replica_pid - after 10 + pause_process $replica_pid # Fill up the OS socket send buffer for the replica connection to # prevent the following INCR k from reaching the replica via the OS. @@ -129,15 +127,15 @@ test {Shutting down master waits for replica timeout} { verify_log_message -1 "*0 of 1 replicas are in sync*" 0 # Wake up replica. - exec kill -SIGCONT $replica_pid + resume_process $replica_pid assert_equal 1 [$replica get k] } } } {} {repl external:skip} test "Shutting down master waits for replica then fails" { - start_server {} { - start_server {} { + start_server {overrides {save ""}} { + start_server {overrides {save ""}} { set master [srv -1 client] set master_host [srv -1 host] set master_port [srv -1 port] @@ -150,8 +148,7 @@ test "Shutting down master waits for replica then fails" { wait_for_sync $replica # Pause the replica and write a key on master. - exec kill -SIGSTOP $replica_pid - after 10 + pause_process $replica_pid $master incr k # Two clients call blocking SHUTDOWN in parallel. @@ -168,7 +165,7 @@ test "Shutting down master waits for replica then fails" { $master config set appendonly yes # Wake up replica, causing master to continue shutting down. - exec kill -SIGCONT $replica_pid + resume_process $replica_pid # SHUTDOWN returns an error to both clients blocking on SHUTDOWN. catch { $rd1 read } e1 @@ -190,8 +187,8 @@ test "Shutting down master waits for replica then fails" { } {} {repl external:skip} test "Shutting down master waits for replica then aborted" { - start_server {} { - start_server {} { + start_server {overrides {save ""}} { + start_server {overrides {save ""}} { set master [srv -1 client] set master_host [srv -1 host] set master_port [srv -1 port] @@ -204,8 +201,7 @@ test "Shutting down master waits for replica then aborted" { wait_for_sync $replica # Pause the replica and write a key on master. - exec kill -SIGSTOP $replica_pid - after 10 + pause_process $replica_pid $master incr k # Two clients call blocking SHUTDOWN in parallel. @@ -221,7 +217,7 @@ test "Shutting down master waits for replica then aborted" { $master shutdown abort # Wake up replica, causing master to continue shutting down. - exec kill -SIGCONT $replica_pid + resume_process $replica_pid # SHUTDOWN returns an error to both clients blocking on SHUTDOWN. catch { $rd1 read } e1 diff --git a/tests/modules/Makefile b/tests/modules/Makefile index a1f5b074b..d63c8548d 100644 --- a/tests/modules/Makefile +++ b/tests/modules/Makefile @@ -61,7 +61,8 @@ TEST_MODULES = \ publish.so \ usercall.so \ postnotifications.so \ - moduleauthtwo.so + moduleauthtwo.so \ + rdbloadsave.so .PHONY: all diff --git a/tests/modules/blockonkeys.c b/tests/modules/blockonkeys.c index 8f4353a55..bc3b6b1a4 100644 --- a/tests/modules/blockonkeys.c +++ b/tests/modules/blockonkeys.c @@ -21,7 +21,7 @@ typedef struct { static RedisModuleType *fsltype = NULL; -fsl_t *fsl_type_create() { +fsl_t *fsl_type_create(void) { fsl_t *o; o = RedisModule_Alloc(sizeof(*o)); o->length = 0; diff --git a/tests/modules/cmdintrospection.c b/tests/modules/cmdintrospection.c index dd9fb7f60..1a5e4863b 100644 --- a/tests/modules/cmdintrospection.c +++ b/tests/modules/cmdintrospection.c @@ -23,7 +23,7 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) RedisModuleCommandInfo info = { .version = REDISMODULE_COMMAND_INFO_VERSION, .arity = -5, - .summary = "Appends a new entry to a stream", + .summary = "Appends a new message to a stream. Creates the key if it doesn't exist.", .since = "5.0.0", .complexity = "O(1) when adding a new entry, O(N) when trimming where N being the number of entries evicted.", .tips = "nondeterministic_output", diff --git a/tests/modules/rdbloadsave.c b/tests/modules/rdbloadsave.c new file mode 100644 index 000000000..687269a5a --- /dev/null +++ b/tests/modules/rdbloadsave.c @@ -0,0 +1,162 @@ +#include "redismodule.h" + +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <memory.h> +#include <errno.h> + +/* Sanity tests to verify inputs and return values. */ +int sanity(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { + REDISMODULE_NOT_USED(argv); + REDISMODULE_NOT_USED(argc); + + RedisModuleRdbStream *s = RedisModule_RdbStreamCreateFromFile("dbnew.rdb"); + + /* NULL stream should fail. */ + if (RedisModule_RdbLoad(ctx, NULL, 0) == REDISMODULE_OK || errno != EINVAL) { + RedisModule_ReplyWithError(ctx, strerror(errno)); + goto out; + } + + /* Invalid flags should fail. */ + if (RedisModule_RdbLoad(ctx, s, 188) == REDISMODULE_OK || errno != EINVAL) { + RedisModule_ReplyWithError(ctx, strerror(errno)); + goto out; + } + + /* Missing file should fail. */ + if (RedisModule_RdbLoad(ctx, s, 0) == REDISMODULE_OK || errno != ENOENT) { + RedisModule_ReplyWithError(ctx, strerror(errno)); + goto out; + } + + /* Save RDB file. */ + if (RedisModule_RdbSave(ctx, s, 0) != REDISMODULE_OK || errno != 0) { + RedisModule_ReplyWithError(ctx, strerror(errno)); + goto out; + } + + /* Load the saved RDB file. */ + if (RedisModule_RdbLoad(ctx, s, 0) != REDISMODULE_OK || errno != 0) { + RedisModule_ReplyWithError(ctx, strerror(errno)); + goto out; + } + + RedisModule_ReplyWithSimpleString(ctx, "OK"); + + out: + RedisModule_RdbStreamFree(s); + return REDISMODULE_OK; +} + +int cmd_rdbsave(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { + if (argc != 2) { + RedisModule_WrongArity(ctx); + return REDISMODULE_OK; + } + + size_t len; + const char *filename = RedisModule_StringPtrLen(argv[1], &len); + + char tmp[len + 1]; + memcpy(tmp, filename, len); + tmp[len] = '\0'; + + RedisModuleRdbStream *stream = RedisModule_RdbStreamCreateFromFile(tmp); + + if (RedisModule_RdbSave(ctx, stream, 0) != REDISMODULE_OK || errno != 0) { + RedisModule_ReplyWithError(ctx, strerror(errno)); + goto out; + } + + RedisModule_ReplyWithSimpleString(ctx, "OK"); + +out: + RedisModule_RdbStreamFree(stream); + return REDISMODULE_OK; +} + +/* Fork before calling RM_RdbSave(). */ +int cmd_rdbsave_fork(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { + if (argc != 2) { + RedisModule_WrongArity(ctx); + return REDISMODULE_OK; + } + + size_t len; + const char *filename = RedisModule_StringPtrLen(argv[1], &len); + + char tmp[len + 1]; + memcpy(tmp, filename, len); + tmp[len] = '\0'; + + int fork_child_pid = RedisModule_Fork(NULL, NULL); + if (fork_child_pid < 0) { + RedisModule_ReplyWithError(ctx, strerror(errno)); + return REDISMODULE_OK; + } else if (fork_child_pid > 0) { + /* parent */ + RedisModule_ReplyWithSimpleString(ctx, "OK"); + return REDISMODULE_OK; + } + + RedisModuleRdbStream *stream = RedisModule_RdbStreamCreateFromFile(tmp); + + int ret = 0; + if (RedisModule_RdbSave(ctx, stream, 0) != REDISMODULE_OK) { + ret = errno; + } + RedisModule_RdbStreamFree(stream); + + RedisModule_ExitFromChild(ret); + return REDISMODULE_OK; +} + +int cmd_rdbload(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { + if (argc != 2) { + RedisModule_WrongArity(ctx); + return REDISMODULE_OK; + } + + size_t len; + const char *filename = RedisModule_StringPtrLen(argv[1], &len); + + char tmp[len + 1]; + memcpy(tmp, filename, len); + tmp[len] = '\0'; + + RedisModuleRdbStream *stream = RedisModule_RdbStreamCreateFromFile(tmp); + + if (RedisModule_RdbLoad(ctx, stream, 0) != REDISMODULE_OK || errno != 0) { + RedisModule_RdbStreamFree(stream); + RedisModule_ReplyWithError(ctx, strerror(errno)); + return REDISMODULE_OK; + } + + RedisModule_RdbStreamFree(stream); + RedisModule_ReplyWithSimpleString(ctx, "OK"); + return REDISMODULE_OK; +} + +int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { + REDISMODULE_NOT_USED(argv); + REDISMODULE_NOT_USED(argc); + + if (RedisModule_Init(ctx, "rdbloadsave", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR) + return REDISMODULE_ERR; + + if (RedisModule_CreateCommand(ctx, "test.sanity", sanity, "", 0, 0, 0) == REDISMODULE_ERR) + return REDISMODULE_ERR; + + if (RedisModule_CreateCommand(ctx, "test.rdbsave", cmd_rdbsave, "", 0, 0, 0) == REDISMODULE_ERR) + return REDISMODULE_ERR; + + if (RedisModule_CreateCommand(ctx, "test.rdbsave_fork", cmd_rdbsave_fork, "", 0, 0, 0) == REDISMODULE_ERR) + return REDISMODULE_ERR; + + if (RedisModule_CreateCommand(ctx, "test.rdbload", cmd_rdbload, "", 0, 0, 0) == REDISMODULE_ERR) + return REDISMODULE_ERR; + + return REDISMODULE_OK; +} diff --git a/tests/modules/reply.c b/tests/modules/reply.c index f890560e0..c5baa6635 100644 --- a/tests/modules/reply.c +++ b/tests/modules/reply.c @@ -156,6 +156,14 @@ int rw_error(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { return RedisModule_ReplyWithError(ctx, "An error"); } +int rw_error_format(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { + if (argc != 3) return RedisModule_WrongArity(ctx); + + return RedisModule_ReplyWithErrorFormat(ctx, + RedisModule_StringPtrLen(argv[1], NULL), + RedisModule_StringPtrLen(argv[2], NULL)); +} + int rw_verbatim(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { if (argc != 2) return RedisModule_WrongArity(ctx); @@ -197,6 +205,8 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) return REDISMODULE_ERR; if (RedisModule_CreateCommand(ctx,"rw.error",rw_error,"",0,0,0) != REDISMODULE_OK) return REDISMODULE_ERR; + if (RedisModule_CreateCommand(ctx,"rw.error_format",rw_error_format,"",0,0,0) != REDISMODULE_OK) + return REDISMODULE_ERR; if (RedisModule_CreateCommand(ctx,"rw.verbatim",rw_verbatim,"",0,0,0) != REDISMODULE_OK) return REDISMODULE_ERR; diff --git a/tests/modules/zset.c b/tests/modules/zset.c index 91791f907..13f2ab3b6 100644 --- a/tests/modules/zset.c +++ b/tests/modules/zset.c @@ -1,4 +1,6 @@ #include "redismodule.h" +#include <math.h> +#include <errno.h> /* ZSET.REM key element * @@ -17,14 +19,73 @@ int zset_rem(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { return RedisModule_ReplyWithError(ctx, "ERR ZsetRem failed"); } +/* ZSET.ADD key score member + * + * Adds a specified member with the specified score to the sorted + * set stored at key. + */ +int zset_add(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { + if (argc != 4) return RedisModule_WrongArity(ctx); + RedisModule_AutoMemory(ctx); + int keymode = REDISMODULE_READ | REDISMODULE_WRITE; + RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], keymode); + + size_t len; + double score; + char *endptr; + const char *str = RedisModule_StringPtrLen(argv[2], &len); + score = strtod(str, &endptr); + if (*endptr != '\0' || errno == ERANGE) + return RedisModule_ReplyWithError(ctx, "value is not a valid float"); + + if (RedisModule_ZsetAdd(key, score, argv[3], NULL) == REDISMODULE_OK) + return RedisModule_ReplyWithSimpleString(ctx, "OK"); + else + return RedisModule_ReplyWithError(ctx, "ERR ZsetAdd failed"); +} + +/* ZSET.INCRBY key member increment + * + * Increments the score stored at member in the sorted set stored at key by increment. + * Replies with the new score of this element. + */ +int zset_incrby(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { + if (argc != 4) return RedisModule_WrongArity(ctx); + RedisModule_AutoMemory(ctx); + int keymode = REDISMODULE_READ | REDISMODULE_WRITE; + RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], keymode); + + size_t len; + double score, newscore; + char *endptr; + const char *str = RedisModule_StringPtrLen(argv[3], &len); + score = strtod(str, &endptr); + if (*endptr != '\0' || errno == ERANGE) + return RedisModule_ReplyWithError(ctx, "value is not a valid float"); + + if (RedisModule_ZsetIncrby(key, score, argv[2], NULL, &newscore) == REDISMODULE_OK) + return RedisModule_ReplyWithDouble(ctx, newscore); + else + return RedisModule_ReplyWithError(ctx, "ERR ZsetIncrby failed"); +} + int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { REDISMODULE_NOT_USED(argv); REDISMODULE_NOT_USED(argc); - if (RedisModule_Init(ctx, "zset", 1, REDISMODULE_APIVER_1) == - REDISMODULE_OK && - RedisModule_CreateCommand(ctx, "zset.rem", zset_rem, "write", - 1, 1, 1) == REDISMODULE_OK) - return REDISMODULE_OK; - else + if (RedisModule_Init(ctx, "zset", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR) return REDISMODULE_ERR; + + if (RedisModule_CreateCommand(ctx, "zset.rem", zset_rem, "write", + 1, 1, 1) == REDISMODULE_ERR) + return REDISMODULE_ERR; + + if (RedisModule_CreateCommand(ctx, "zset.add", zset_add, "write", + 1, 1, 1) == REDISMODULE_ERR) + return REDISMODULE_ERR; + + if (RedisModule_CreateCommand(ctx, "zset.incrby", zset_incrby, "write", + 1, 1, 1) == REDISMODULE_ERR) + return REDISMODULE_ERR; + + return REDISMODULE_OK; } diff --git a/tests/sentinel/run.tcl b/tests/sentinel/run.tcl index 98c4c118b..6d3db324d 100644 --- a/tests/sentinel/run.tcl +++ b/tests/sentinel/run.tcl @@ -22,6 +22,7 @@ proc main {} { spawn_instance redis $::redis_base_port $::instances_count { "enable-protected-configs yes" "enable-debug-command yes" + "save ''" } run_tests cleanup diff --git a/tests/sentinel/tests/00-base.tcl b/tests/sentinel/tests/00-base.tcl index 1b33ca7a3..b4d65751b 100644 --- a/tests/sentinel/tests/00-base.tcl +++ b/tests/sentinel/tests/00-base.tcl @@ -31,6 +31,50 @@ test "Sentinel command flag infrastructure works correctly" { } } +test "SENTINEL HELP output the sentinel subcommand help" { + assert_match "*SENTINEL <subcommand> *" [S 0 SENTINEL HELP] +} + +test "SENTINEL MYID return the sentinel instance ID" { + assert_equal 40 [string length [S 0 SENTINEL MYID]] + assert_equal [S 0 SENTINEL MYID] [S 0 SENTINEL MYID] +} + +test "SENTINEL INFO CACHE returns the cached info" { + set res [S 0 SENTINEL INFO-CACHE mymaster] + assert_morethan_equal [llength $res] 2 + assert_equal "mymaster" [lindex $res 0] + + set res [lindex $res 1] + assert_morethan_equal [llength $res] 2 + assert_morethan [lindex $res 0] 0 + assert_match "*# Server*" [lindex $res 1] +} + +test "SENTINEL PENDING-SCRIPTS returns the information about pending scripts" { + # may or may not have a value, so assert greater than or equal to 0. + assert_morethan_equal [llength [S 0 SENTINEL PENDING-SCRIPTS]] 0 +} + +test "SENTINEL MASTERS returns a list of monitored masters" { + assert_match "*mymaster*" [S 0 SENTINEL MASTERS] + assert_morethan_equal [llength [S 0 SENTINEL MASTERS]] 1 +} + +test "SENTINEL SENTINELS returns a list of sentinel instances" { + assert_morethan_equal [llength [S 0 SENTINEL SENTINELS mymaster]] 1 +} + +test "SENTINEL SLAVES returns a list of the monitored replicas" { + assert_morethan_equal [llength [S 0 SENTINEL SLAVES mymaster]] 1 +} + +test "SENTINEL SIMULATE-FAILURE HELP list supported flags" { + set res [S 0 SENTINEL SIMULATE-FAILURE HELP] + assert_morethan_equal [llength $res] 2 + assert_equal {crash-after-election crash-after-promotion} $res +} + test "Basic failover works if the master is down" { set old_port [RPort $master_id] set addr [S 0 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster] @@ -149,3 +193,10 @@ test "Failover works if we configure for absolute agreement" { test "New master [join $addr {:}] role matches" { assert {[RI $master_id role] eq {master}} } + +test "SENTINEL RESET can resets the master" { + assert_equal 1 [S 0 SENTINEL RESET mymaster] + assert_equal 0 [llength [S 0 SENTINEL SENTINELS mymaster]] + assert_equal 0 [llength [S 0 SENTINEL SLAVES mymaster]] + assert_equal 0 [llength [S 0 SENTINEL REPLICAS mymaster]] +} diff --git a/tests/sentinel/tests/05-manual.tcl b/tests/sentinel/tests/05-manual.tcl index 72d80fdf8..7e050b0dc 100644 --- a/tests/sentinel/tests/05-manual.tcl +++ b/tests/sentinel/tests/05-manual.tcl @@ -61,3 +61,28 @@ test "The old master eventually gets reconfigured as a slave" { fail "Old master not reconfigured as slave of new master" } } + +foreach flag {crash-after-election crash-after-promotion} { + test "SENTINEL SIMULATE-FAILURE $flag works" { + assert_equal {OK} [S 0 SENTINEL SIMULATE-FAILURE $flag] + + # Trigger a failover, failover will trigger leader election, replica promotion + wait_for_condition 300 50 { + [catch {S 0 SENTINEL FAILOVER mymaster}] == 0 + } else { + catch {S 0 SENTINEL FAILOVER mymaster} reply + puts [S 0 SENTINEL REPLICAS mymaster] + fail "Sentinel manual failover did not work, got: $reply" + } + + # Wait for sentinel to exit (due to simulate-failure flags) + wait_for_condition 1000 50 { + [catch {S 0 PING}] == 1 + } else { + fail "Sentinel set $flag but did not exit" + } + assert_error {*couldn't open socket: connection refused*} {S 0 PING} + + restart_instance sentinel 0 + } +} diff --git a/tests/sentinel/tests/07-down-conditions.tcl b/tests/sentinel/tests/07-down-conditions.tcl index 403f81e73..dabbc14c5 100644 --- a/tests/sentinel/tests/07-down-conditions.tcl +++ b/tests/sentinel/tests/07-down-conditions.tcl @@ -49,9 +49,9 @@ test "SDOWN is triggered by non-responding but not crashed instance" { set master_id [get_instance_id_by_port redis [lindex $master_addr 1]] set pid [get_instance_attrib redis $master_id pid] - exec kill -SIGSTOP $pid + pause_process $pid ensure_master_down - exec kill -SIGCONT $pid + resume_process $pid ensure_master_up } diff --git a/tests/support/cluster_util.tcl b/tests/support/cluster_util.tcl index 8a58a4fa9..d80dcf062 100644 --- a/tests/support/cluster_util.tcl +++ b/tests/support/cluster_util.tcl @@ -36,7 +36,7 @@ proc wait_for_cluster_propagation {} { # Wait for cluster size to be consistent across nodes. proc wait_for_cluster_size {cluster_size} { - wait_for_condition 50 100 { + wait_for_condition 1000 50 { [cluster_size_consistent $cluster_size] eq 1 } else { fail "cluster size did not reach a consistent size $cluster_size" diff --git a/tests/support/server.tcl b/tests/support/server.tcl index 4c596290d..9a3733b61 100644 --- a/tests/support/server.tcl +++ b/tests/support/server.tcl @@ -537,6 +537,9 @@ proc start_server {options {code undefined}} { set fd [open $stdout "a+"] puts $fd "### Starting server for test $::cur_test" close $fd + if {$::verbose > 1} { + puts "### Starting server $stdout for test - $::cur_test" + } } # We may have a stdout left over from the previous tests, so we need diff --git a/tests/support/test.tcl b/tests/support/test.tcl index 68180bea4..b7cd38b38 100644 --- a/tests/support/test.tcl +++ b/tests/support/test.tcl @@ -168,7 +168,9 @@ proc test {name code {okpattern undefined} {tags {}}} { send_data_packet $::test_server_fd skip $name return } - + if {$::verbose > 1} { + puts "starting test $name" + } # abort if only_tests was set but test name is not included if {[llength $::only_tests] > 0 && ![search_pattern_list $name $::only_tests]} { incr ::num_skipped @@ -200,11 +202,16 @@ proc test {name code {okpattern undefined} {tags {}}} { $r close } } else { + set servers {} foreach srv $::servers { set stdout [dict get $srv stdout] set fd [open $stdout "a+"] puts $fd "### Starting test $::cur_test" close $fd + lappend servers $stdout + } + if {$::verbose > 1} { + puts "### Starting test $::cur_test - with servers: $servers" } } diff --git a/tests/support/util.tcl b/tests/support/util.tcl index 236fad314..a36003029 100644 --- a/tests/support/util.tcl +++ b/tests/support/util.tcl @@ -602,15 +602,24 @@ proc stop_bg_complex_data {handle} { # Write num keys with the given key prefix and value size (in bytes). If idx is # given, it's the index (AKA level) used with the srv procedure and it specifies # to which Redis instance to write the keys. -proc populate {num {prefix key:} {size 3} {idx 0}} { - set rd [redis_deferring_client $idx] - for {set j 0} {$j < $num} {incr j} { - $rd set $prefix$j [string repeat A $size] +proc populate {num {prefix key:} {size 3} {idx 0} {prints false}} { + r $idx deferred 1 + if {$num > 16} {set pipeline 16} else {set pipeline $num} + set val [string repeat A $size] + for {set j 0} {$j < $pipeline} {incr j} { + r $idx set $prefix$j $val + if {$prints} {puts $j} } - for {set j 0} {$j < $num} {incr j} { - $rd read + for {} {$j < $num} {incr j} { + r $idx set $prefix$j $val + r $idx read + if {$prints} {puts $j} } - $rd close + for {set j 0} {$j < $pipeline} {incr j} { + r $idx read + if {$prints} {puts $j} + } + r $idx deferred 0 } proc get_child_pid {idx} { @@ -628,7 +637,7 @@ proc get_child_pid {idx} { } proc process_is_alive pid { - if {[catch {exec ps -p $pid} err]} { + if {[catch {exec ps -p $pid -f} err]} { return 0 } else { if {[string match "*<defunct>*" $err]} { return 0 } @@ -636,6 +645,20 @@ proc process_is_alive pid { } } +proc pause_process pid { + exec kill -SIGSTOP $pid + wait_for_condition 50 100 { + [string match {*T*} [lindex [exec ps j $pid] 16]] + } else { + puts [exec ps j $pid] + fail "process didn't stop" + } +} + +proc resume_process pid { + exec kill -SIGCONT $pid +} + proc cmdrstat {cmd r} { if {[regexp "\r\ncmdstat_$cmd:(.*?)\r\n" [$r info commandstats] _ value]} { set _ $value diff --git a/tests/test_helper.tcl b/tests/test_helper.tcl index 922cb438d..6ec2ae1fc 100644 --- a/tests/test_helper.tcl +++ b/tests/test_helper.tcl @@ -526,6 +526,7 @@ proc signal_idle_client fd { incr ::next_test if {$::loop && $::next_test == [llength $::all_tests]} { set ::next_test 0 + incr ::loop -1 } } elseif {[llength $::run_solo_tests] != 0 && [llength $::active_clients] == 0} { if {!$::quiet} { @@ -620,6 +621,7 @@ proc print_help_screen {} { "--no-latency Skip latency measurements and validation by some tests." "--stop Blocks once the first test fails." "--loop Execute the specified set of tests forever." + "--loops <count> Execute the specified set of tests several times." "--wait-server Wait after server is started (so that you can attach a debugger)." "--dump-logs Dump server log on test failure." "--tls Run tests in TLS mode." @@ -721,7 +723,7 @@ for {set j 0} {$j < [llength $argv]} {incr j} { } exit 0 } elseif {$opt eq {--verbose}} { - set ::verbose 1 + incr ::verbose } elseif {$opt eq {--client}} { set ::client 1 set ::test_server_port $arg @@ -744,7 +746,10 @@ for {set j 0} {$j < [llength $argv]} {incr j} { } elseif {$opt eq {--stop}} { set ::stop_on_failure 1 } elseif {$opt eq {--loop}} { - set ::loop 1 + set ::loop 2147483647 + } elseif {$opt eq {--loops}} { + set ::loop $arg + incr j } elseif {$opt eq {--timeout}} { set ::timeout $arg incr j diff --git a/tests/unit/acl.tcl b/tests/unit/acl.tcl index 555fb5a34..4d8c77b9f 100644 --- a/tests/unit/acl.tcl +++ b/tests/unit/acl.tcl @@ -289,6 +289,20 @@ start_server {tags {"acl external:skip"}} { $rd close } {0} + test {Subscribers are killed when revoked of allchannels permission} { + set rd [redis_deferring_client] + r ACL setuser psuser allchannels + $rd AUTH psuser pspass + $rd read + $rd CLIENT SETNAME deathrow + $rd read + $rd PSUBSCRIBE foo + $rd read + r ACL setuser psuser resetchannels + assert_no_match {*deathrow*} [r CLIENT LIST] + $rd close + } {0} + test {Subscribers are pardoned if literal permissions are retained and/or gaining allchannels} { set rd [redis_deferring_client] r ACL setuser psuser resetchannels &foo:1 &bar:* &orders diff --git a/tests/unit/aofrw.tcl b/tests/unit/aofrw.tcl index fe07351a3..cc7545265 100644 --- a/tests/unit/aofrw.tcl +++ b/tests/unit/aofrw.tcl @@ -1,6 +1,6 @@ # This unit has the potential to create huge .reqres files, causing log-req-res-validator.py to run for a very long time... # Since this unit doesn't do anything worth validating, reply_schema-wise, we decided to skip it -start_server {tags {"aofrw external:skip logreqres:skip"}} { +start_server {tags {"aofrw external:skip logreqres:skip"} overrides {save {}}} { # Enable the AOF r config set appendonly yes r config set auto-aof-rewrite-percentage 0 ; # Disable auto-rewrite. diff --git a/tests/unit/client-eviction.tcl b/tests/unit/client-eviction.tcl index 76f7bf0f2..1fc7c02ca 100644 --- a/tests/unit/client-eviction.tcl +++ b/tests/unit/client-eviction.tcl @@ -347,12 +347,12 @@ start_server {} { # We use two obuf-clients to make sure that even if client eviction is attempted # between two command processing (with no sleep) we don't perform any client eviction # because the obuf limit is enforced with precedence. - exec kill -SIGSTOP $server_pid + pause_process $server_pid $rr2 get k $rr2 flush $rr3 get k $rr3 flush - exec kill -SIGCONT $server_pid + resume_process $server_pid r ping ;# make sure a full event loop cycle is processed before issuing CLIENT LIST # Validate obuf-clients were disconnected (because of obuf limit) diff --git a/tests/unit/cluster/cli.tcl b/tests/unit/cluster/cli.tcl index 7131ee20f..5b7f24927 100644 --- a/tests/unit/cluster/cli.tcl +++ b/tests/unit/cluster/cli.tcl @@ -64,7 +64,7 @@ start_multiple_servers 3 [list overrides $base_conf] { } test "Wait for cluster to be stable" { - # Cluster check just verifies the the config state is self-consistent, + # Cluster check just verifies the config state is self-consistent, # waiting for cluster_state to be okay is an independent check that all the # nodes actually believe each other are healthy, prevent cluster down error. wait_for_condition 1000 50 { @@ -116,7 +116,7 @@ start_multiple_servers 3 [list overrides $base_conf] { test "Kill a cluster node and wait for fail state" { # kill node3 in cluster - exec kill -SIGSTOP $node3_pid + pause_process $node3_pid wait_for_condition 1000 50 { [CI 0 cluster_state] eq {fail} && @@ -134,7 +134,7 @@ start_multiple_servers 3 [list overrides $base_conf] { assert_equal [s -1 blocked_clients] {0} } - exec kill -SIGCONT $node3_pid + resume_process $node3_pid $node1_rd close } ;# stop servers diff --git a/tests/unit/cluster/links.tcl b/tests/unit/cluster/links.tcl index 63c2b143c..a202c378b 100644 --- a/tests/unit/cluster/links.tcl +++ b/tests/unit/cluster/links.tcl @@ -200,7 +200,7 @@ start_cluster 3 0 {tags {external:skip cluster}} { # To manufacture an ever-growing send buffer from primary1 to primary2, # make primary2 unresponsive. set primary2_pid [srv [expr -1*$primary2_id] pid] - exec kill -SIGSTOP $primary2_pid + pause_process $primary2_pid # On primary1, send 128KB Pubsub messages in a loop until the send buffer of the link from # primary1 to primary2 exceeds buffer limit therefore be dropped. @@ -226,7 +226,7 @@ start_cluster 3 0 {tags {external:skip cluster}} { assert {[dict get $same_link_p1_from_p2 create-time] eq [dict get $orig_link_p1_from_p2 create-time]} # Revive primary2 - exec kill -SIGCONT $primary2_pid + resume_process $primary2_pid # Reset configs on primary1 so config changes don't leak out to other tests $primary1 CONFIG set cluster-node-timeout $oldtimeout diff --git a/tests/unit/expire.tcl b/tests/unit/expire.tcl index 5ee4488b3..ec9d73cc2 100644 --- a/tests/unit/expire.tcl +++ b/tests/unit/expire.tcl @@ -76,20 +76,22 @@ start_server {tags {"expire"}} { # This test is very likely to do a false positive if the # server is under pressure, so if it does not work give it a few more # chances. - for {set j 0} {$j < 10} {incr j} { + for {set j 0} {$j < 30} {incr j} { r del x r setex x 1 somevalue - after 900 + after 800 set a [r get x] - after 1100 + if {$a ne {somevalue}} continue + after 300 set b [r get x] - if {$a eq {somevalue} && $b eq {}} break + if {$b eq {}} break } if {$::verbose} { puts "millisecond expire test attempts: $j" } - list $a $b - } {somevalue {}} + assert_equal $a {somevalue} + assert_equal $b {} + } test "PSETEX can set sub-second expires" { # This test is very likely to do a false positive if the server is diff --git a/tests/unit/info.tcl b/tests/unit/info.tcl index 759e5bc0b..2f3ad8d6f 100644 --- a/tests/unit/info.tcl +++ b/tests/unit/info.tcl @@ -274,5 +274,68 @@ start_server {tags {"info" "external:skip"}} { $rd close } + test {stats: eventloop metrics} { + set info1 [r info stats] + set cycle1 [getInfoProperty $info1 eventloop_cycles] + set el_sum1 [getInfoProperty $info1 eventloop_duration_sum] + set cmd_sum1 [getInfoProperty $info1 eventloop_duration_cmd_sum] + assert_morethan $cycle1 0 + assert_morethan $el_sum1 0 + assert_morethan $cmd_sum1 0 + after 110 ;# default hz is 10, wait for a cron tick. + set info2 [r info stats] + set cycle2 [getInfoProperty $info2 eventloop_cycles] + set el_sum2 [getInfoProperty $info2 eventloop_duration_sum] + set cmd_sum2 [getInfoProperty $info2 eventloop_duration_cmd_sum] + assert_morethan $cycle2 $cycle1 + assert_lessthan $cycle2 [expr $cycle1+10] ;# we expect 2 or 3 cycles here, but allow some tolerance + assert_morethan $el_sum2 $el_sum1 + assert_lessthan $el_sum2 [expr $el_sum1+5000] ;# we expect roughly 100ms here, but allow some tolerance + assert_morethan $cmd_sum2 $cmd_sum1 + assert_lessthan $cmd_sum2 [expr $cmd_sum1+3000] ;# we expect about tens of ms here, but allow some tolerance + } + + test {stats: instantaneous metrics} { + r config resetstat + after 1600 ;# hz is 10, wait for 16 cron tick so that sample array is fulfilled + set value [s instantaneous_eventloop_cycles_per_sec] + assert_morethan $value 0 + assert_lessthan $value 15 ;# default hz is 10 + set value [s instantaneous_eventloop_duration_usec] + assert_morethan $value 0 + assert_lessthan $value 22000 ;# default hz is 10, so duration < 1000 / 10, allow some tolerance + } + + test {stats: debug metrics} { + # make sure debug info is hidden + set info [r info] + assert_equal [getInfoProperty $info eventloop_duration_aof_sum] {} + set info_all [r info all] + assert_equal [getInfoProperty $info_all eventloop_duration_aof_sum] {} + + set info1 [r info debug] + + set aof1 [getInfoProperty $info1 eventloop_duration_aof_sum] + assert {$aof1 >= 0} + set cron1 [getInfoProperty $info1 eventloop_duration_cron_sum] + assert {$cron1 > 0} + set cycle_max1 [getInfoProperty $info1 eventloop_cmd_per_cycle_max] + assert {$cycle_max1 > 0} + set duration_max1 [getInfoProperty $info1 eventloop_duration_max] + assert {$duration_max1 > 0} + + after 110 ;# hz is 10, wait for a cron tick. + set info2 [r info debug] + + set aof2 [getInfoProperty $info2 eventloop_duration_aof_sum] + assert {$aof2 >= $aof1} ;# AOF is disabled, we expect $aof2 == $aof1, but allow some tolerance. + set cron2 [getInfoProperty $info2 eventloop_duration_cron_sum] + assert_morethan $cron2 $cron1 + set cycle_max2 [getInfoProperty $info2 eventloop_cmd_per_cycle_max] + assert {$cycle_max2 >= $cycle_max1} + set duration_max2 [getInfoProperty $info2 eventloop_duration_max] + assert {$duration_max2 >= $duration_max1} + } + } } diff --git a/tests/unit/introspection-2.tcl b/tests/unit/introspection-2.tcl index a0cf0c30f..52a13edf7 100644 --- a/tests/unit/introspection-2.tcl +++ b/tests/unit/introspection-2.tcl @@ -118,6 +118,10 @@ start_server {tags {"introspection"}} { assert_match {*calls=1,*} [cmdstat geoadd] } {} {needs:config-resetstat} + test {COMMAND COUNT get total number of Redis commands} { + assert_morethan [r command count] 0 + } + test {COMMAND GETKEYS GET} { assert_equal {key} [r command getkeys get key] } diff --git a/tests/unit/introspection.tcl b/tests/unit/introspection.tcl index 4452de6b1..76d56ee65 100644 --- a/tests/unit/introspection.tcl +++ b/tests/unit/introspection.tcl @@ -340,10 +340,10 @@ start_server {tags {"introspection"}} { r client info } {*lib-name=redis.py lib-ver=1.2.3*} - test {RESET doesn NOT clean library name} { + test {RESET does NOT clean library name} { r reset r client info - } {*lib-name=redis.py*} + } {*lib-name=redis.py*} {needs:reset} test {CLIENT SETINFO can clear library name} { r CLIENT SETINFO lib-name "" @@ -362,18 +362,13 @@ start_server {tags {"introspection"}} { assert_match [r config get save] {save {100 100}} } - # First "save" keyword in default config file - start_server {config "default.conf"} { - assert_match [r config get save] {save {900 1}} - } - # First "save" keyword appends default from config file - start_server {config "default.conf" args {--save 100 100}} { + start_server {config "default.conf" overrides {save {900 1}} args {--save 100 100}} { assert_match [r config get save] {save {900 1 100 100}} } # Empty "save" keyword resets all - start_server {config "default.conf" args {--save {}}} { + start_server {config "default.conf" overrides {save {900 1}} args {--save {}}} { assert_match [r config get save] {save {}} } } {} {external:skip} @@ -789,7 +784,7 @@ start_server {config "minimal.conf" tags {"introspection external:skip"} overrid } test {config during loading} { - start_server [list overrides [list key-load-delay 50 loading-process-events-interval-bytes 1024 rdbcompression no]] { + start_server [list overrides [list key-load-delay 50 loading-process-events-interval-bytes 1024 rdbcompression no save "900 1"]] { # create a big rdb that will take long to load. it is important # for keys to be big since the server processes events only once in 2mb. # 100mb of rdb, 100k keys will load in more than 5 seconds diff --git a/tests/unit/maxmemory.tcl b/tests/unit/maxmemory.tcl index 564250f2e..54aba6715 100644 --- a/tests/unit/maxmemory.tcl +++ b/tests/unit/maxmemory.tcl @@ -350,7 +350,7 @@ proc test_slave_buffers {test_name cmd_count payload_len limit_memory pipeline} # put the slave to sleep set rd_slave [redis_deferring_client] - exec kill -SIGSTOP $slave_pid + pause_process $slave_pid # send some 10mb worth of commands that don't increase the memory usage if {$pipeline == 1} { @@ -399,7 +399,7 @@ proc test_slave_buffers {test_name cmd_count payload_len limit_memory pipeline} } # unfreeze slave process (after the 'test' succeeded or failed, but before we attempt to terminate the server - exec kill -SIGCONT $slave_pid + resume_process $slave_pid } } } diff --git a/tests/unit/moduleapi/cluster.tcl b/tests/unit/moduleapi/cluster.tcl index 43356f77d..807508387 100644 --- a/tests/unit/moduleapi/cluster.tcl +++ b/tests/unit/moduleapi/cluster.tcl @@ -132,7 +132,7 @@ start_cluster 3 0 [list config_lines $modules] { test "Kill a cluster node and wait for fail state" { # kill node3 in cluster - exec kill -SIGSTOP $node3_pid + pause_process $node3_pid wait_for_condition 1000 50 { [CI 0 cluster_state] eq {fail} && @@ -158,7 +158,7 @@ start_cluster 3 0 [list config_lines $modules] { assert_error "ERR Can not execute a command 'set' while the cluster is down" {$node1 do_rm_call set x 1} } - exec kill -SIGCONT $node3_pid + resume_process $node3_pid $node1_rd close $node2_rd close } diff --git a/tests/unit/moduleapi/misc.tcl b/tests/unit/moduleapi/misc.tcl index 6bf7b8c2a..9b0989149 100644 --- a/tests/unit/moduleapi/misc.tcl +++ b/tests/unit/moduleapi/misc.tcl @@ -1,6 +1,6 @@ set testmodule [file normalize tests/modules/misc.so] -start_server {tags {"modules"}} { +start_server {overrides {save {900 1}} tags {"modules"}} { r module load $testmodule test {test RM_Call} { diff --git a/tests/unit/moduleapi/rdbloadsave.tcl b/tests/unit/moduleapi/rdbloadsave.tcl new file mode 100644 index 000000000..9319c9385 --- /dev/null +++ b/tests/unit/moduleapi/rdbloadsave.tcl @@ -0,0 +1,200 @@ +set testmodule [file normalize tests/modules/rdbloadsave.so] + +start_server {tags {"modules"}} { + r module load $testmodule + + test "Module rdbloadsave sanity" { + r test.sanity + + # Try to load non-existing file + assert_error {*No such file or directory*} {r test.rdbload sanity.rdb} + + r set x 1 + assert_equal OK [r test.rdbsave sanity.rdb] + + r flushdb + assert_equal OK [r test.rdbload sanity.rdb] + assert_equal 1 [r get x] + } + + test "Module rdbloadsave test with pipelining" { + r config set save "" + r config set loading-process-events-interval-bytes 1024 + r config set key-load-delay 50 + r flushdb + + populate 3000 a 1024 + r set x 111 + assert_equal [r dbsize] 3001 + + assert_equal OK [r test.rdbsave blabla.rdb] + r flushdb + assert_equal [r dbsize] 0 + + # Send commands with pipeline. First command will call RM_RdbLoad() in + # the command callback. While loading RDB, Redis can go to networking to + # reply -LOADING. By sending commands in pipeline, we verify it doesn't + # cause a problem. + # e.g. Redis won't try to process next message of the current client + # while it is in the command callback for that client . + set rd1 [redis_deferring_client] + $rd1 test.rdbload blabla.rdb + + wait_for_condition 50 100 { + [s loading] eq 1 + } else { + fail "Redis did not start loading or loaded RDB too fast" + } + + $rd1 get x + $rd1 dbsize + + assert_equal OK [$rd1 read] + assert_equal 111 [$rd1 read] + assert_equal 3001 [$rd1 read] + r flushdb + r config set key-load-delay 0 + } + + test "Module rdbloadsave with aof" { + r config set save "" + + # Enable the AOF + r config set appendonly yes + r config set auto-aof-rewrite-percentage 0 ; # Disable auto-rewrite. + waitForBgrewriteaof r + + r set k v1 + assert_equal OK [r test.rdbsave aoftest.rdb] + + r set k v2 + r config set rdb-key-save-delay 10000000 + r bgrewriteaof + + # RM_RdbLoad() should kill aof fork + assert_equal OK [r test.rdbload aoftest.rdb] + + wait_for_condition 50 100 { + [string match {*Killing*AOF*child*} [exec tail -20 < [srv 0 stdout]]] + } else { + fail "Can't find 'Killing AOF child' in recent log lines" + } + + # Verify the value in the loaded rdb + assert_equal v1 [r get k] + + r flushdb + r config set rdb-key-save-delay 0 + r config set appendonly no + } + + test "Module rdbloadsave with bgsave" { + r flushdb + r config set save "" + + r set k v1 + assert_equal OK [r test.rdbsave bgsave.rdb] + + r set k v2 + r config set rdb-key-save-delay 500000 + r bgsave + + # RM_RdbLoad() should kill RDB fork + assert_equal OK [r test.rdbload bgsave.rdb] + + wait_for_condition 10 1000 { + [string match {*Background*saving*terminated*} [exec tail -20 < [srv 0 stdout]]] + } else { + fail "Can't find 'Background saving terminated' in recent log lines" + } + + assert_equal v1 [r get k] + r flushall + waitForBgsave r + r config set rdb-key-save-delay 0 + } + + test "Module rdbloadsave calls rdbsave in a module fork" { + r flushdb + r config set save "" + r config set rdb-key-save-delay 500000 + + r set k v1 + + # Module will call RM_Fork() before calling RM_RdbSave() + assert_equal OK [r test.rdbsave_fork rdbfork.rdb] + assert_equal [s module_fork_in_progress] 1 + + wait_for_condition 10 1000 { + [status r module_fork_in_progress] == "0" + } else { + fail "Module fork didn't finish" + } + + r set k v2 + assert_equal OK [r test.rdbload rdbfork.rdb] + assert_equal v1 [r get k] + + r config set rdb-key-save-delay 0 + } + + test "Unload the module - rdbloadsave" { + assert_equal {OK} [r module unload rdbloadsave] + } + + tags {repl} { + test {Module rdbloadsave on master and replica} { + 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] + + $master set x 10000 + + # Start the replication process... + $replica replicaof $master_host $master_port + + wait_for_condition 100 100 { + [status $master sync_full] == 1 + } else { + fail "Master <-> Replica didn't start the full sync" + } + + # RM_RdbSave() is allowed on replicas + assert_equal OK [$replica test.rdbsave rep.rdb] + + # RM_RdbLoad() is not allowed on replicas + assert_error {*supported*} {$replica test.rdbload rep.rdb} + + assert_equal OK [$master test.rdbsave master.rdb] + $master set x 20000 + + wait_for_condition 100 100 { + [$replica get x] == 20000 + } else { + fail "Replica didn't get the update" + } + + # Loading RDB on master will drop replicas + assert_equal OK [$master test.rdbload master.rdb] + + wait_for_condition 100 100 { + [status $master sync_full] == 2 + } else { + fail "Master <-> Replica didn't start the full sync" + } + + wait_for_condition 100 100 { + [$replica get x] == 10000 + } else { + fail "Replica didn't get the update" + } + } + } + } + } +} diff --git a/tests/unit/moduleapi/reply.tcl b/tests/unit/moduleapi/reply.tcl index 291253d3c..547be21c0 100644 --- a/tests/unit/moduleapi/reply.tcl +++ b/tests/unit/moduleapi/reply.tcl @@ -126,6 +126,17 @@ start_server {tags {"modules"}} { assert_match "An error" $e } + test "RESP$proto: RM_ReplyWithErrorFormat: error format reply" { + catch {r rw.error_format "An error: %s" foo} e + assert_match "ERR An error: foo" $e + + catch {r rw.error_format "-ERR An error: %s" foo2} e + assert_match "ERR An error: foo2" $e + + catch {r rw.error_format "-WRONGTYPE A type error: %s" foo3} e + assert_match "WRONGTYPE A type error: foo3" $e + } + r hello 2 } diff --git a/tests/unit/moduleapi/stream.tcl b/tests/unit/moduleapi/stream.tcl index 80c24ff6c..7ad1a3059 100644 --- a/tests/unit/moduleapi/stream.tcl +++ b/tests/unit/moduleapi/stream.tcl @@ -61,6 +61,23 @@ start_server {tags {"modules"}} { assert_equal $result $n } + test {Module stream XADD big fields doesn't create empty key} { + set original_proto [config_get_set proto-max-bulk-len 2147483647] ;#2gb + set original_query [config_get_set client-query-buffer-limit 2147483647] ;#2gb + + r del mystream + r write "*4\r\n\$10\r\nstream.add\r\n\$8\r\nmystream\r\n\$5\r\nfield\r\n" + catch { + write_big_bulk 1073741824 ;#1gb + } err + assert {$err eq "ERR StreamAdd failed"} + assert_equal 0 [r exists mystream] + + # restore defaults + r config set proto-max-bulk-len $original_proto + r config set client-query-buffer-limit $original_query + } {OK} {large-memory} + test {Module stream iterator} { r del mystream set streamid1 [r xadd mystream * item 1 value a] diff --git a/tests/unit/moduleapi/testrdb.tcl b/tests/unit/moduleapi/testrdb.tcl index 2545a8ad2..ae3036f70 100644 --- a/tests/unit/moduleapi/testrdb.tcl +++ b/tests/unit/moduleapi/testrdb.tcl @@ -61,13 +61,13 @@ tags "modules" { # 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] keep_persistence true] { + 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]] { + 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] } @@ -80,11 +80,11 @@ tags "modules" { # 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] keep_persistence true] { + 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]] { + start_server [list overrides [list loadmodule "$testmodule $test_case" "dir" $server_path "save" "900 1"]] { assert_equal "global2" [r testrdb.get.after] } } diff --git a/tests/unit/moduleapi/zset.tcl b/tests/unit/moduleapi/zset.tcl index 1c146eaf4..b6ab41d5f 100644 --- a/tests/unit/moduleapi/zset.tcl +++ b/tests/unit/moduleapi/zset.tcl @@ -14,6 +14,26 @@ start_server {tags {"modules"}} { assert_equal 0 [r exists k] } + test {Module zset add} { + r del k + # Check that failure does not create empty key + assert_error "ERR ZsetAdd failed" {r zset.add k nan hello} + assert_equal 0 [r exists k] + + r zset.add k 100 hello + assert_equal {hello 100} [r zrange k 0 -1 withscores] + } + + test {Module zset incrby} { + r del k + # Check that failure does not create empty key + assert_error "ERR ZsetIncrby failed" {r zset.incrby k hello nan} + assert_equal 0 [r exists k] + + r zset.incrby k hello 100 + assert_equal {hello 100} [r zrange k 0 -1 withscores] + } + test "Unload the module - zset" { assert_equal {OK} [r module unload zset] } diff --git a/tests/unit/multi.tcl b/tests/unit/multi.tcl index bdf398150..851e02247 100644 --- a/tests/unit/multi.tcl +++ b/tests/unit/multi.tcl @@ -891,14 +891,14 @@ start_server {tags {"multi"}} { set res [r read] assert_equal $res "+OK" set res [r read] - r readraw 1 + r readraw 0 set _ $res } {*CONFIG SET failed*} test "Flushall while watching several keys by one client" { r flushall - r mset a a b b - r watch b a + r mset a{t} a b{t} b + r watch b{t} a{t} r flushall r ping } diff --git a/tests/unit/querybuf.tcl b/tests/unit/querybuf.tcl index bbbea12f4..f4859dd27 100644 --- a/tests/unit/querybuf.tcl +++ b/tests/unit/querybuf.tcl @@ -5,7 +5,7 @@ proc client_idle_sec {name} { return $idle } -# Calculate query buffer memory of slave +# Calculate query buffer memory of client proc client_query_buffer {name} { set clients [split [r client list] "\r\n"] set c [lsearch -inline $clients *name=$name*] @@ -18,6 +18,9 @@ proc client_query_buffer {name} { } start_server {tags {"querybuf slow"}} { + # increase the execution frequency of clientsCron + r config set hz 100 + # The test will run at least 2s to check if client query # buffer will be resized when client idle 2s. test "query buffer resized correctly" { @@ -38,6 +41,9 @@ start_server {tags {"querybuf slow"}} { } test "query buffer resized correctly when not idle" { + # Pause cron to prevent premature shrinking (timing issue). + r debug pause-cron 1 + # Memory will increase by more than 32k due to client query buffer. set rd [redis_client] $rd client setname test_client @@ -49,6 +55,8 @@ start_server {tags {"querybuf slow"}} { set orig_test_client_qbuf [client_query_buffer test_client] assert {$orig_test_client_qbuf > 32768} + r debug pause-cron 0 + # Wait for qbuf to shrink due to lower peak set t [clock milliseconds] while true { @@ -62,5 +70,27 @@ start_server {tags {"querybuf slow"}} { # Validate qbuf shrunk but isn't 0 since we maintain room based on latest peak assert {[client_query_buffer test_client] > 0 && [client_query_buffer test_client] < $orig_test_client_qbuf} $rd close + } {0} {needs:debug} + + test "query buffer resized correctly with fat argv" { + set rd [redis_client] + $rd client setname test_client + $rd write "*3\r\n\$3\r\nset\r\n\$1\r\na\r\n\$1000000\r\n" + $rd flush + + after 20 + if {[client_query_buffer test_client] < 1000000} { + fail "query buffer should not be resized when client idle time smaller than 2s" + } + + # Check that the query buffer is resized after 2 sec + wait_for_condition 1000 10 { + [client_idle_sec test_client] >= 3 && [client_query_buffer test_client] < 1000000 + } else { + fail "query buffer should be resized when client idle time bigger than 2s" + } + + $rd close } + } diff --git a/tests/unit/shutdown.tcl b/tests/unit/shutdown.tcl index b419c83a1..7504851a1 100644 --- a/tests/unit/shutdown.tcl +++ b/tests/unit/shutdown.tcl @@ -30,7 +30,7 @@ start_server {tags {"shutdown external:skip"}} { } } -start_server {tags {"shutdown external:skip"}} { +start_server {tags {"shutdown external:skip"} overrides {save {900 1}}} { test {SHUTDOWN ABORT can cancel SIGTERM} { r debug pause-cron 1 set pid [s process_id] @@ -48,7 +48,7 @@ start_server {tags {"shutdown external:skip"}} { } # It will cost 2s (20 * 100ms) to dump rdb r config set rdb-key-save-delay 100000 - + set pid [s process_id] set temp_rdb [file join [lindex [r config get dir] 1] temp-${pid}.rdb] @@ -72,7 +72,7 @@ start_server {tags {"shutdown external:skip"}} { } } -start_server {tags {"shutdown external:skip"}} { +start_server {tags {"shutdown external:skip"} overrides {save {900 1}}} { set pid [s process_id] set dump_rdb [file join [lindex [r config get dir] 1] dump.rdb] @@ -107,3 +107,27 @@ start_server {tags {"shutdown external:skip"}} { exec rm -r $dump_rdb } } + + +start_server {tags {"shutdown external:skip"} overrides {appendonly no}} { + test {SHUTDOWN SIGTERM will abort if there's an initial AOFRW - default} { + r config set shutdown-on-sigterm default + r config set rdb-key-save-delay 10000000 + for {set i 0} {$i < 10} {incr i} { + r set $i $i + } + + r config set appendonly yes + wait_for_condition 1000 10 { + [s aof_rewrite_in_progress] eq 1 + } else { + fail "aof rewrite did not start in time" + } + + set pid [s process_id] + exec kill -SIGTERM $pid + wait_for_log_messages 0 {"*Writing initial AOF, can't exit*"} 0 1000 10 + + r config set shutdown-on-sigterm force + } +} diff --git a/tests/unit/slowlog.tcl b/tests/unit/slowlog.tcl index bc15c6411..3c547b924 100644 --- a/tests/unit/slowlog.tcl +++ b/tests/unit/slowlog.tcl @@ -24,8 +24,11 @@ start_server {tags {"slowlog"} overrides {slowlog-log-slower-than 1000000}} { } {10} test {SLOWLOG - GET optional argument to limit output len works} { - llength [r slowlog get 5] - } {5} + + assert_equal 5 [llength [r slowlog get 5]] + assert_equal 10 [llength [r slowlog get -1]] + assert_equal 10 [llength [r slowlog get 20]] + } test {SLOWLOG - RESET subcommand works} { r config set slowlog-log-slower-than 100000 @@ -39,7 +42,7 @@ start_server {tags {"slowlog"} overrides {slowlog-log-slower-than 1000000}} { set e [lindex [r slowlog get] 0] assert_equal [llength $e] 6 if {!$::external} { - assert_equal [lindex $e 0] 105 + assert_equal [lindex $e 0] 107 } assert_equal [expr {[lindex $e 2] > 100000}] 1 assert_equal [lindex $e 3] {debug sleep 0.2} diff --git a/tests/unit/type/hash.tcl b/tests/unit/type/hash.tcl index 17e3ba40b..4cff0f244 100644 --- a/tests/unit/type/hash.tcl +++ b/tests/unit/type/hash.tcl @@ -350,9 +350,16 @@ start_server {tags {"hash"}} { set _ $rv } {{{} {}} {{} {}} {{} {}}} - test {HMGET against wrong type} { + test {HMGET HRANDFIELD HGET HGETALL HDEL HINCRBY HINCRBYFLOAT HSTRLEN against wrong type} { r set wrongtype somevalue - assert_error "*wrong*" {r hmget wrongtype field1 field2} + assert_error "WRONGTYPE Operation against a key*" {r hmget wrongtype field1 field2} + assert_error "WRONGTYPE Operation against a key*" {r hrandfield wrongtype} + assert_error "WRONGTYPE Operation against a key*" {r hget wrongtype field1} + assert_error "WRONGTYPE Operation against a key*" {r hgetall wrongtype} + assert_error "WRONGTYPE Operation against a key*" {r hdel wrongtype field1} + assert_error "WRONGTYPE Operation against a key*" {r hincrby wrongtype field1 2} + assert_error "WRONGTYPE Operation against a key*" {r hincrbyfloat wrongtype field1 2.5} + assert_error "WRONGTYPE Operation against a key*" {r hstrlen wrongtype field1} } test {HMGET - small hash} { diff --git a/tests/unit/type/incr.tcl b/tests/unit/type/incr.tcl index a64f357ae..c09f2e8b2 100644 --- a/tests/unit/type/incr.tcl +++ b/tests/unit/type/incr.tcl @@ -13,6 +13,12 @@ start_server {tags {"incr"}} { r decr novar } {1} + test {DECR against key is not exist and incr} { + r del novar_not_exist + assert_equal {-1} [r decr novar_not_exist] + assert_equal {0} [r incr novar_not_exist] + } + test {INCR against key originally set with SET} { r set novar 100 r incr novar @@ -64,6 +70,11 @@ start_server {tags {"incr"}} { r decrby novar 17179869185 } {-1} + test {DECRBY against key is not exist} { + r del key_not_exist + assert_equal {-1} [r decrby key_not_exist 1] + } + test {INCR uses shared objects in the 0-9999 range} { r set foo -1 r incr foo diff --git a/tests/unit/type/list.tcl b/tests/unit/type/list.tcl index d970b0278..a57e5df3e 100644 --- a/tests/unit/type/list.tcl +++ b/tests/unit/type/list.tcl @@ -464,6 +464,7 @@ foreach {type large} [array get largevalue] { assert {[r LPOS mylist c RANK -1] == 7} assert {[r LPOS mylist c RANK -2] == 6} assert_error "*RANK can't be zero: use 1 to start from the first match, 2 from the second ... or use negative to start*" {r LPOS mylist c RANK 0} + assert_error "*value is out of range*" {r LPOS mylist c RANK -9223372036854775808} } test {LPOS COUNT option} { @@ -1416,6 +1417,15 @@ foreach {pop} {BLPOP BLMPOP_LEFT} { set e } {*ERR*syntax*error*} + test {LINSERT against non-list value error} { + r set k1 v1 + assert_error {WRONGTYPE Operation against a key holding the wrong kind of value*} {r linsert k1 after 0 0} + } + + test {LINSERT against non existing key} { + assert_equal 0 [r linsert not-a-key before 0 0] + } + foreach type {listpack quicklist} { foreach {num} {250 500} { if {$type == "quicklist"} { diff --git a/tests/unit/type/string.tcl b/tests/unit/type/string.tcl index b25a14f4c..68c360b97 100644 --- a/tests/unit/type/string.tcl +++ b/tests/unit/type/string.tcl @@ -151,6 +151,14 @@ start_server {tags {"string"}} { set ex } {*syntax*} + test "GETEX and GET expired key or not exist" { + r del foo + r set foo bar px 1 + after 2 + assert_equal {} [r getex foo] + assert_equal {} [r get foo] + } + test "GETEX no arguments" { set ex {} catch {r getex} ex @@ -438,6 +446,11 @@ start_server {tags {"string"}} { assert_equal "" [r getrange mykey 0 -1] } + test "GETRANGE against wrong key type" { + r lpush lkey1 "list" + assert_error {WRONGTYPE Operation against a key holding the wrong kind of value*} {r getrange lkey1 0 -1} + } + test "GETRANGE against string value" { r set mykey "Hello World" assert_equal "Hell" [r getrange mykey 0 3] @@ -474,6 +487,9 @@ start_server {tags {"string"}} { assert_equal "a" [r substr key 0 0] assert_equal "abcd" [r substr key 0 3] assert_equal "bcde" [r substr key -4 -1] + assert_equal "" [r substr key -1 -3] + assert_equal "" [r substr key 7 8] + assert_equal "" [r substr nokey 0 1] } if {[string match {*jemalloc*} [s mem_allocator]]} { diff --git a/tests/unit/type/zset.tcl b/tests/unit/type/zset.tcl index a52a77f24..d1da02778 100644 --- a/tests/unit/type/zset.tcl +++ b/tests/unit/type/zset.tcl @@ -755,6 +755,46 @@ start_server {tags {"zset"}} { assert_equal 0 [r exists zset] } + test "ZREMRANGEBYLEX basics - $encoding" { + proc remrangebylex {min max} { + create_default_lex_zset + assert_equal 1 [r exists zset] + r zremrangebylex zset $min $max + } + + # inclusive range + assert_equal 3 [remrangebylex - \[cool] + assert_equal {down elephant foo great hill omega} [r zrange zset 0 -1] + assert_equal 3 [remrangebylex \[bar \[down] + assert_equal {alpha elephant foo great hill omega} [r zrange zset 0 -1] + assert_equal 3 [remrangebylex \[g +] + assert_equal {alpha bar cool down elephant foo} [r zrange zset 0 -1] + assert_equal 6 [r zcard zset] + + # exclusive range + assert_equal 2 [remrangebylex - (cool] + assert_equal {cool down elephant foo great hill omega} [r zrange zset 0 -1] + assert_equal 1 [remrangebylex (bar (down] + assert_equal {alpha bar down elephant foo great hill omega} [r zrange zset 0 -1] + assert_equal 2 [remrangebylex (great +] + assert_equal {alpha bar cool down elephant foo great} [r zrange zset 0 -1] + assert_equal 7 [r zcard zset] + + # inclusive and exclusive + assert_equal 0 [remrangebylex (az (b] + assert_equal {alpha bar cool down elephant foo great hill omega} [r zrange zset 0 -1] + assert_equal 0 [remrangebylex (z +] + assert_equal {alpha bar cool down elephant foo great hill omega} [r zrange zset 0 -1] + assert_equal 0 [remrangebylex - \[aaaa] + assert_equal {alpha bar cool down elephant foo great hill omega} [r zrange zset 0 -1] + assert_equal 9 [r zcard zset] + + # destroy when empty + assert_equal 9 [remrangebylex - +] + assert_equal 0 [r zcard zset] + assert_equal 0 [r exists zset] + } + test "ZUNIONSTORE against non-existing key doesn't set destination - $encoding" { r del zseta{t} assert_equal 0 [r zunionstore dst_key{t} 1 zseta{t}] diff --git a/tests/unit/wait.tcl b/tests/unit/wait.tcl index 08a7a71f6..8c6010afb 100644 --- a/tests/unit/wait.tcl +++ b/tests/unit/wait.tcl @@ -47,25 +47,25 @@ start_server {} { } test {WAIT should not acknowledge 1 additional copy if slave is blocked} { - exec kill -SIGSTOP $slave_pid + pause_process $slave_pid $master set foo 0 $master incr foo $master incr foo $master incr foo assert {[$master wait 1 1000] == 0} - exec kill -SIGCONT $slave_pid + resume_process $slave_pid assert {[$master wait 1 1000] == 1} } test {WAIT implicitly blocks on client pause since ACKs aren't sent} { - exec kill -SIGSTOP $slave_pid + pause_process $slave_pid $master multi $master incr foo $master client pause 10000 write $master exec assert {[$master wait 1 1000] == 0} $master client unpause - exec kill -SIGCONT $slave_pid + resume_process $slave_pid assert {[$master wait 1 1000] == 1} } @@ -73,7 +73,7 @@ start_server {} { set rd [redis_deferring_client -1] set rd2 [redis_deferring_client -1] - exec kill -SIGSTOP $slave_pid + pause_process $slave_pid $rd incr foo $rd read @@ -85,7 +85,7 @@ start_server {} { $rd2 wait 1 0 wait_for_blocked_clients_count 2 100 10 -1 - exec kill -SIGCONT $slave_pid + resume_process $slave_pid assert_equal [$rd read] {1} assert_equal [$rd2 read] {1} @@ -175,7 +175,49 @@ tags {"wait aof network external:skip"} { $replica config set appendfsync everysec test {WAITAOF replica copy everysec} { + $replica config set appendfsync everysec + waitForBgrewriteaof $replica ;# Make sure there is no AOFRW + + $master incr foo + assert_equal [$master waitaof 0 1 0] {1 1} + } + + test {WAITAOF replica copy everysec with AOFRW} { + $replica config set appendfsync everysec + + # When we trigger an AOFRW, a fsync is triggered when closing the old INCR file, + # so with the everysec, we will skip that second of fsync, and in the next second + # after that, we will eventually do the fsync. + $replica bgrewriteaof + waitForBgrewriteaof $replica + + $master incr foo + assert_equal [$master waitaof 0 1 0] {1 1} + } + + test {WAITAOF replica copy everysec with slow AOFRW} { + $replica config set appendfsync everysec + $replica config set rdb-key-save-delay 1000000 ;# 1 sec + + $replica bgrewriteaof + + $master incr foo + assert_equal [$master waitaof 0 1 0] {1 1} + + $replica config set rdb-key-save-delay 0 + waitForBgrewriteaof $replica + } + + test {WAITAOF replica copy everysec->always with AOFRW} { + $replica config set appendfsync everysec + + # Try to fit all of them in the same round second, although there's no way to guarantee + # that, it can be done on fast machine. In any case, the test shouldn't fail either. + $replica bgrewriteaof $master incr foo + waitForBgrewriteaof $replica + $replica config set appendfsync always + assert_equal [$master waitaof 0 1 0] {1 1} } @@ -187,10 +229,10 @@ tags {"wait aof network external:skip"} { } test {WAITAOF replica copy if replica is blocked} { - exec kill -SIGSTOP $replica_pid + pause_process $replica_pid $master incr foo assert_equal [$master waitaof 0 1 50] {1 0} ;# exits on timeout - exec kill -SIGCONT $replica_pid + resume_process $replica_pid assert_equal [$master waitaof 0 1 0] {1 1} } @@ -198,7 +240,7 @@ tags {"wait aof network external:skip"} { set rd [redis_deferring_client -1] set rd2 [redis_deferring_client -1] - exec kill -SIGSTOP $replica_pid + pause_process $replica_pid $rd incr foo $rd read @@ -210,7 +252,7 @@ tags {"wait aof network external:skip"} { $rd2 waitaof 0 1 0 wait_for_blocked_clients_count 2 100 10 -1 - exec kill -SIGCONT $replica_pid + resume_process $replica_pid assert_equal [$rd read] {1 1} assert_equal [$rd2 read] {1 1} @@ -396,7 +438,7 @@ start_server {} { waitForBgrewriteaof $replica1 waitForBgrewriteaof $replica2 - exec kill -SIGSTOP $replica1_pid + pause_process $replica1_pid $rd incr foo $rd read @@ -409,7 +451,7 @@ start_server {} { wait_for_blocked_clients_count 2 - exec kill -SIGCONT $replica1_pid + resume_process $replica1_pid # WAIT will unblock the client first. assert_equal [$rd2 read] {2} |