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.
This commit is contained in:
Yossi Gottlieb 2020-10-05 17:03:17 +03:00
parent e66e79aa9f
commit bf5beab64a
7 changed files with 171 additions and 120 deletions

View File

@ -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

View File

@ -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

169
src/db.c
View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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);

View File

@ -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