summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorYossi Gottlieb <yossigo@gmail.com>2020-10-05 17:03:17 +0300
committerYossi Gottlieb <yossigo@gmail.com>2020-10-11 16:04:14 +0300
commit9b7f8ba84b9de1ec846badd45e4004e3cd23f178 (patch)
treef83d80d28ce292b2f6914e117d195705cb141208 /src
parentdab5ec9b8d5c6f7a99a557d65cc95816b981835b (diff)
downloadredis-9b7f8ba84b9de1ec846badd45e4004e3cd23f178.tar.gz
Introduce getKeysResult for getKeysFromCommand.
Avoid using a static buffer for short key index responses, and make it caller's responsibility to stack-allocate a result type. Responses that don't fit are still allocated on the heap.
Diffstat (limited to 'src')
-rw-r--r--src/acl.c9
-rw-r--r--src/cluster.c11
-rw-r--r--src/db.c169
-rw-r--r--src/module.c33
-rw-r--r--src/server.c14
-rw-r--r--src/server.h42
-rw-r--r--src/tracking.c13
7 files changed, 171 insertions, 120 deletions
diff --git a/src/acl.c b/src/acl.c
index 30fdc8e07..aa3ed197f 100644
--- a/src/acl.c
+++ b/src/acl.c
@@ -1115,8 +1115,9 @@ int ACLCheckCommandPerm(client *c, int *keyidxptr) {
if (!(c->user->flags & USER_FLAG_ALLKEYS) &&
(c->cmd->getkeys_proc || c->cmd->firstkey))
{
- int numkeys;
- int *keyidx = getKeysFromCommand(c->cmd,c->argv,c->argc,&numkeys);
+ getKeysResult result = GETKEYS_RESULT_INIT;
+ int numkeys = getKeysFromCommand(c->cmd,c->argv,c->argc,&result);
+ int *keyidx = result.keys;
for (int j = 0; j < numkeys; j++) {
listIter li;
listNode *ln;
@@ -1137,11 +1138,11 @@ int ACLCheckCommandPerm(client *c, int *keyidxptr) {
}
if (!match) {
if (keyidxptr) *keyidxptr = keyidx[j];
- getKeysFreeResult(keyidx);
+ getKeysFreeResult(&result);
return ACL_DENIED_KEY;
}
}
- getKeysFreeResult(keyidx);
+ getKeysFreeResult(&result);
}
/* If we survived all the above checks, the user can execute the
diff --git a/src/cluster.c b/src/cluster.c
index f7f033ba3..248d5c17d 100644
--- a/src/cluster.c
+++ b/src/cluster.c
@@ -5644,7 +5644,10 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
margc = ms->commands[i].argc;
margv = ms->commands[i].argv;
- keyindex = getKeysFromCommand(mcmd,margv,margc,&numkeys);
+ getKeysResult result = GETKEYS_RESULT_INIT;
+ numkeys = getKeysFromCommand(mcmd,margv,margc,&result);
+ keyindex = result.keys;
+
for (j = 0; j < numkeys; j++) {
robj *thiskey = margv[keyindex[j]];
int thisslot = keyHashSlot((char*)thiskey->ptr,
@@ -5662,7 +5665,7 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
* not trapped earlier in processCommand(). Report the same
* error to the client. */
if (n == NULL) {
- getKeysFreeResult(keyindex);
+ getKeysFreeResult(&result);
if (error_code)
*error_code = CLUSTER_REDIR_DOWN_UNBOUND;
return NULL;
@@ -5686,7 +5689,7 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
if (!equalStringObjects(firstkey,thiskey)) {
if (slot != thisslot) {
/* Error: multiple keys from different slots. */
- getKeysFreeResult(keyindex);
+ getKeysFreeResult(&result);
if (error_code)
*error_code = CLUSTER_REDIR_CROSS_SLOT;
return NULL;
@@ -5705,7 +5708,7 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
missing_keys++;
}
}
- getKeysFreeResult(keyindex);
+ getKeysFreeResult(&result);
}
/* No key at all in command? then we can serve the request
diff --git a/src/db.c b/src/db.c
index c004468d8..d30956c9a 100644
--- a/src/db.c
+++ b/src/db.c
@@ -1314,27 +1314,54 @@ int expireIfNeeded(redisDb *db, robj *key) {
/* -----------------------------------------------------------------------------
* API to get key arguments from commands
* ---------------------------------------------------------------------------*/
-#define MAX_KEYS_BUFFER 256
-static int getKeysTempBuffer[MAX_KEYS_BUFFER];
+
+/* Prepare the getKeysResult struct to hold numkeys, either by using the
+ * pre-allocated keysbuf or by allocating a new array on the heap.
+ *
+ * This function must be called at least once before starting to populate
+ * the result, and can be called repeatedly to enlarge the result array.
+ */
+int *getKeysPrepareResult(getKeysResult *result, int numkeys) {
+ /* GETKEYS_RESULT_INIT initializes keys to NULL, point it to the pre-allocated stack
+ * buffer here. */
+ if (!result->keys) {
+ serverAssert(!result->numkeys);
+ result->keys = result->keysbuf;
+ }
+
+ /* Resize if necessary */
+ if (numkeys > result->size) {
+ if (result->keys != result->keysbuf) {
+ /* We're not using a static buffer, just (re)alloc */
+ result->keys = zrealloc(result->keys, numkeys * sizeof(int));
+ } else {
+ /* We are using a static buffer, copy its contents */
+ result->keys = zmalloc(numkeys * sizeof(int));
+ if (result->numkeys)
+ memcpy(result->keys, result->keysbuf, result->numkeys * sizeof(int));
+ }
+ result->size = numkeys;
+ }
+
+ return result->keys;
+}
/* The base case is to use the keys position as given in the command table
* (firstkey, lastkey, step). */
-int *getKeysUsingCommandTable(struct redisCommand *cmd,robj **argv, int argc, int *numkeys) {
+int getKeysUsingCommandTable(struct redisCommand *cmd,robj **argv, int argc, getKeysResult *result) {
int j, i = 0, last, *keys;
UNUSED(argv);
if (cmd->firstkey == 0) {
- *numkeys = 0;
- return NULL;
+ result->numkeys = 0;
+ return 0;
}
last = cmd->lastkey;
if (last < 0) last = argc+last;
int count = ((last - cmd->firstkey)+1);
- keys = getKeysTempBuffer;
- if (count > MAX_KEYS_BUFFER)
- keys = zmalloc(sizeof(int)*count);
+ keys = getKeysPrepareResult(result, count);
for (j = cmd->firstkey; j <= last; j += cmd->keystep) {
if (j >= argc) {
@@ -1345,17 +1372,17 @@ int *getKeysUsingCommandTable(struct redisCommand *cmd,robj **argv, int argc, in
* return no keys and expect the command implementation to report
* an arity or syntax error. */
if (cmd->flags & CMD_MODULE || cmd->arity < 0) {
- getKeysFreeResult(keys);
- *numkeys = 0;
- return NULL;
+ getKeysFreeResult(result);
+ result->numkeys = 0;
+ return 0;
} else {
serverPanic("Redis built-in command declared keys positions not matching the arity requirements.");
}
}
keys[i++] = j;
}
- *numkeys = i;
- return keys;
+ result->numkeys = i;
+ return i;
}
/* Return all the arguments that are keys in the command passed via argc / argv.
@@ -1369,20 +1396,20 @@ int *getKeysUsingCommandTable(struct redisCommand *cmd,robj **argv, int argc, in
*
* This function uses the command table if a command-specific helper function
* is not required, otherwise it calls the command-specific function. */
-int *getKeysFromCommand(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
+int getKeysFromCommand(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
if (cmd->flags & CMD_MODULE_GETKEYS) {
- return moduleGetCommandKeysViaAPI(cmd,argv,argc,numkeys);
+ return moduleGetCommandKeysViaAPI(cmd,argv,argc,result);
} else if (!(cmd->flags & CMD_MODULE) && cmd->getkeys_proc) {
- return cmd->getkeys_proc(cmd,argv,argc,numkeys);
+ return cmd->getkeys_proc(cmd,argv,argc,result);
} else {
- return getKeysUsingCommandTable(cmd,argv,argc,numkeys);
+ return getKeysUsingCommandTable(cmd,argv,argc,result);
}
}
/* Free the result of getKeysFromCommand. */
-void getKeysFreeResult(int *result) {
- if (result != getKeysTempBuffer)
- zfree(result);
+void getKeysFreeResult(getKeysResult *result) {
+ if (result && result->keys != result->keysbuf)
+ zfree(result->keys);
}
/* Helper function to extract keys from following commands:
@@ -1397,43 +1424,42 @@ void getKeysFreeResult(int *result) {
* 'firstKeyOfs': firstkey index.
* 'keyStep': the interval of each key, usually this value is 1.
* */
-int *genericGetKeys(int storeKeyOfs, int keyCountOfs, int firstKeyOfs, int keyStep,
- robj **argv, int argc, int *numkeys) {
+int genericGetKeys(int storeKeyOfs, int keyCountOfs, int firstKeyOfs, int keyStep,
+ robj **argv, int argc, getKeysResult *result) {
int i, num, *keys;
num = atoi(argv[keyCountOfs]->ptr);
/* Sanity check. Don't return any key if the command is going to
* reply with syntax error. (no input keys). */
if (num < 1 || num > (argc - firstKeyOfs)/keyStep) {
- *numkeys = 0;
- return NULL;
+ result->numkeys = 0;
+ return 0;
}
- keys = getKeysTempBuffer;
- *numkeys = storeKeyOfs ? num + 1 : num;
- if (*numkeys > MAX_KEYS_BUFFER)
- keys = zmalloc(sizeof(int)*(*numkeys));
+ int numkeys = storeKeyOfs ? num + 1 : num;
+ keys = getKeysPrepareResult(result, numkeys);
+ result->numkeys = numkeys;
/* Add all key positions for argv[firstKeyOfs...n] to keys[] */
for (i = 0; i < num; i++) keys[i] = firstKeyOfs+(i*keyStep);
if (storeKeyOfs) keys[num] = storeKeyOfs;
- return keys;
+ return result->numkeys;
}
-int *zunionInterStoreGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
+int zunionInterStoreGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
UNUSED(cmd);
- return genericGetKeys(1, 2, 3, 1, argv, argc, numkeys);
+ return genericGetKeys(1, 2, 3, 1, argv, argc, result);
}
-int *zunionInterGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
+int zunionInterGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
UNUSED(cmd);
- return genericGetKeys(0, 1, 2, 1, argv, argc, numkeys);
+ return genericGetKeys(0, 1, 2, 1, argv, argc, result);
}
-int *evalGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
+int evalGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
UNUSED(cmd);
- return genericGetKeys(0, 2, 3, 1, argv, argc, numkeys);
+ return genericGetKeys(0, 2, 3, 1, argv, argc, result);
}
/* Helper function to extract keys from the SORT command.
@@ -1443,13 +1469,12 @@ int *evalGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys)
* The first argument of SORT is always a key, however a list of options
* follow in SQL-alike style. Here we parse just the minimum in order to
* correctly identify keys in the "STORE" option. */
-int *sortGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
+int sortGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
int i, j, num, *keys, found_store = 0;
UNUSED(cmd);
num = 0;
- keys = getKeysTempBuffer; /* Alloc 2 places for the worst case. */
-
+ keys = getKeysPrepareResult(result, 2); /* Alloc 2 places for the worst case. */
keys[num++] = 1; /* <sort-key> is always present. */
/* Search for STORE option. By default we consider options to don't
@@ -1481,11 +1506,11 @@ int *sortGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys)
}
}
}
- *numkeys = num + found_store;
- return keys;
+ result->numkeys = num + found_store;
+ return result->numkeys;
}
-int *migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
+int migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
int i, num, first, *keys;
UNUSED(cmd);
@@ -1506,20 +1531,17 @@ int *migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkey
}
}
- keys = getKeysTempBuffer;
- if (num>MAX_KEYS_BUFFER)
- keys = zmalloc(sizeof(int)*num);
-
+ keys = getKeysPrepareResult(result, num);
for (i = 0; i < num; i++) keys[i] = first+i;
- *numkeys = num;
- return keys;
+ result->numkeys = num;
+ return num;
}
/* Helper function to extract keys from following commands:
* GEORADIUS key x y radius unit [WITHDIST] [WITHHASH] [WITHCOORD] [ASC|DESC]
* [COUNT count] [STORE key] [STOREDIST key]
* GEORADIUSBYMEMBER key member radius unit ... options ... */
-int *georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
+int georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
int i, num, *keys;
UNUSED(cmd);
@@ -1542,24 +1564,21 @@ int *georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numk
* argv[1] = key,
* argv[5...n] = stored key if present
*/
- keys = getKeysTempBuffer;
- if (num>MAX_KEYS_BUFFER)
- keys = zmalloc(sizeof(int) * num);
+ keys = getKeysPrepareResult(result, num);
/* Add all key positions to keys[] */
keys[0] = 1;
if(num > 1) {
keys[1] = stored_key;
}
- *numkeys = num;
- return keys;
+ result->numkeys = num;
+ return num;
}
/* LCS ... [KEYS <key1> <key2>] ... */
-int *lcsGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys)
-{
+int lcsGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
int i;
- int *keys = getKeysTempBuffer;
+ int *keys = getKeysPrepareResult(result, 2);
UNUSED(cmd);
/* We need to parse the options of the command in order to check for the
@@ -1573,33 +1592,32 @@ int *lcsGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys)
} else if (!strcasecmp(arg, "keys") && moreargs >= 2) {
keys[0] = i+1;
keys[1] = i+2;
- *numkeys = 2;
- return keys;
+ result->numkeys = 2;
+ return result->numkeys;
}
}
- *numkeys = 0;
- return keys;
+ result->numkeys = 0;
+ return result->numkeys;
}
/* Helper function to extract keys from memory command.
* MEMORY USAGE <key> */
-int *memoryGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
- int *keys;
+int memoryGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
UNUSED(cmd);
+ getKeysPrepareResult(result, 1);
if (argc >= 3 && !strcasecmp(argv[1]->ptr,"usage")) {
- keys = getKeysTempBuffer;
- keys[0] = 2;
- *numkeys = 1;
- return keys;
+ result->keys[0] = 2;
+ result->numkeys = 1;
+ return result->numkeys;
}
- *numkeys = 0;
- return NULL;
+ result->numkeys = 0;
+ return 0;
}
/* XREAD [BLOCK <milliseconds>] [COUNT <count>] [GROUP <groupname> <ttl>]
* STREAMS key_1 key_2 ... key_N ID_1 ID_2 ... ID_N */
-int *xreadGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
+int xreadGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
int i, num = 0, *keys;
UNUSED(cmd);
@@ -1629,19 +1647,16 @@ int *xreadGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys)
/* Syntax error. */
if (streams_pos == -1 || num == 0 || num % 2 != 0) {
- *numkeys = 0;
- return NULL;
+ result->numkeys = 0;
+ return 0;
}
num /= 2; /* We have half the keys as there are arguments because
there are also the IDs, one per key. */
- keys = getKeysTempBuffer;
- if (num>MAX_KEYS_BUFFER)
- keys = zmalloc(sizeof(int) * num);
-
+ keys = getKeysPrepareResult(result, num);
for (i = streams_pos+1; i < argc-num; i++) keys[i-streams_pos-1] = i;
- *numkeys = num;
- return keys;
+ result->numkeys = num;
+ return num;
}
/* Slot to Key API. This is used by Redis Cluster in order to obtain in
diff --git a/src/module.c b/src/module.c
index 3f3d92aea..a48780be9 100644
--- a/src/module.c
+++ b/src/module.c
@@ -147,8 +147,7 @@ struct RedisModuleCtx {
on keys. */
/* Used if there is the REDISMODULE_CTX_KEYS_POS_REQUEST flag set. */
- int *keys_pos;
- int keys_count;
+ getKeysResult *keys_result;
struct RedisModulePoolAllocBlock *pa_head;
redisOpArray saved_oparray; /* When propagating commands in a callback
@@ -158,7 +157,7 @@ struct RedisModuleCtx {
};
typedef struct RedisModuleCtx RedisModuleCtx;
-#define REDISMODULE_CTX_INIT {(void*)(unsigned long)&RM_GetApi, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, NULL, 0, NULL, {0}}
+#define REDISMODULE_CTX_INIT {(void*)(unsigned long)&RM_GetApi, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, NULL, NULL, {0}}
#define REDISMODULE_CTX_MULTI_EMITTED (1<<0)
#define REDISMODULE_CTX_AUTO_MEMORY (1<<1)
#define REDISMODULE_CTX_KEYS_POS_REQUEST (1<<2)
@@ -665,18 +664,24 @@ void RedisModuleCommandDispatcher(client *c) {
* In order to accomplish its work, the module command is called, flagging
* the context in a way that the command can recognize this is a special
* "get keys" call by calling RedisModule_IsKeysPositionRequest(ctx). */
-int *moduleGetCommandKeysViaAPI(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
+int moduleGetCommandKeysViaAPI(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
RedisModuleCommandProxy *cp = (void*)(unsigned long)cmd->getkeys_proc;
RedisModuleCtx ctx = REDISMODULE_CTX_INIT;
ctx.module = cp->module;
ctx.client = NULL;
ctx.flags |= REDISMODULE_CTX_KEYS_POS_REQUEST;
+
+ /* Initialize getKeysResult */
+ getKeysPrepareResult(result, MAX_KEYS_BUFFER);
+ ctx.keys_result = result;
+
cp->func(&ctx,(void**)argv,argc);
- int *res = ctx.keys_pos;
- if (numkeys) *numkeys = ctx.keys_count;
+ /* We currently always use the array allocated by RM_KeyAtPos() and don't try
+ * to optimize for the pre-allocated buffer.
+ */
moduleFreeContext(&ctx);
- return res;
+ return result->numkeys;
}
/* Return non-zero if a module command, that was declared with the
@@ -701,10 +706,18 @@ int RM_IsKeysPositionRequest(RedisModuleCtx *ctx) {
* keys are at fixed positions. This interface is only used for commands
* with a more complex structure. */
void RM_KeyAtPos(RedisModuleCtx *ctx, int pos) {
- if (!(ctx->flags & REDISMODULE_CTX_KEYS_POS_REQUEST)) return;
+ if (!(ctx->flags & REDISMODULE_CTX_KEYS_POS_REQUEST) || !ctx->keys_result) return;
if (pos <= 0) return;
- ctx->keys_pos = zrealloc(ctx->keys_pos,sizeof(int)*(ctx->keys_count+1));
- ctx->keys_pos[ctx->keys_count++] = pos;
+
+ getKeysResult *res = ctx->keys_result;
+
+ /* Check overflow */
+ if (res->numkeys == res->size) {
+ int newsize = res->size + (res->size > 8192 ? 8192 : res->size);
+ getKeysPrepareResult(res, newsize);
+ }
+
+ res->keys[res->numkeys++] = pos;
}
/* Helper for RM_CreateCommand(). Turns a string representing command
diff --git a/src/server.c b/src/server.c
index ef40b4fff..6ae7037af 100644
--- a/src/server.c
+++ b/src/server.c
@@ -3273,7 +3273,7 @@ struct redisCommand *lookupCommand(sds name) {
return dictFetchValue(server.commands, name);
}
-struct redisCommand *lookupCommandByCString(char *s) {
+struct redisCommand *lookupCommandByCString(const char *s) {
struct redisCommand *cmd;
sds name = sdsnew(s);
@@ -4142,7 +4142,8 @@ NULL
addReplyLongLong(c, dictSize(server.commands));
} else if (!strcasecmp(c->argv[1]->ptr,"getkeys") && c->argc >= 3) {
struct redisCommand *cmd = lookupCommand(c->argv[2]->ptr);
- int *keys, numkeys, j;
+ getKeysResult result = GETKEYS_RESULT_INIT;
+ int j;
if (!cmd) {
addReplyError(c,"Invalid command specified");
@@ -4157,14 +4158,13 @@ NULL
return;
}
- keys = getKeysFromCommand(cmd,c->argv+2,c->argc-2,&numkeys);
- if (!keys) {
+ if (!getKeysFromCommand(cmd,c->argv+2,c->argc-2,&result)) {
addReplyError(c,"Invalid arguments specified for command");
} else {
- addReplyArrayLen(c,numkeys);
- for (j = 0; j < numkeys; j++) addReplyBulk(c,c->argv[keys[j]+2]);
- getKeysFreeResult(keys);
+ addReplyArrayLen(c,result.numkeys);
+ for (j = 0; j < result.numkeys; j++) addReplyBulk(c,c->argv[result.keys[j]+2]);
}
+ getKeysFreeResult(&result);
} else {
addReplySubcommandSyntaxError(c);
}
diff --git a/src/server.h b/src/server.h
index 50083652e..a5881e9a2 100644
--- a/src/server.h
+++ b/src/server.h
@@ -1497,8 +1497,21 @@ typedef struct pubsubPattern {
robj *pattern;
} pubsubPattern;
+#define MAX_KEYS_BUFFER 256
+
+/* A result structure for the various getkeys function calls. It lists the
+ * keys as indices to the provided argv.
+ */
+typedef struct {
+ int keysbuf[MAX_KEYS_BUFFER]; /* Pre-allocated buffer, to save heap allocations */
+ int *keys; /* Key indices array, points to keysbuf or heap */
+ int numkeys; /* Number of key indices return */
+ int size; /* Available array size */
+} getKeysResult;
+#define GETKEYS_RESULT_INIT { {0}, NULL, 0, MAX_KEYS_BUFFER }
+
typedef void redisCommandProc(client *c);
-typedef int *redisGetKeysProc(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
+typedef int redisGetKeysProc(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
struct redisCommand {
char *name;
redisCommandProc *proc;
@@ -1608,7 +1621,7 @@ void moduleInitModulesSystem(void);
void moduleInitModulesSystemLast(void);
int moduleLoad(const char *path, void **argv, int argc);
void moduleLoadFromQueue(void);
-int *moduleGetCommandKeysViaAPI(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
+int moduleGetCommandKeysViaAPI(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
moduleType *moduleTypeLookupModuleByID(uint64_t id);
void moduleTypeNameByID(char *name, uint64_t moduleid);
void moduleFreeContext(struct RedisModuleCtx *ctx);
@@ -2020,7 +2033,7 @@ int processCommand(client *c);
void setupSignalHandlers(void);
void removeSignalHandlers(void);
struct redisCommand *lookupCommand(sds name);
-struct redisCommand *lookupCommandByCString(char *s);
+struct redisCommand *lookupCommandByCString(const char *s);
struct redisCommand *lookupCommandOrOriginal(sds name);
void call(client *c, int flags);
void propagate(struct redisCommand *cmd, int dbid, robj **argv, int argc, int flags);
@@ -2181,17 +2194,18 @@ size_t lazyfreeGetPendingObjectsCount(void);
void freeObjAsync(robj *o);
/* API to get key arguments from commands */
-int *getKeysFromCommand(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
-void getKeysFreeResult(int *result);
-int *zunionInterGetKeys(struct redisCommand *cmd,robj **argv, int argc, int *numkeys);
-int *zunionInterStoreGetKeys(struct redisCommand *cmd,robj **argv, int argc, int *numkeys);
-int *evalGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
-int *sortGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
-int *migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
-int *georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
-int *xreadGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
-int *memoryGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
-int *lcsGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
+int *getKeysPrepareResult(getKeysResult *result, int numkeys);
+int getKeysFromCommand(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
+void getKeysFreeResult(getKeysResult *result);
+int zunionInterGetKeys(struct redisCommand *cmd,robj **argv, int argc, getKeysResult *result);
+int zunionInterStoreGetKeys(struct redisCommand *cmd,robj **argv, int argc, getKeysResult *result);
+int evalGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
+int sortGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
+int migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
+int georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
+int xreadGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
+int memoryGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
+int lcsGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
/* Cluster */
void clusterInit(void);
diff --git a/src/tracking.c b/src/tracking.c
index 3737f6859..913577eab 100644
--- a/src/tracking.c
+++ b/src/tracking.c
@@ -171,9 +171,14 @@ void trackingRememberKeys(client *c) {
uint64_t caching_given = c->flags & CLIENT_TRACKING_CACHING;
if ((optin && !caching_given) || (optout && caching_given)) return;
- int numkeys;
- int *keys = getKeysFromCommand(c->cmd,c->argv,c->argc,&numkeys);
- if (keys == NULL) return;
+ getKeysResult result = GETKEYS_RESULT_INIT;
+ int numkeys = getKeysFromCommand(c->cmd,c->argv,c->argc,&result);
+ if (!numkeys) {
+ getKeysFreeResult(&result);
+ return;
+ }
+
+ int *keys = result.keys;
for(int j = 0; j < numkeys; j++) {
int idx = keys[j];
@@ -188,7 +193,7 @@ void trackingRememberKeys(client *c) {
if (raxTryInsert(ids,(unsigned char*)&c->id,sizeof(c->id),NULL,NULL))
TrackingTableTotalItems++;
}
- getKeysFreeResult(keys);
+ getKeysFreeResult(&result);
}
/* Given a key name, this function sends an invalidation message in the