Fix crash during SLAVEOF when clients are blocked on lazyfree (#13853)

After https://github.com/redis/redis/pull/13167, when a client calls
`FLUSHDB` command, we still async empty database, and the client was
blocked until the lazyfree completes.

1) If another client calls `SLAVEOF` command during this time, the
server will unblock all blocked clients, including those blocked by the
lazyfree. However, when unblocking a lazyfree blocked client, we forgot
to call `updateStatsOnUnblock()`, which ultimately triggered the
following assertion.

2) If a client blocked by Lazyfree is unblocked midway, and at this
point the `bio_comp_list` has already received the completion
notification for the bio, we might end up processing a client that has
already been unblocked in `flushallSyncBgDone()`. Therefore, we need to
filter it out.

---------

Co-authored-by: oranagra <oran@redislabs.com>
This commit is contained in:
debing.sun 2025-03-17 20:27:05 +08:00 committed by YaacovHazan
parent 9b45a148f2
commit 949d4efa36
3 changed files with 16 additions and 1 deletions

View File

@ -270,6 +270,7 @@ void disconnectAllBlockedClients(void) {
if (c->bstate.btype == BLOCKED_LAZYFREE) {
addReply(c, shared.ok); /* No reason lazy-free to fail */
updateStatsOnUnblock(c, 0, 0, 0);
c->flags &= ~CLIENT_PENDING_COMMAND;
unblockClient(c, 1);
} else {

View File

@ -702,7 +702,7 @@ void flushallSyncBgDone(uint64_t client_id) {
client *c = lookupClientByID(client_id);
/* Verify that client still exists */
if (!c) return;
if (!(c && c->flags & CLIENT_BLOCKED)) return;
/* Update current_client (Called functions might rely on it) */
client *old_client = server.current_client;

View File

@ -174,4 +174,18 @@ start_server {tags {"lazyfree"}} {
assert_equal [s lazyfreed_objects] 2
$rd close
}
test "Unblocks client blocked on lazyfree via REPLICAOF command" {
set rd [redis_deferring_client]
populate 50000 ;# Just to make flushdb async slower
$rd flushdb
wait_for_blocked_client
# Test that slaveof command unblocks clients without assertion failure
r slaveof 127.0.0.1 0
assert_equal [$rd read] {OK}
$rd close
r ping
r slaveof no one
} {OK} {external:skip}
}