diff --git a/src/blocked.c b/src/blocked.c index 1bce1eaa7..2ada95760 100644 --- a/src/blocked.c +++ b/src/blocked.c @@ -707,6 +707,9 @@ static void moduleUnblockClientOnKey(client *c, robj *key) { * we want to remove the pending flag to indicate we already responded to the * command with timeout reply. */ void unblockClientOnTimeout(client *c) { + /* The client has been unlocked (in the moduleUnblocked list), return ASAP. */ + if (c->bstate.btype == BLOCKED_MODULE && isModuleClientUnblocked(c)) return; + replyToBlockedClientTimedOut(c); if (c->flags & CLIENT_PENDING_COMMAND) c->flags &= ~CLIENT_PENDING_COMMAND; diff --git a/src/module.c b/src/module.c index b18377156..2fe2be8c1 100644 --- a/src/module.c +++ b/src/module.c @@ -7698,6 +7698,13 @@ void RM_LatencyAddSample(const char *event, mstime_t latency) { * https://redis.io/topics/modules-blocking-ops. * -------------------------------------------------------------------------- */ +/* Returns 1 if the client already in the moduleUnblocked list, 0 otherwise. */ +int isModuleClientUnblocked(client *c) { + RedisModuleBlockedClient *bc = c->bstate.module_blocked_handle; + + return bc->unblocked == 1; +} + /* This is called from blocked.c in order to unblock a client: may be called * for multiple reasons while the client is in the middle of being blocked * because the client is terminated, but is also called for cleanup when a diff --git a/src/server.h b/src/server.h index 04829f197..b1e851f34 100644 --- a/src/server.h +++ b/src/server.h @@ -2532,6 +2532,7 @@ const char *moduleTypeModuleName(moduleType *mt); const char *moduleNameFromCommand(struct redisCommand *cmd); void moduleFreeContext(struct RedisModuleCtx *ctx); void moduleCallCommandUnblockedHandler(client *c); +int isModuleClientUnblocked(client *c); void unblockClientFromModule(client *c); void moduleHandleBlockedClients(void); void moduleBlockedClientTimedOut(client *c, int from_module); diff --git a/tests/unit/moduleapi/blockedclient.tcl b/tests/unit/moduleapi/blockedclient.tcl index fdacb49b9..073d30895 100644 --- a/tests/unit/moduleapi/blockedclient.tcl +++ b/tests/unit/moduleapi/blockedclient.tcl @@ -290,6 +290,12 @@ foreach call_type {nested normal} { after 120 $rd close } + + test {block time is equal to timer period} { + # These time is equal, they will be unlocked in the same event loop, + # when the client is unlock, we will get the OK reply from timer. + assert_match "OK" [r unblock_by_timer 100 100] + } test "Unload the module - blockedclient" { assert_equal {OK} [r module unload blockedclient]