diff options
author | Salvatore Sanfilippo <antirez@gmail.com> | 2019-10-28 09:55:11 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-10-28 09:55:11 +0100 |
commit | 24fdc87fe0fafe4f9ccb7acdeeac1748e27ed212 (patch) | |
tree | ad037b663882b2bda83ea7ab7506bc4351c1e598 | |
parent | 6e98214f740eb04c9761ccb983ba51988ee35bc2 (diff) | |
parent | c74398e130af6983aa7d109a7b19bb9bb2668745 (diff) | |
download | redis-24fdc87fe0fafe4f9ccb7acdeeac1748e27ed212.tar.gz |
Merge pull request #4994 from soloestoy/module-blocked-client
Modules: make unloading module more safe
-rw-r--r-- | src/module.c | 11 | ||||
-rw-r--r-- | src/server.c | 2 |
2 files changed, 12 insertions, 1 deletions
diff --git a/src/module.c b/src/module.c index cabb37e08..eb994a8e5 100644 --- a/src/module.c +++ b/src/module.c @@ -63,6 +63,7 @@ struct RedisModule { int in_call; /* RM_Call() nesting level */ int in_hook; /* Hooks callback nesting level for this module (0 or 1). */ int options; /* Module options and capabilities. */ + int blocked_clients; /* Count of RedisModuleBlockedClient in this module. */ RedisModuleInfoFunc info_cb; /* Callback for module to add INFO fields. */ }; typedef struct RedisModule RedisModule; @@ -3961,6 +3962,7 @@ RedisModuleBlockedClient *RM_BlockClient(RedisModuleCtx *ctx, RedisModuleCmdFunc c->bpop.module_blocked_handle = zmalloc(sizeof(RedisModuleBlockedClient)); RedisModuleBlockedClient *bc = c->bpop.module_blocked_handle; + ctx->module->blocked_clients++; /* We need to handle the invalid operation of calling modules blocking * commands from Lua or MULTI. We actually create an already aborted @@ -4119,6 +4121,7 @@ void moduleHandleBlockedClients(void) { /* Free 'bc' only after unblocking the client, since it is * referenced in the client blocking context, and must be valid * when calling unblockClient(). */ + bc->module->blocked_clients--; zfree(bc); /* Lock again before to iterate the loop. */ @@ -6089,6 +6092,7 @@ int moduleLoad(const char *path, void **module_argv, int module_argc) { /* Redis module loaded! Register it. */ dictAdd(modules,ctx.module->name,ctx.module); + ctx.module->blocked_clients = 0; ctx.module->handle = handle; serverLog(LL_NOTICE,"Module '%s' loaded from %s",ctx.module->name,path); moduleFreeContext(&ctx); @@ -6114,6 +6118,9 @@ int moduleUnload(sds name) { } else if (listLength(module->usedby)) { errno = EPERM; return REDISMODULE_ERR; + } else if (module->blocked_clients) { + errno = EAGAIN; + return REDISMODULE_ERR; } /* Give module a chance to clean up. */ @@ -6279,6 +6286,10 @@ NULL errmsg = "the module exports APIs used by other modules. " "Please unload them first and try again"; break; + case EAGAIN: + errmsg = "the module has blocked clients. " + "Please wait them unblocked and try again"; + break; default: errmsg = "operation not possible."; break; diff --git a/src/server.c b/src/server.c index d16ff0a8e..77db810b2 100644 --- a/src/server.c +++ b/src/server.c @@ -2104,7 +2104,7 @@ void beforeSleep(struct aeEventLoop *eventLoop) { /* Check if there are clients unblocked by modules that implement * blocking commands. */ - moduleHandleBlockedClients(); + if (moduleCount()) moduleHandleBlockedClients(); /* Try to process pending commands for clients that were just unblocked. */ if (listLength(server.unblocked_clients)) |