From 3f160d0638e9c029e299c4289256bcedf77d0714 Mon Sep 17 00:00:00 2001 From: Stav Bentov Date: Mon, 14 Jul 2025 06:32:58 +0000 Subject: [PATCH] Reduce duplication in hashexpire.tcl --- tests/unit/hashexpire.tcl | 466 ++++++++++++++++---------------------- 1 file changed, 190 insertions(+), 276 deletions(-) diff --git a/tests/unit/hashexpire.tcl b/tests/unit/hashexpire.tcl index 8bffeeef38..be329e7624 100644 --- a/tests/unit/hashexpire.tcl +++ b/tests/unit/hashexpire.tcl @@ -8,11 +8,83 @@ proc info_field {info field} { return [s field_name] } -proc assert_keyevent_pattern {rd event_type key} { - set event [$rd read] - assert_match "pmessage __keyevent@* __keyevent@*:$event_type $key" $event +proc get_short_expire_value {command} { + expr { + ($command eq "HEXPIRE" || $command eq "EX") ? 1 : + ($command eq "HPEXPIRE" || $command eq "PX") ? 10 : + ($command eq "HEXPIREAT" || $command eq "EXAT") ? [clock seconds] + 1 : + [clock milliseconds] + 10 + } +} + +proc get_long_expire_value {command} { + expr { + ($command eq "HEXPIRE" || $command eq "EX") ? 60000000 : + ($command eq "HPEXPIRE" || $command eq "PX") ? 60000000 : + ($command eq "HEXPIREAT" || $command eq "EXAT") ? [clock seconds] + 60000000 : + [clock milliseconds] + 60000000 + } +} + +proc get_longer_then_long_expire_value {command} { + expr { + ($command eq "HEXPIRE" || $command eq "EX") ? 1200000000 : + ($command eq "HPEXPIRE" || $command eq "PX") ? 1200000000 : + ($command eq "HEXPIREAT" || $command eq "EXAT") ? [clock seconds] + 1200000000 : + [clock milliseconds] + 1200000000 + } +} + +proc get_past_zero_expire_value {command} { + expr { + ($command eq "HEXPIRE" || $command eq "EX") ? 0 : + ($command eq "HPEXPIRE" || $command eq "PX") ? 0 : + ($command eq "HEXPIREAT" || $command eq "EXAT") ? [clock seconds] - 200000 : + [clock milliseconds] - 200000 + } +} + +proc get_check_ttl_command {command} { + if {$command eq "EX"} { + return "HTTL" + } elseif {$command eq "PX"} { + return "HPTTL" + } elseif {$command eq "EXAT"} { + return "HEXPIRETIME" + } else { + return "HPEXPIRETIME" + } } +proc assert_keyevent_patterns {rd key args} { + foreach event_type $args { + set event [$rd read] + assert_match "pmessage __keyevent@* __keyevent@*:$event_type $key" $event + } +} + +proc setup_replication_test {primary replica primary_host primary_port} { + $primary FLUSHALL + $replica replicaof $primary_host $primary_port + wait_for_condition 50 100 { + [lindex [$replica role] 0] eq {slave} && + [string match {*master_link_status:up*} [$replica info replication]] + } else { + fail "Can't turn the instance into a replica" + } + set primary_initial_expired [info_field [$primary info stats] expired_subkeys] + set replica_initial_expired [info_field [$replica info stats] expired_subkeys] + return [list $primary_initial_expired $replica_initial_expired] +} + +proc setup_single_keyspace_notification {r} { + $r config set notify-keyspace-events KEA + set rd [valkey_deferring_client] + assert_equal {1} [psubscribe $rd __keyevent@*] + return $rd +} + + start_server {tags {"hashexpire"}} { ####### Valid scenarios tests ####### foreach command {EX PX EXAT PXAT} { @@ -21,20 +93,8 @@ start_server {tags {"hashexpire"}} { r DEBUG SET-ACTIVE-EXPIRE no r HSET myhash f1 v1 - # Configuration dictionary mapping expiry commands to their test parameters: - # - time: expiry value (seconds/milliseconds or absolute timestamp) - # - wait: milliseconds to wait before checking expiration - # - cmd: command to verify the TTL/expiry time - set config [dict create \ - EX [list time 1 wait 1100 cmd HTTL] \ - PX [list time 100 wait 150 cmd HPTTL] \ - EXAT [list time [expr {[clock seconds] + 1}] wait 1100 cmd HEXPIRETIME] \ - PXAT [list time [expr {[clock milliseconds] + 100}] wait 150 cmd HPEXPIRETIME] \ - ] - set params [dict get $config $command] - set expire_time [dict get $params time] - set wait_time [dict get $params wait] - set ttl_cmd [dict get $params cmd] + set ttl_cmd [get_check_ttl_command $command] + set expire_time [get_short_expire_value $command] # Verify HGETEX command assert_equal "v1" [r HGETEX myhash $command $expire_time FIELDS 1 f1] @@ -46,7 +106,7 @@ start_server {tags {"hashexpire"}} { } else { assert_morethan $expire_result 0 } - after $wait_time + after 1100 assert_equal "" [r HGET myhash f1] # Re-enable active expiry r DEBUG SET-ACTIVE-EXPIRE yes @@ -56,17 +116,8 @@ start_server {tags {"hashexpire"}} { r FLUSHALL r HSET myhash f1 v1 f3 v3 - set config [dict create \ - EX [list time 2000000] \ - PX [list time 2000000] \ - EXAT [list time [expr {[clock seconds] + 2000000}]] \ - PXAT [list time [expr {[clock milliseconds] + 20000000}]] \ - ] - set params [dict get $config $command] - set expire_time [dict get $params time] - # HGETEX on exist/non-exist fields - assert_equal "v1 {} v3" [r HGETEX myhash $command $expire_time FIELDS 3 f1 f2 f3] + assert_equal "v1 {} v3" [r HGETEX myhash $command [get_long_expire_value $command] FIELDS 3 f1 f2 f3] # Verification checks (f2 should not be created) assert_equal "" [r HGET myhash f2] @@ -80,29 +131,21 @@ start_server {tags {"hashexpire"}} { r DEBUG SET-ACTIVE-EXPIRE no r HSET myhash f1 v1 f2 v2 - set config [dict create \ - EX [list time 1 wait 1100 check_cmd HTTL] \ - PX [list time 100 wait 150 check_cmd HPTTL] \ - EXAT [list time [expr {[clock seconds] + 1}] wait 1100 check_cmd HEXPIRETIME] \ - PXAT [list time [expr {[clock milliseconds] + 100}] wait 150 check_cmd HPEXPIRETIME] \ - ] - set params [dict get $config $command] - set expire_time [dict get $params time] - set wait_time [dict get $params wait] - set check_cmd [dict get $params check_cmd] + set ttl_cmd [get_check_ttl_command $command] + set expire_time [get_short_expire_value $command] assert_equal "v1 v2" [r HGETEX myhash $command $expire_time FIELDS 2 f1 f2] # Verify expiration if {[regexp "AT$" $command]} { - assert_equal $expire_time [r $check_cmd myhash FIELDS 1 f1] - assert_equal $expire_time [r $check_cmd myhash FIELDS 1 f2] + assert_equal $expire_time [r $ttl_cmd myhash FIELDS 1 f1] + assert_equal $expire_time [r $ttl_cmd myhash FIELDS 1 f2] } else { - assert_morethan [r $check_cmd myhash FIELDS 1 f1] 0 - assert_morethan [r $check_cmd myhash FIELDS 1 f2] 0 + assert_morethan [r $ttl_cmd myhash FIELDS 1 f1] 0 + assert_morethan [r $ttl_cmd myhash FIELDS 1 f2] 0 } - after $wait_time + after 1100 assert_equal "" [r HGET myhash f1] assert_equal "" [r HGET myhash f2] # Re-enable active expiry @@ -114,20 +157,12 @@ start_server {tags {"hashexpire"}} { r HSET myhash f1 v1 r HSETEX myhash EX 10000 FIELDS 1 f2 v2 - set config [dict create \ - EX [list time 1 cmd HTTL check_cmd HTTL] \ - PX [list time 100 cmd HPTTL check_cmd HPTTL] \ - EXAT [list time [expr {[clock seconds] + 1}] cmd HTTL check_cmd HEXPIRETIME] \ - PXAT [list time [expr {[clock milliseconds] + 100}] cmd HPTTL check_cmd HPEXPIRETIME] \ - ] - set params [dict get $config $command] - set expire_time [dict get $params time] - set ttl_cmd [dict get $params cmd] - set check_cmd [dict get $params check_cmd] + set ttl_cmd [get_check_ttl_command $command] + set expire_time [get_short_expire_value $command] assert_equal "v1" [r HGETEX myhash $command $expire_time FIELDS 1 f1] if {[regexp "AT$" $command]} { - assert_equal $expire_time [r $check_cmd myhash FIELDS 1 f1] + assert_equal $expire_time [r $ttl_cmd myhash FIELDS 1 f1] } else { assert_morethan [r $ttl_cmd myhash FIELDS 1 f1] 0 } @@ -140,49 +175,38 @@ start_server {tags {"hashexpire"}} { test "HGETEX $command on non-exist field" { r FLUSHALL - r HSET myhash f1 v1 - - set config [dict create \ - EX [list time 1] \ - PX [list time 100] \ - EXAT [list time [expr {[clock seconds] + 1}]] \ - PXAT [list time [expr {[clock milliseconds] + 100}]] \ - ] - set params [dict get $config $command] - set expire_time [dict get $params time] - - assert_equal {{}} [r HGETEX myhash $command $expire_time FIELDS 1 f2] + r HSET myhash f1 v1 + assert_equal {{}} [r HGETEX myhash $command [get_short_expire_value $command] FIELDS 1 f2] } test "HGETEX $command on non-exist key" { r FLUSHALL - - set config [dict create \ - EX [list time 100000] \ - PX [list time 10000000] \ - EXAT [list time [expr {[clock seconds] + 10000}]] \ - PXAT [list time [expr {[clock milliseconds] + 100000}]] \ - ] - set params [dict get $config $command] - set expire_time [dict get $params time] - - assert_equal "" [r HGETEX myhash $command $expire_time FIELDS 1 f2] + assert_equal "" [r HGETEX myhash $command [get_long_expire_value $command] FIELDS 1 f2] } test "HGETEX $command with duplicate field names" { r FLUSHALL r HSET myhash f1 v1 - - set config [dict create \ - EX [list time 10000] \ - PX [list time 10000] \ - EXAT [list time [expr {[clock seconds] + 10000}]] \ - PXAT [list time [expr {[clock milliseconds] + 100000}]] \ - ] - set params [dict get $config $command] - set expire_time [dict get $params time] - - assert_equal "v1 v1" [r HGETEX myhash $command $expire_time FIELDS 2 f1 f1] + assert_equal "v1 v1" [r HGETEX myhash $command [get_long_expire_value $command] FIELDS 2 f1 f1] + } + + + test "HGETEX $command overwrites existing field TTL with bigger value" { + r FLUSHALL + r HSETEX myhash $command [get_long_expire_value $command] FIELDS 1 f1 v1 + set old_ttl [r HTTL myhash FIELDS 1 f1] + r HGETEX myhash $command [get_longer_then_long_expire_value $command] FIELDS 1 f1 + set new_ttl [r HTTL myhash FIELDS 1 f1] + assert {$new_ttl > $old_ttl} + } + + test "HGETEX $command overwrites existing field TTL with smaller value" { + r FLUSHALL + r HSETEX myhash $command [get_long_expire_value $command] FIELDS 1 f1 v1 + set old_ttl [r HTTL myhash FIELDS 1 f1] + r HGETEX myhash $command [get_short_expire_value $command] FIELDS 1 f1 + set new_ttl [r HTTL myhash FIELDS 1 f1] + assert {$new_ttl <= $old_ttl} } } @@ -200,59 +224,12 @@ start_server {tags {"hashexpire"}} { test "HGETEX $command with past expiry" { r FLUSHALL r HSET myhash f1 v1 - if {$command eq "EXAT"} { - set expire_time [expr {[clock seconds] - 100000}] - } else { - set expire_time [expr {[clock milliseconds] - 100000}] - } - assert_equal "v1" [r HGETEX myhash $command $expire_time FIELDS 1 f1] + assert_equal "v1" [r HGETEX myhash $command [get_past_zero_expire_value $command] FIELDS 1 f1] assert_equal "" [r HGET myhash f1] assert_equal -2 [r HTTL myhash FIELDS 1 f1] } } - foreach command {EX PX EXAT PXAT} { - test "HGETEX $command overwrites existing field TTL with bigger value" { - r FLUSHALL - set config [dict create \ - EX [list setup_cmd EX setup_val 100000 bigger_val 200000] \ - PX [list setup_cmd PX setup_val 100000000 bigger_val 200000000] \ - EXAT [list setup_cmd EX setup_val 100000 bigger_val [expr {[clock seconds] + 200000}]] \ - PXAT [list setup_cmd PX setup_val 100000000 bigger_val [expr {[clock milliseconds] + 200000000}]] \ - ] - set params [dict get $config $command] - set setup_cmd [dict get $params setup_cmd] - set setup_val [dict get $params setup_val] - set bigger_val [dict get $params bigger_val] - - r HSETEX myhash $setup_cmd $setup_val FIELDS 1 f1 v1 - set old_ttl [r HTTL myhash FIELDS 1 f1] - r HGETEX myhash $command $bigger_val FIELDS 1 f1 - set new_ttl [r HTTL myhash FIELDS 1 f1] - assert {$new_ttl > $old_ttl} - } - - test "HGETEX $command overwrites existing field TTL with smaller value" { - r FLUSHALL - set config [dict create \ - EX [list setup_cmd EX setup_val 100000 smaller_val 50000] \ - PX [list setup_cmd PX setup_val 100000000 smaller_val 50000000] \ - EXAT [list setup_cmd EX setup_val 100000 smaller_val [expr {[clock seconds] + 50000}]] \ - PXAT [list setup_cmd PX setup_val 100000000 smaller_val [expr {[clock milliseconds] + 50000000}]] \ - ] - set params [dict get $config $command] - set setup_cmd [dict get $params setup_cmd] - set setup_val [dict get $params setup_val] - set smaller_val [dict get $params smaller_val] - - r HSETEX myhash $setup_cmd $setup_val FIELDS 1 f1 v1 - set old_ttl [r HTTL myhash FIELDS 1 f1] - r HGETEX myhash $command $smaller_val FIELDS 1 f1 - set new_ttl [r HTTL myhash FIELDS 1 f1] - assert {$new_ttl <= $old_ttl} - } - } - test {HGETEX - verify no change when field does not exist} { r FLUSHALL r HSET myhash f1 v1 @@ -276,12 +253,7 @@ start_server {tags {"hashexpire"}} { } # Common error scenarios for all commands - foreach {cmd ttl_val} [list \ - EX 60 \ - PX 60 \ - EXAT [expr {[clock seconds] + 100}] \ - PXAT [expr {[clock milliseconds] + 100}] \ - ] { + foreach cmd {EX PX EXAT PXAT} { test "HGETEX $cmd- missing TTL value" { r FLUSHALL r HSET myhash f1 v1 @@ -306,47 +278,46 @@ start_server {tags {"hashexpire"}} { test "HGETEX $cmd- missing FIELDS keyword" { r FLUSHALL r HSET myhash f1 v1 - catch {r HGETEX myhash $cmd $ttl_val 1 f1} e + catch {r HGETEX myhash $cmd [get_short_expire_value $cmd] 1 f1} e set e } {ERR *} test "HGETEX $cmd- wrong numfields count (too few fields)" { r FLUSHALL r HSET myhash f1 v1 f2 v2 - catch {r HGETEX myhash $cmd $ttl_val FIELDS 2 f1} e + catch {r HGETEX myhash $cmd [get_short_expire_value $cmd] FIELDS 2 f1} e set e } {ERR *} test "HGETEX $cmd- wrong numfields count (too many fields)" { r FLUSHALL r HSET myhash f1 v1 - catch {r HGETEX myhash $cmd $ttl_val FIELDS 1 f1 f2} e + catch {r HGETEX myhash $cmd [get_short_expire_value $cmd] FIELDS 1 f1 f2} e set e } {ERR *} test "HGETEX $cmd- key is wrong type (string instead of hash)" { r FLUSHALL r SET mystring "v1" - catch {r HGETEX mystring $cmd $ttl_val FIELDS 1 f1} e + catch {r HGETEX mystring $cmd [get_short_expire_value $cmd] FIELDS 1 f1} e set e } {WRONGTYPE Operation against a key holding the wrong kind of value} test "HGETEX $cmd with FIELDS 0" { r FLUSHALL - catch {r HGETEX myhash $cmd $ttl_val FIELDS 0} e + catch {r HGETEX myhash $cmd [get_short_expire_value $cmd] FIELDS 0} e set e } {ERR *} test "HGETEX $cmd with negative numfields" { r FLUSHALL - catch {r HGETEX myhash $cmd $ttl_val FIELDS -10} e + catch {r HGETEX myhash $cmd [get_short_expire_value $cmd] FIELDS -10} e set e } {ERR *} test "HGETEX $cmd with missing key" { r FLUSHALL - set expire [expr {[clock seconds] + 100}] - catch {r HGETEX $cmd $expire FIELDS 1 f1} e + catch {r HGETEX $cmd [get_short_expire_value $cmd] FIELDS 1 f1} e set e } {ERR *} } @@ -366,25 +337,15 @@ start_server {tags {"hashexpire"}} { r config set notify-keyspace-events KEA foreach command {EX PX EXAT PXAT} { - set config [dict create \ - EX [list time 6000000] \ - PX [list time 6000000] \ - EXAT [list time [expr {[clock seconds] + 6000000}]] \ - PXAT [list time [expr {[clock milliseconds] + 6000000}]] \ - ] - set params [dict get $config $command] - set expire_time [dict get $params time] - test "HGETEX $command generates hexpire keyspace notification" { r FLUSHALL r HSET myhash f1 v1 - set rd [valkey_deferring_client] - assert_equal {1} [psubscribe $rd __keyevent@*] + set rd [setup_single_keyspace_notification r] - r HGETEX myhash $command $expire_time FIELDS 1 f1 + r HGETEX myhash $command [get_long_expire_value $command] FIELDS 1 f1 - assert_keyevent_pattern $rd hexpire myhash + assert_keyevent_patterns $rd myhash hexpire $rd close } @@ -392,15 +353,14 @@ start_server {tags {"hashexpire"}} { r FLUSHALL r HSET myhash f1 v1 f2 v2 f3 v3 - set rd [valkey_deferring_client] - assert_equal {1} [psubscribe $rd __keyevent@*] + set rd [setup_single_keyspace_notification r] - r HGETEX myhash $command $expire_time FIELDS 3 f1 f2 f3 + r HGETEX myhash $command [get_long_expire_value $command] FIELDS 3 f1 f2 f3 - assert_keyevent_pattern $rd hexpire myhash + assert_keyevent_patterns $rd myhash hexpire # Verify no notification (getting hset and not hexpire) r HSET dummy dummy dummy - assert_keyevent_pattern $rd hset dummy + assert_keyevent_patterns $rd dummy hset $rd close } @@ -408,15 +368,14 @@ start_server {tags {"hashexpire"}} { r FLUSHALL r HSET myhash f1 v1 - set rd [valkey_deferring_client] - assert_equal {1} [psubscribe $rd __keyevent@*] + set rd [setup_single_keyspace_notification r] # This HGETEX targets a non-existent field, so no notification about hexpire should be emitted - r HGETEX myhash $command $expire_time FIELDS 1 f2 + r HGETEX myhash $command [get_long_expire_value $command] FIELDS 1 f2 # Verify no notification (getting hset and not hexpire) r HSET dummy dummy dummy - assert_keyevent_pattern $rd hset dummy + assert_keyevent_patterns $rd dummy hset $rd close } @@ -427,24 +386,15 @@ start_server {tags {"hashexpire"}} { r HSET myhash f1 v1 r HEXPIRE myhash 60 FIELDS 1 f1 - set rd [valkey_deferring_client] - assert_equal {1} [psubscribe $rd __keyevent@*] + set rd [setup_single_keyspace_notification r] r HGETEX myhash PERSIST FIELDS 1 f1 - assert_keyevent_pattern $rd hpersist myhash + assert_keyevent_patterns $rd myhash hpersist $rd close } foreach command {EX PX EXAT PXAT} { - set config [dict create \ - EX [list time 0] \ - PX [list time 0] \ - EXAT [list time [expr {[clock seconds] - 2000}]] \ - PXAT [list time [expr {[clock milliseconds] - 2000}]] \ - ] - set params [dict get $config $command] - set expire_time [dict get $params time] test "HGETEX $command 0/past time works correctly with 1 field" { r FLUSHALL @@ -454,15 +404,13 @@ start_server {tags {"hashexpire"}} { assert_equal 1 [r HLEN myhash] assert_match {1} [scan [regexp -inline {keys\=([\d]*)} [r info keyspace]] keys=%d] - set rd [valkey_deferring_client] - assert_equal {1} [psubscribe $rd __keyevent@*] + set rd [setup_single_keyspace_notification r] # Set field to expire immediately - r HGETEX myhash $command $expire_time FIELDS 1 f1 + r HGETEX myhash $command [get_past_zero_expire_value $command] FIELDS 1 f1 # Verify field and keys are deleted - assert_keyevent_pattern $rd hexpired myhash - assert_keyevent_pattern $rd del myhash + assert_keyevent_patterns $rd myhash hexpired del assert_equal -2 [r HTTL myhash FIELDS 1 f1] assert_equal 0 [r HLEN myhash] assert_equal 0 [r EXISTS myhash] @@ -479,15 +427,13 @@ start_server {tags {"hashexpire"}} { assert_equal 1 [r HLEN myhash] assert_match {1} [scan [regexp -inline {keys\=([\d]*)} [r info keyspace]] keys=%d] - set rd [valkey_deferring_client] - assert_equal {1} [psubscribe $rd __keyevent@*] + set rd [setup_single_keyspace_notification r] # Set field to expire immediately - r HGETEX myhash $command $expire_time FIELDS 1 f1 + r HGETEX myhash $command [get_past_zero_expire_value $command] FIELDS 1 f1 # Verify field and keys are deleted - assert_keyevent_pattern $rd hexpired myhash - assert_keyevent_pattern $rd del myhash + assert_keyevent_patterns $rd myhash hexpired del assert_equal -2 [r HTTL myhash FIELDS 1 f1] assert_equal 0 [r HLEN myhash] assert_equal 0 [r EXISTS myhash] @@ -504,14 +450,13 @@ start_server {tags {"hashexpire"}} { assert_equal 2 [r HLEN myhash] assert_match {1} [scan [regexp -inline {keys\=([\d]*)} [r info keyspace]] keys=%d] - set rd [valkey_deferring_client] - assert_equal {1} [psubscribe $rd __keyevent@*] + set rd [setup_single_keyspace_notification r] # Set field to expire immediately - r HGETEX myhash $command $expire_time FIELDS 1 f2 + r HGETEX myhash $command [get_past_zero_expire_value $command] FIELDS 1 f2 # Verify field and keys are deleted - assert_keyevent_pattern $rd hexpired myhash + assert_keyevent_patterns $rd myhash hexpired assert_equal -2 [r HTTL myhash FIELDS 1 f2] assert_equal 1 [r HLEN myhash] assert_equal 1 [r EXISTS myhash] @@ -529,14 +474,13 @@ start_server {tags {"hashexpire"}} { assert_equal 4 [r HLEN myhash] assert_match {1} [scan [regexp -inline {keys\=([\d]*)} [r info keyspace]] keys=%d] - set rd [valkey_deferring_client] - assert_equal {1} [psubscribe $rd __keyevent@*] + set rd [setup_single_keyspace_notification r] # Set field to expire immediately - r HGETEX myhash $command $expire_time FIELDS 1 f1 + r HGETEX myhash $command [get_past_zero_expire_value $command] FIELDS 1 f1 # Verify field and keys are deleted - assert_keyevent_pattern $rd hexpired myhash + assert_keyevent_patterns $rd myhash hexpired assert_equal -2 [r HTTL myhash FIELDS 1 f1] assert_equal 3 [r HLEN myhash] assert_equal 1 [r EXISTS myhash] @@ -1912,21 +1856,17 @@ start_server {tags {"hashexpire external:skip"}} { set replica_1_port [srv 0 port] test {Replication Primary -> R1} { - $primary FLUSHALL - ####### Replication setup ####### - $replica_1 replicaof $primary_host $primary_port - wait_for_condition 50 100 { - [lindex [$replica_1 role] 0] eq {slave} && - [string match {*master_link_status:up*} [$replica_1 info replication]] - } else { - fail "Can't turn the instance into a replica" - } + lassign [setup_replication_test $primary $replica_1 $primary_host $primary_port] primary_initial_expired replica_1_initial_expired # Initialize deferred clients and subscribe to keyspace notifications + foreach instance [list $primary $replica_1] { + $instance config set notify-keyspace-events KEA + } set rd_primary [valkey_deferring_client -1] set rd_replica_1 [valkey_deferring_client $replica_1_host $replica_1_port] - assert_equal {1} [psubscribe $rd_primary __keyevent@*] - assert_equal {1} [psubscribe $rd_replica_1 __keyevent@*] + foreach rd [list $rd_primary $rd_replica_1] { + assert_equal {1} [psubscribe $rd __keyevent@*] + } # Setup hash, set expire and set expire 0 @@ -1949,12 +1889,11 @@ start_server {tags {"hashexpire external:skip"}} { # Verify keyspace notification foreach rd [list $rd_primary $rd_replica_1] { - assert_keyevent_pattern $rd hset myhash - assert_keyevent_pattern $rd hexpire myhash + assert_keyevent_patterns $rd myhash hset hexpire } # primary gets hexpired and replica gets hdel - assert_keyevent_pattern $rd_primary hexpired myhash - assert_keyevent_pattern $rd_replica_1 hdel myhash + assert_keyevent_patterns $rd_primary myhash hexpired + assert_keyevent_patterns $rd_replica_1 myhash hdel $rd_primary close $rd_replica_1 close @@ -2016,12 +1955,11 @@ start_server {tags {"hashexpire external:skip"}} { # primary gets hexpired and replicas get hdel foreach rd [list $rd_primary $rd_replica_1 $rd_replica_2] { - assert_keyevent_pattern $rd hset myhash - assert_keyevent_pattern $rd hexpire myhash + assert_keyevent_patterns $rd myhash hset hexpire } - assert_keyevent_pattern $rd_primary hexpired myhash - assert_keyevent_pattern $rd_replica_1 hdel myhash - assert_keyevent_pattern $rd_replica_2 hdel myhash + assert_keyevent_patterns $rd_primary myhash hexpired + assert_keyevent_patterns $rd_replica_1 myhash hdel + assert_keyevent_patterns $rd_replica_2 myhash hdel $rd_primary close $rd_replica_1 close @@ -2112,9 +2050,7 @@ start_server {tags {"hashexpire external:skip"}} { assert_equal "{} {} v3" [$replica_1 HGETEX myhash FIELDS 3 f1 f2 f3] assert_equal "v1 v2 v3" [$primary HGETEX myhash FIELDS 3 f1 f2 f3] ;# No change for primary - assert_keyevent_pattern $rd_replica hexpire myhash - assert_keyevent_pattern $rd_replica hexpire myhash - assert_keyevent_pattern $rd_replica hexpired myhash + assert_keyevent_patterns $rd_replica myhash hexpire hexpire hexpired $rd_replica close # Re-enable active expiry @@ -2124,9 +2060,13 @@ start_server {tags {"hashexpire external:skip"}} { test {Promotion to primary} { - $primary FLUSHALL - $primary DEBUG SET-ACTIVE-EXPIRE no - $replica_1 DEBUG SET-ACTIVE-EXPIRE no + lassign [setup_replication_test $primary $replica_1 $primary_host $primary_port] primary_initial_expired replica_1_initial_expired + + # Initialize deferred clients and subscribe to keyspace notifications + foreach instance [list $primary $replica_1] { + $instance config set notify-keyspace-events KEA + $instance DEBUG SET-ACTIVE-EXPIRE no + } ####### Replication setup ####### $replica_1 replicaof $primary_host $primary_port wait_for_condition 50 100 { @@ -2153,9 +2093,7 @@ start_server {tags {"hashexpire external:skip"}} { assert_equal $f2_exp [$instance HEXPIRETIME myhash FIELDS 1 f2] assert_equal -1 [$instance HTTL myhash FIELDS 1 f3] assert_match {1} [scan [regexp -inline {keys\=([\d]*)} [$instance info keyspace]] keys=%d] - assert_equal "v1" [$instance HGET myhash f1] - assert_equal "v2" [$instance HGET myhash f2] - assert_equal "v3" [$instance HGET myhash f3] + assert_equal "v1 v2 v3" [$instance HMGET myhash f1 f2 f3] assert_equal 3 [$instance HLEN myhash] } @@ -2200,9 +2138,7 @@ start_server {tags {"hashexpire external:skip"}} { assert_equal $f2_exp [$instance HEXPIRETIME myhash FIELDS 1 f2] assert_equal -2 [$instance HTTL myhash FIELDS 1 f1] assert_match {1} [scan [regexp -inline {keys\=([\d]*)} [$instance info keyspace]] keys=%d] - assert_equal "" [$instance HGET myhash f1] - assert_equal "v2" [$instance HGET myhash f2] - assert_equal "v3" [$instance HGET myhash f3] + assert_equal "{} v2 v3" [$instance HMGET myhash f1 f2 f3] assert_equal 3 [$instance HLEN myhash] } @@ -2218,17 +2154,14 @@ start_server {tags {"hashexpire external:skip"}} { # Verify final state foreach instance [list $primary $replica_1] { assert_equal 2 [$instance HLEN myhash] - assert_equal "" [$instance HGET myhash f1] - assert_equal "" [$instance HGET myhash f2] - assert_equal "v3" [$instance HGET myhash f3] + assert_equal "{} {} v3" [r HMGET myhash f1 f2 f3] } foreach rd [list $rd_replica_1 $rd_primary] { - assert_keyevent_pattern $rd hexpire myhash - assert_keyevent_pattern $rd hexpire myhash + assert_keyevent_patterns $rd myhash hexpire hexpire } - assert_keyevent_pattern $rd_replica_1 hexpired myhash - assert_keyevent_pattern $rd_primary hdel myhash + assert_keyevent_patterns $rd_replica_1 myhash hexpired + assert_keyevent_patterns $rd_primary myhash hdel $rd_replica_1 close $rd_primary close @@ -2296,7 +2229,7 @@ start_cluster 3 0 {tags {"cluster mytest external:skip"} overrides {cluster-node R 1 HGETEX $key EX 0 FIELDS 1 f1 # Veridy expiration - assert_keyevent_pattern $rd hexpired "{$key}" + assert_keyevent_patterns $rd "{$key}" hexpired assert_equal 2 [R 1 HLEN $key] assert_equal "" [R 1 HGET $key f1] assert_equal -2 [R 1 HTTL $key FIELDS 1 f1] @@ -2318,8 +2251,7 @@ start_server {tags {"hashexpire external:skip"}} { # Verify initial TTL state set mem_before [r MEMORY USAGE myhash] - assert_equal "v1" [r HGET myhash f1] - assert_equal "v2" [r HGET myhash f2] + assert_equal "v1 v2" [r HMGET myhash f1 f2] assert_morethan [r HTTL myhash FIELDS 1 f1] 100 assert_equal -1 [r HTTL myhash FIELDS 1 f2] assert_equal 2 [r HLEN myhash] @@ -2337,8 +2269,7 @@ start_server {tags {"hashexpire external:skip"}} { # Verify field values and TTLs are preserved set memory_after [r MEMORY USAGE $newhash] - assert_equal "v1" [r HGET $newhash f1] - assert_equal "v2" [r HGET $newhash f2] + assert_equal "v1 v2" [r HMGET $newhash f1 f2] assert_morethan [r HTTL $newhash FIELDS 1 f1] 100 assert_equal -1 [r HTTL $newhash FIELDS 1 f2] assert_equal 2 [r HLEN $newhash] @@ -2364,9 +2295,7 @@ start_server {tags {"hashexpire external:skip"}} { # Verify initial TTL state set mem_before [r MEMORY USAGE myhash] - assert_equal "v1" [r HGET myhash f1] - assert_equal "v3" [r HGET myhash f3] - assert_equal "v4" [r HGET myhash f4] + assert_equal "v1 v3 v4" [r HMGET myhash f1 f3 f4] assert_morethan [r HTTL myhash FIELDS 1 f1] 100 assert_morethan [r HTTL myhash FIELDS 1 f3] 0 assert_equal -1 [r HTTL myhash FIELDS 1 f4] @@ -2377,9 +2306,7 @@ start_server {tags {"hashexpire external:skip"}} { r copy myhash newhash1 # Verify myhash is the same - assert_equal "v1" [r HGET myhash f1] - assert_equal "v3" [r HGET myhash f3] - assert_equal "v4" [r HGET myhash f4] + assert_equal "v1 v3 v4" [r HMGET myhash f1 f3 f4] assert_morethan [r HTTL myhash FIELDS 1 f1] 100 assert_morethan [r HTTL myhash FIELDS 1 f3] 0 assert_equal -1 [r HTTL myhash FIELDS 1 f4] @@ -2387,9 +2314,7 @@ start_server {tags {"hashexpire external:skip"}} { # Verify new hash got same values set mem_after [r MEMORY USAGE myhash] - assert_equal "v1" [r HGET newhash1 f1] - assert_equal "v3" [r HGET newhash1 f3] - assert_equal "v4" [r HGET newhash1 f4] + assert_equal "v1 v3 v4" [r HMGET myhash f1 f3 f4] assert_morethan [r HTTL newhash1 FIELDS 1 f1] 100 assert_morethan [r HTTL newhash1 FIELDS 1 f3] 0 assert_equal -1 [r HTTL newhash1 FIELDS 1 f4] @@ -2403,10 +2328,8 @@ start_server {tags {"hashexpire external:skip"}} { # Wait for original TTL to expire in copy after 2000 - assert_equal "v1" [r HGET newhash1 f1] - assert_equal "" [r HGET newhash1 f3] - assert_equal "v1" [r HGET myhash f1] - assert_equal "v3" [r HGET myhash f3] + assert_equal "v1 {}" [r HMGET newhash1 f1 f3] + assert_equal "v1 v3" [r HMGET myhash f1 f3] r HSETEX myhash EX 2 FIELDS 1 f3 v3 # Create second copy @@ -2417,10 +2340,8 @@ start_server {tags {"hashexpire external:skip"}} { # Wait for original hash TTL to expire after 2000 - assert_equal "v1" [r HGET myhash f1] - assert_equal "" [r HGET myhash f3] - assert_equal "v1" [r HGET newhash2 f1] - assert_equal "v3" [r HGET newhash2 f3] + assert_equal "v1 {}" [r HMGET myhash f1 f3] + assert_equal "v1 v3" [r HMGET newhash2 f1 f3] # Re-enable active expiry r DEBUG SET-ACTIVE-EXPIRE yes } {OK} {needs:debug} @@ -2442,8 +2363,7 @@ start_server {tags {"hashexpire external:skip"}} { set "hashtable" [r OBJECT ENCODING myhash] # Verify field values are preserved - assert_equal "v1" [r HGET myhash f1] - assert_equal "v2" [r HGET myhash f2] + assert_equal "v1 v2" [r HMGET myhash f1 f2] # Veridy expiry assert_morethan [r HTTL myhash FIELDS 1 f1] 100 assert_equal -1 [r HTTL myhash FIELDS 1 f2] @@ -2515,9 +2435,7 @@ start_server {tags {"hashexpire external:skip"}} { assert_equal 0 [r HLEN myhash] assert_match "" [scan [regexp -inline {keys\=([\d]*)} [r info keyspace]] keys=%d] - assert_keyevent_pattern $rd hset myhash - assert_keyevent_pattern $rd hexpire myhash - assert_keyevent_pattern $rd expire myhash + assert_keyevent_patterns $rd myhash hset hexpire expire $rd close # Re-enable active expiry r DEBUG SET-ACTIVE-EXPIRE yes @@ -2547,10 +2465,8 @@ start_server {tags {"hashexpire external:skip"}} { } assert_match {1} [scan [regexp -inline {keys\=([\d]*)} [r info keyspace]] keys=%d] assert_equal 1 [r EXISTS myhash] - assert_equal "v2" [r HGET myhash f2] - assert_equal "v3" [r HGET myhash f3] - assert_keyevent_pattern $rd hset myhash - assert_keyevent_pattern $rd hexpire myhash + assert_equal "{} v2 v3" [r HMGET myhash f1 f2 f3] + assert_keyevent_patterns $rd myhash hset hexpire $rd close # Re-enable active expiry r DEBUG SET-ACTIVE-EXPIRE yes @@ -2581,9 +2497,7 @@ start_server {tags {"hashexpire external:skip"}} { fail "myhash still exist" } - assert_equal "" [r HGET myhash f1] - assert_equal "" [r HGET myhash f2] - assert_equal "" [r HGET myhash f3] + assert_equal "{} {} {}" [r HMGET myhash f1 f2 f3] assert_match "" [scan [regexp -inline {keys\=([\d]*)} [r info keyspace]] keys=%d] assert_equal 0 [r HLEN myhash] # Re-enable active expiry @@ -2635,7 +2549,7 @@ tags {"aof external:skip"} { r config set appendonly yes r config set appendfsync always - # Create hash with 1short, long and no expired fields + # Create hash with 1 short, long and no expired fields set long_expire [expr {[clock seconds] + 1000000}] # Create 10 fields with long expiry for {set i 1} {$i <= 10} {incr i} { @@ -2727,4 +2641,4 @@ tags {"aof external:skip"} { r DEBUG SET-ACTIVE-EXPIRE yes } {OK} {needs:debug} } -} +} \ No newline at end of file