diff --git a/src/module.c b/src/module.c index 3fc6a5499..59b432d7d 100644 --- a/src/module.c +++ b/src/module.c @@ -2079,6 +2079,12 @@ int RM_BlockedClientMeasureTimeEnd(RedisModuleBlockedClient *bc) { * the -LOADING error) */ void RM_Yield(RedisModuleCtx *ctx, int flags, const char *busy_reply) { + static int yield_nesting = 0; + /* Avoid nested calls to RM_Yield */ + if (yield_nesting) + return; + yield_nesting++; + long long now = getMonotonicUs(); if (now >= ctx->next_yield_time) { /* In loading mode, there's no need to handle busy_module_yield_reply, @@ -2092,10 +2098,11 @@ void RM_Yield(RedisModuleCtx *ctx, int flags, const char *busy_reply) { server.busy_module_yield_reply = busy_reply; /* start the blocking operation if not already started. */ if (!server.busy_module_yield_flags) { - server.busy_module_yield_flags = flags & REDISMODULE_YIELD_FLAG_CLIENTS ? - BUSY_MODULE_YIELD_CLIENTS : BUSY_MODULE_YIELD_EVENTS; + server.busy_module_yield_flags = BUSY_MODULE_YIELD_EVENTS; blockingOperationStarts(); } + if (flags & REDISMODULE_YIELD_FLAG_CLIENTS) + server.busy_module_yield_flags |= BUSY_MODULE_YIELD_CLIENTS; /* Let redis process events */ processEventsWhileBlocked(); @@ -2110,6 +2117,7 @@ void RM_Yield(RedisModuleCtx *ctx, int flags, const char *busy_reply) { /* decide when the next event should fire. */ ctx->next_yield_time = now + 1000000 / server.hz; } + yield_nesting --; } /* Set flags defining capabilities or behavior bit flags. diff --git a/tests/unit/moduleapi/blockedclient.tcl b/tests/unit/moduleapi/blockedclient.tcl index de3cf5946..d1692ed34 100644 --- a/tests/unit/moduleapi/blockedclient.tcl +++ b/tests/unit/moduleapi/blockedclient.tcl @@ -90,7 +90,8 @@ start_server {tags {"modules"}} { } } - test {Busy module command} { +foreach call_type {nested normal} { + test "Busy module command - $call_type" { set busy_time_limit 50 set old_time_limit [lindex [r config get busy-reply-threshold] 1] r config set busy-reply-threshold $busy_time_limit @@ -98,7 +99,11 @@ start_server {tags {"modules"}} { # run command that blocks until released set start [clock clicks -milliseconds] - $rd slow_fg_command 0 + if {$call_type == "nested"} { + $rd do_rm_call slow_fg_command 0 + } else { + $rd slow_fg_command 0 + } $rd flush # make sure we get BUSY error, and that we didn't get it too early @@ -114,9 +119,13 @@ start_server {tags {"modules"}} { } $rd read - #run command that blocks for 200ms + # run command that blocks for 200ms set start [clock clicks -milliseconds] - $rd slow_fg_command 200000 + if {$call_type == "nested"} { + $rd do_rm_call slow_fg_command 200000 + } else { + $rd slow_fg_command 200000 + } $rd flush after 10 ;# try to make sure redis started running the command before we proceed @@ -128,6 +137,7 @@ start_server {tags {"modules"}} { $rd close r config set busy-reply-threshold $old_time_limit } +} test {RM_Call from blocked client} { set busy_time_limit 50