mirror of
https://github.com/valkey-io/valkey.git
synced 2026-07-29 12:44:01 -04:00
The test "Blocking keyspace notification with pipelining hset after
hget" was recently failing intermittently with two different errors:
1. `Expected [expr {114 * 10 < 1114}]` - timing assertion failed under Valgrind
2. `Timeout waiting for blocked clients` - race condition on normal runs
The test used wall-clock timing to verify that hget (non-blocking)
completed faster than hset (blocking). This is unreliable because:
- Valgrind slows execution 10-50x, making timing ratios meaningless
- Fast systems may complete both operations in <10ms, causing ratio failures
This fix replaces timing assertions with blocked client count checks,
which directly verify the blocking mechanism rather than inferring it
from timing. The test now confirms hget's response is available before
hset blocks, then waits for the blocked client count to transition
through the expected states.
Signed-off-by: Rain Valentine <[email protected]>
127 lines
4.9 KiB
Tcl
127 lines
4.9 KiB
Tcl
|
|
set testmodule [file normalize tests/modules/block_keyspace_notification.so]
|
|
|
|
start_server {tags {"modules"}} {
|
|
r module load $testmodule
|
|
test {Blocking keyspace notification one} {
|
|
wait_for_blocked_clients_count 0
|
|
r b_keyspace.clear
|
|
assert_equal "1" [r hset a b c]
|
|
assert_equal "{event hset key a}" [r b_keyspace.events]
|
|
}
|
|
test {Blocking keyspace notification two} {
|
|
wait_for_blocked_clients_count 0
|
|
r b_keyspace.clear
|
|
set rd1 [valkey_deferring_client]
|
|
$rd1 hset b c d
|
|
after 500
|
|
set rd2 [valkey_deferring_client]
|
|
$rd2 hset c d e
|
|
wait_for_blocked_clients_count 2
|
|
assert_equal "" [r b_keyspace.events]
|
|
wait_for_blocked_clients_count 1
|
|
assert_equal "{event hset key b}" [r b_keyspace.events]
|
|
wait_for_blocked_clients_count 0
|
|
assert_equal "{event hset key b} {event hset key c}" [r b_keyspace.events]
|
|
}
|
|
test {Blocking keyspace notification with pipelining hset after hget} {
|
|
wait_for_blocked_clients_count 0
|
|
r b_keyspace.clear
|
|
set rd1 [valkey_deferring_client]
|
|
$rd1 hset key_10 field_10 value_10
|
|
wait_for_blocked_clients_count 0
|
|
assert_equal "1" [$rd1 read]
|
|
pause_process [srv 0 pid]
|
|
# Queue up three commands: hget (non-blocking), hset (blocking), hget (non-blocking)
|
|
$rd1 hget key_10 field_10
|
|
$rd1 hset key_10 value_10 ss
|
|
$rd1 hget key_10 field_10
|
|
resume_process [srv 0 pid]
|
|
# First hget should return immediately (non-blocking)
|
|
assert_equal "value_10" [$rd1 read]
|
|
# hset triggers blocking keyspace notification - wait for it to block
|
|
wait_for_blocked_clients_count 1
|
|
# Now wait for hset to complete
|
|
wait_for_blocked_clients_count 0
|
|
assert_equal "1" [$rd1 read]
|
|
# Second hget returns after hset completes
|
|
assert_equal "value_10" [$rd1 read]
|
|
$rd1 close
|
|
}
|
|
test {Blocking keyspace notification with pipelining hget after hset} {
|
|
wait_for_blocked_clients_count 0
|
|
r b_keyspace.clear
|
|
set rd1 [valkey_deferring_client]
|
|
pause_process [srv 0 pid]
|
|
$rd1 hset key_2 field_1 value_1
|
|
$rd1 hget key_2 field_1
|
|
resume_process [srv 0 pid]
|
|
wait_for_blocked_clients_count 1
|
|
assert_equal "" [r b_keyspace.events]
|
|
wait_for_blocked_clients_count 0
|
|
assert_equal "{event hset key key_2}" [r b_keyspace.events]
|
|
assert_equal "1" [$rd1 read]
|
|
assert_equal "value_1" [$rd1 read]
|
|
}
|
|
test {Blocking keyspace notif during multi} {
|
|
wait_for_blocked_clients_count 0
|
|
r b_keyspace.clear
|
|
r multi
|
|
r hset d e f
|
|
r hset e f g
|
|
assert_equal "1 1" [r exec]
|
|
# Nothing should be blocked inside the transaction
|
|
assert_equal [s 0 blocked_clients] 0
|
|
assert_equal [r b_keyspace.events] ""
|
|
# In the background, the work should still occur
|
|
wait_for_condition 1000 50 {
|
|
[string match "{event hset key *} {event hset key *}" [set latest [r b_keyspace.events]]]
|
|
} else {
|
|
fail "Keyspace event not propagated within 5 seconds"
|
|
}
|
|
}
|
|
test {Event that fires twice} {
|
|
wait_for_blocked_clients_count 0
|
|
r b_keyspace.clear
|
|
r hset f g h
|
|
wait_for_blocked_clients_count 0
|
|
assert_equal "OK" [r b_keyspace.clear]
|
|
set rd1 [valkey_deferring_client]
|
|
$rd1 RENAME f g
|
|
# Only one blocked client
|
|
assert_equal [s 0 blocked_clients] 1
|
|
assert_equal "" [r b_keyspace.events]
|
|
# We should still see async processing for both events, which could be processed in either order
|
|
wait_for_condition 1000 50 {
|
|
[string match "{event rename_* key *} {event rename_* key *}" [set latest [r b_keyspace.events]]]
|
|
} else {
|
|
fail "Rename event not propagated within 5 seconds: latest value ${latest}"
|
|
}
|
|
}
|
|
test {Server-created keyspace notification} {
|
|
wait_for_blocked_clients_count 0
|
|
r b_keyspace.clear
|
|
assert_equal "1" [r hset h i j]
|
|
assert_equal "1" [r expire h 1]
|
|
assert_equal [s 0 blocked_clients] 0
|
|
# Expire will proceed processing in the background as we haven't
|
|
# enabled blocking for the command yet.
|
|
wait_for_condition 1000 50 {
|
|
[r b_keyspace.events] eq "{event hset key h} {event expire key h}"
|
|
} else {
|
|
fail "Expire event not propagated within 5 seconds"
|
|
}
|
|
assert_equal "OK" [r b_keyspace.clear]
|
|
# Expiration should happen in the background
|
|
wait_for_condition 1000 50 {
|
|
[r b_keyspace.events] eq "{event expired key h}"
|
|
} else {
|
|
fail "Expired event not propagated within 5 seconds"
|
|
}
|
|
}
|
|
test "Unload the module - testblockingkeyspacenotif" {
|
|
assert_equal {OK} [r module unload testblockingkeyspacenotif]
|
|
}
|
|
}
|
|
|