Fix WAITAOF reply when using last_offset and last_numreplicas (#11917)
WAITAOF wad added in #11713, its return is an array. But forget to handle WAITAOF in last_offset and last_numreplicas, causing WAITAOF to return a WAIT like reply. Tests was added to validate that case (both WAIT and WAITAOF). This PR also refactored processClientsWaitingReplicas a bit for better maintainability and readability.
This commit is contained in:
+30
-25
@@ -3575,7 +3575,7 @@ void waitaofCommand(client *c) {
|
||||
return;
|
||||
}
|
||||
if (numlocal && !server.aof_enabled) {
|
||||
addReplyError(c,"WAITAOF cannot be used when appendonly is disabled");
|
||||
addReplyError(c, "WAITAOF cannot be used when numlocal is set but appendonly is disabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3619,51 +3619,56 @@ void processClientsWaitingReplicas(void) {
|
||||
|
||||
listRewind(server.clients_waiting_acks,&li);
|
||||
while((ln = listNext(&li))) {
|
||||
int numlocal = 0;
|
||||
int numreplicas = 0;
|
||||
|
||||
client *c = ln->value;
|
||||
int is_wait_aof = c->bstate.btype == BLOCKED_WAITAOF;
|
||||
|
||||
if (is_wait_aof && c->bstate.numlocal && !server.aof_enabled) {
|
||||
addReplyError(c, "WAITAOF cannot be used when numlocal is set but appendonly is disabled.");
|
||||
unblockClient(c);
|
||||
addReplyError(c,"WAITAOF cannot be used when appendonly is disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Every time we find a client that is satisfied for a given
|
||||
* offset and number of replicas, we remember it so the next client
|
||||
* may be unblocked without calling replicationCountAcksByOffset()
|
||||
* or calling replicationCountAOFAcksByOffset()
|
||||
* if the requested offset / replicas were equal or less. */
|
||||
if (last_offset && last_offset >= c->bstate.reploffset &&
|
||||
last_numreplicas >= c->bstate.numreplicas)
|
||||
{
|
||||
/* Reply before unblocking, because unblock client calls reqresAppendResponse */
|
||||
addReplyLongLong(c,last_numreplicas);
|
||||
unblockClient(c);
|
||||
numreplicas = last_numreplicas;
|
||||
} else {
|
||||
int numreplicas = is_wait_aof ?
|
||||
numreplicas = is_wait_aof ?
|
||||
replicationCountAOFAcksByOffset(c->bstate.reploffset) :
|
||||
replicationCountAcksByOffset(c->bstate.reploffset);
|
||||
|
||||
if (numreplicas >= c->bstate.numreplicas) {
|
||||
last_offset = c->bstate.reploffset;
|
||||
last_numreplicas = numreplicas;
|
||||
/* Check if the number of replicas is satisfied. */
|
||||
if (numreplicas < c->bstate.numreplicas) continue;
|
||||
|
||||
/* Check if the local constraint of WAITAOF is served */
|
||||
int numlocal = server.fsynced_reploff >= c->bstate.reploffset;
|
||||
if (is_wait_aof && numlocal < c->bstate.numlocal)
|
||||
continue;
|
||||
|
||||
if (is_wait_aof) {
|
||||
/* WAITAOF has an array reply*/
|
||||
addReplyArrayLen(c,2);
|
||||
addReplyLongLong(c,numlocal);
|
||||
addReplyLongLong(c,numreplicas);
|
||||
} else {
|
||||
addReplyLongLong(c,numreplicas);
|
||||
}
|
||||
/* Reply before unblocking, because unblock client calls reqresAppendResponse */
|
||||
unblockClient(c);
|
||||
}
|
||||
last_offset = c->bstate.reploffset;
|
||||
last_numreplicas = numreplicas;
|
||||
}
|
||||
|
||||
/* Check if the local constraint of WAITAOF is served */
|
||||
if (is_wait_aof) {
|
||||
numlocal = server.fsynced_reploff >= c->bstate.reploffset;
|
||||
if (numlocal < c->bstate.numlocal) continue;
|
||||
}
|
||||
|
||||
/* Reply before unblocking, because unblock client calls reqresAppendResponse */
|
||||
if (is_wait_aof) {
|
||||
/* WAITAOF has an array reply */
|
||||
addReplyArrayLen(c, 2);
|
||||
addReplyLongLong(c, numlocal);
|
||||
addReplyLongLong(c, numreplicas);
|
||||
} else {
|
||||
addReplyLongLong(c, numreplicas);
|
||||
}
|
||||
|
||||
unblockClient(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -886,9 +886,9 @@ proc wait_for_blocked_client {{idx 0}} {
|
||||
}
|
||||
}
|
||||
|
||||
proc wait_for_blocked_clients_count {count {maxtries 100} {delay 10}} {
|
||||
proc wait_for_blocked_clients_count {count {maxtries 100} {delay 10} {idx 0}} {
|
||||
wait_for_condition $maxtries $delay {
|
||||
[s blocked_clients] == $count
|
||||
[s $idx blocked_clients] == $count
|
||||
} else {
|
||||
fail "Timeout waiting for blocked clients"
|
||||
}
|
||||
|
||||
+67
-7
@@ -18,20 +18,20 @@ start_server {} {
|
||||
fail "Replication not started."
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
test {WAIT out of range timeout (milliseconds)} {
|
||||
# Timeout is parsed as milliseconds by getLongLongFromObjectOrReply().
|
||||
# Verify we get out of range message if value is behind LLONG_MAX
|
||||
# (decimal value equals to 0x8000000000000000)
|
||||
assert_error "*or out of range*" {$master wait 2 9223372036854775808}
|
||||
|
||||
|
||||
# expected to fail by later overflow condition after addition
|
||||
# of mstime(). (decimal value equals to 0x7FFFFFFFFFFFFFFF)
|
||||
assert_error "*timeout is out of range*" {$master wait 2 9223372036854775807}
|
||||
|
||||
assert_error "*timeout is negative*" {$master wait 2 -1}
|
||||
|
||||
assert_error "*timeout is negative*" {$master wait 2 -1}
|
||||
}
|
||||
|
||||
|
||||
test {WAIT should acknowledge 1 additional copy of the data} {
|
||||
$master set foo 0
|
||||
$master incr foo
|
||||
@@ -68,6 +68,36 @@ start_server {} {
|
||||
exec kill -SIGCONT $slave_pid
|
||||
assert {[$master wait 1 1000] == 1}
|
||||
}
|
||||
|
||||
test {WAIT replica multiple clients unblock - reuse last result} {
|
||||
set rd [redis_deferring_client -1]
|
||||
set rd2 [redis_deferring_client -1]
|
||||
|
||||
exec kill -SIGSTOP $slave_pid
|
||||
|
||||
$rd incr foo
|
||||
$rd read
|
||||
|
||||
$rd2 incr foo
|
||||
$rd2 read
|
||||
|
||||
$rd wait 1 0
|
||||
$rd2 wait 1 0
|
||||
wait_for_blocked_clients_count 2 100 10 -1
|
||||
|
||||
exec kill -SIGCONT $slave_pid
|
||||
|
||||
assert_equal [$rd read] {1}
|
||||
assert_equal [$rd2 read] {1}
|
||||
|
||||
$rd ping
|
||||
assert_equal [$rd read] {PONG}
|
||||
$rd2 ping
|
||||
assert_equal [$rd2 read] {PONG}
|
||||
|
||||
$rd close
|
||||
$rd2 close
|
||||
}
|
||||
}}
|
||||
|
||||
|
||||
@@ -101,13 +131,13 @@ tags {"wait aof network external:skip"} {
|
||||
$rd waitaof 1 0 0
|
||||
wait_for_blocked_client
|
||||
r config set appendonly no ;# this should release the blocked client as an error
|
||||
assert_error {ERR WAITAOF cannot be used when appendonly is disabled} {$rd read}
|
||||
assert_error {ERR WAITAOF cannot be used when numlocal is set but appendonly is disabled.} {$rd read}
|
||||
$rd close
|
||||
}
|
||||
|
||||
test {WAITAOF local on server with aof disabled} {
|
||||
$master incr foo
|
||||
assert_error {ERR WAITAOF cannot be used when appendonly is disabled} {$master waitaof 1 0 0}
|
||||
assert_error {ERR WAITAOF cannot be used when numlocal is set but appendonly is disabled.} {$master waitaof 1 0 0}
|
||||
}
|
||||
|
||||
$master config set appendonly yes
|
||||
@@ -164,6 +194,36 @@ tags {"wait aof network external:skip"} {
|
||||
assert_equal [$master waitaof 0 1 0] {1 1}
|
||||
}
|
||||
|
||||
test {WAITAOF replica multiple clients unblock - reuse last result} {
|
||||
set rd [redis_deferring_client -1]
|
||||
set rd2 [redis_deferring_client -1]
|
||||
|
||||
exec kill -SIGSTOP $replica_pid
|
||||
|
||||
$rd incr foo
|
||||
$rd read
|
||||
|
||||
$rd2 incr foo
|
||||
$rd2 read
|
||||
|
||||
$rd waitaof 0 1 0
|
||||
$rd2 waitaof 0 1 0
|
||||
wait_for_blocked_clients_count 2 100 10 -1
|
||||
|
||||
exec kill -SIGCONT $replica_pid
|
||||
|
||||
assert_equal [$rd read] {1 1}
|
||||
assert_equal [$rd2 read] {1 1}
|
||||
|
||||
$rd ping
|
||||
assert_equal [$rd read] {PONG}
|
||||
$rd2 ping
|
||||
assert_equal [$rd2 read] {PONG}
|
||||
|
||||
$rd close
|
||||
$rd2 close
|
||||
}
|
||||
|
||||
test {WAITAOF on promoted replica} {
|
||||
$replica replicaof no one
|
||||
$replica incr foo
|
||||
|
||||
Reference in New Issue
Block a user