mirror of
https://codeberg.org/redict/redict.git
synced 2025-01-22 08:08:53 -05:00
Shared reusable client for RM_Call() (#8516)
A single client pointer is added in the server struct. This is initialized by the first RM_Call() and reused for every subsequent RM_Call() except if it's already in use, which means that it's not used for (recursive) module calls to modules. For these, a new "fake" client is created each time. Other changes: * Avoid allocating a dict iterator in pubsubUnsubscribeAllChannels when not needed
This commit is contained in:
parent
4a474843fb
commit
6122f1c450
34
src/module.c
34
src/module.c
@ -3969,16 +3969,26 @@ RedisModuleCallReply *RM_Call(RedisModuleCtx *ctx, const char *cmdname, const ch
|
||||
RedisModuleCallReply *reply = NULL;
|
||||
int replicate = 0; /* Replicate this command? */
|
||||
|
||||
/* Create the client and dispatch the command. */
|
||||
/* Handle arguments. */
|
||||
va_start(ap, fmt);
|
||||
c = createClient(NULL);
|
||||
c->user = NULL; /* Root user. */
|
||||
argv = moduleCreateArgvFromUserFormat(cmdname,fmt,&argc,&flags,ap);
|
||||
replicate = flags & REDISMODULE_ARGV_REPLICATE;
|
||||
va_end(ap);
|
||||
|
||||
/* Setup our fake client for command execution. */
|
||||
c->flags |= CLIENT_MODULE;
|
||||
if (server.module_client == NULL) {
|
||||
/* This is the first RM_Call() ever. Create reusable client. */
|
||||
c = server.module_client = createClient(NULL);
|
||||
} else if (server.module_client->argv == NULL) {
|
||||
/* The reusable module client is not busy with a command. Use it. */
|
||||
c = server.module_client;
|
||||
} else {
|
||||
/* The reusable module client is busy. (It is probably used in a
|
||||
* recursive call to this module.) */
|
||||
c = createClient(NULL);
|
||||
}
|
||||
c->user = NULL; /* Root user. */
|
||||
c->flags = CLIENT_MODULE;
|
||||
|
||||
/* We do not want to allow block, the module do not expect it */
|
||||
c->flags |= CLIENT_DENY_BLOCKING;
|
||||
@ -4066,7 +4076,18 @@ RedisModuleCallReply *RM_Call(RedisModuleCtx *ctx, const char *cmdname, const ch
|
||||
|
||||
cleanup:
|
||||
if (ctx->module) ctx->module->in_call--;
|
||||
freeClient(c);
|
||||
if (c == server.module_client) {
|
||||
/* reset shared client so it can be reused */
|
||||
discardTransaction(c);
|
||||
pubsubUnsubscribeAllChannels(c,0);
|
||||
pubsubUnsubscribeAllPatterns(c,0);
|
||||
resetClient(c); /* frees the contents of argv */
|
||||
zfree(c->argv);
|
||||
c->argv = NULL;
|
||||
c->resp = 2;
|
||||
} else {
|
||||
freeClient(c); /* temporary client */
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
|
||||
@ -8269,6 +8290,9 @@ void moduleInitModulesSystem(void) {
|
||||
/* Set up filter list */
|
||||
moduleCommandFilters = listCreate();
|
||||
|
||||
/* Reusable client for RM_Call() is created on first use */
|
||||
server.module_client = NULL;
|
||||
|
||||
moduleRegisterCoreAPI();
|
||||
if (pipe(server.module_blocked_pipe) == -1) {
|
||||
serverLog(LL_WARNING,
|
||||
|
14
src/pubsub.c
14
src/pubsub.c
@ -250,18 +250,20 @@ int pubsubUnsubscribePattern(client *c, robj *pattern, int notify) {
|
||||
/* Unsubscribe from all the channels. Return the number of channels the
|
||||
* client was subscribed to. */
|
||||
int pubsubUnsubscribeAllChannels(client *c, int notify) {
|
||||
dictIterator *di = dictGetSafeIterator(c->pubsub_channels);
|
||||
dictEntry *de;
|
||||
int count = 0;
|
||||
if (dictSize(c->pubsub_channels) > 0) {
|
||||
dictIterator *di = dictGetSafeIterator(c->pubsub_channels);
|
||||
dictEntry *de;
|
||||
|
||||
while((de = dictNext(di)) != NULL) {
|
||||
robj *channel = dictGetKey(de);
|
||||
while((de = dictNext(di)) != NULL) {
|
||||
robj *channel = dictGetKey(de);
|
||||
|
||||
count += pubsubUnsubscribeChannel(c,channel,notify);
|
||||
count += pubsubUnsubscribeChannel(c,channel,notify);
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
}
|
||||
/* We were subscribed to nothing? Still reply to the client. */
|
||||
if (notify && count == 0) addReplyPubsubUnsubscribed(c,NULL);
|
||||
dictReleaseIterator(di);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -1195,6 +1195,7 @@ struct redisServer {
|
||||
to be processed. */
|
||||
pid_t child_pid; /* PID of current child */
|
||||
int child_type; /* Type of current child */
|
||||
client *module_client; /* "Fake" client to call Redis from modules */
|
||||
/* Networking */
|
||||
int port; /* TCP listening port */
|
||||
int tls_port; /* TLS listening port */
|
||||
|
@ -16,6 +16,11 @@ start_server {tags {"modules"}} {
|
||||
assert { [string match "*cmdstat_module*" $info] }
|
||||
}
|
||||
|
||||
test {test RM_Call recursive} {
|
||||
set info [r test.call_generic test.call_generic info commandstats]
|
||||
assert { [string match "*cmdstat_module*" $info] }
|
||||
}
|
||||
|
||||
test {test redis version} {
|
||||
set version [s redis_version]
|
||||
assert_equal $version [r test.redisversion]
|
||||
|
Loading…
Reference in New Issue
Block a user