Fix edge-case when a module client is unblocked (#8618)

Scenario:
1. A module client is blocked on keys with a timeout
2. Shortly before the timeout expires, the key is being populated and signaled
   as ready
3. Redis calls moduleTryServeClientBlockedOnKey (which replies to client) and
   then moduleUnblockClient
4. moduleUnblockClient doesn't really unblock the client, it writes to
   server.module_blocked_pipe and only marks the BC as unblocked.
5. beforeSleep kics in, by this time the client still exists and techincally
   timed-out. beforeSleep replies to the timeout client (double reply) and
   only then moduleHandleBlockedClients is called, reading from module_blocked_pipe
   and calling unblockClient

The solution is similar to what was done in moduleTryServeClientBlockedOnKey: we
should avoid re-processing an already-unblocked client
This commit is contained in:
guybe7 2021-03-08 18:00:19 +01:00 committed by GitHub
parent 20377a6f3d
commit e58118cda6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -5550,6 +5550,12 @@ void moduleHandleBlockedClients(void) {
* API to unblock the client and the memory will be released. */ * API to unblock the client and the memory will be released. */
void moduleBlockedClientTimedOut(client *c) { void moduleBlockedClientTimedOut(client *c) {
RedisModuleBlockedClient *bc = c->bpop.module_blocked_handle; RedisModuleBlockedClient *bc = c->bpop.module_blocked_handle;
/* Protect against re-processing: don't serve clients that are already
* in the unblocking list for any reason (including RM_UnblockClient()
* explicit call). See #6798. */
if (bc->unblocked) return;
RedisModuleCtx ctx = REDISMODULE_CTX_INIT; RedisModuleCtx ctx = REDISMODULE_CTX_INIT;
ctx.flags |= REDISMODULE_CTX_BLOCKED_TIMEOUT; ctx.flags |= REDISMODULE_CTX_BLOCKED_TIMEOUT;
ctx.module = bc->module; ctx.module = bc->module;