mirror of
https://codeberg.org/redict/redict.git
synced 2025-01-23 16:48:27 -05:00
92fb4f4f61
The SET and BITFIELD command were added `get_keys_function` in #10148, causing them to be wrongly marked movablekeys in `populateCommandMovableKeys`. This was an unintended side effect introduced in #10148 (7.0 RC1) which could cause some clients an extra round trip for these commands in cluster mode. Since we define movablekeys as a way to determine if the legacy range [first, last, step] doesn't find all keys, then we need a completely different approach. The right approach should be to check if the legacy range covers all key-specs, and if none of the key-specs have the INCOMPLETE flag. This way, we don't need to look at getkeys_proc of VARIABLE_FLAG at all. Probably with the exception of modules, who may still not be using key-specs. In this PR, we removed `populateCommandMovableKeys` and put its logic in `populateCommandLegacyRangeSpec`. In order to properly serve both old and new modules, we must probably keep relying CMD_MODULE_GETKEYS, but do that only for modules that don't declare key-specs. For ones that do, we need to take the same approach we take with native redis commands. This approach was proposed by Oran. Fixes #10833 Co-authored-by: Oran Agra <oran@redislabs.com>
228 lines
9.0 KiB
C
228 lines
9.0 KiB
C
#include "redismodule.h"
|
|
|
|
#define UNUSED(V) ((void) V)
|
|
|
|
/* This function implements all commands in this module. All we care about is
|
|
* the COMMAND metadata anyway. */
|
|
int kspec_impl(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
|
UNUSED(argv);
|
|
UNUSED(argc);
|
|
RedisModule_ReplyWithSimpleString(ctx, "OK");
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int createKspecNone(RedisModuleCtx *ctx) {
|
|
/* A command without keyspecs; only the legacy (first,last,step) triple (MSET like spec). */
|
|
if (RedisModule_CreateCommand(ctx,"kspec.none",kspec_impl,"",1,-1,2) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int createKspecNoneWithGetkeys(RedisModuleCtx *ctx) {
|
|
/* A command without keyspecs; only the legacy (first,last,step) triple (MSET like spec), but also has a getkeys callback */
|
|
if (RedisModule_CreateCommand(ctx,"kspec.nonewithgetkeys",kspec_impl,"getkeys-api",1,-1,2) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int createKspecTwoRanges(RedisModuleCtx *ctx) {
|
|
/* Test that two position/range-based key specs are combined to produce the
|
|
* legacy (first,last,step) values representing both keys. */
|
|
if (RedisModule_CreateCommand(ctx,"kspec.tworanges",kspec_impl,"",0,0,0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
|
|
RedisModuleCommand *command = RedisModule_GetCommand(ctx,"kspec.tworanges");
|
|
RedisModuleCommandInfo info = {
|
|
.version = REDISMODULE_COMMAND_INFO_VERSION,
|
|
.arity = -2,
|
|
.key_specs = (RedisModuleCommandKeySpec[]){
|
|
{
|
|
.flags = REDISMODULE_CMD_KEY_RO | REDISMODULE_CMD_KEY_ACCESS,
|
|
.begin_search_type = REDISMODULE_KSPEC_BS_INDEX,
|
|
.bs.index.pos = 1,
|
|
.find_keys_type = REDISMODULE_KSPEC_FK_RANGE,
|
|
.fk.range = {0,1,0}
|
|
},
|
|
{
|
|
.flags = REDISMODULE_CMD_KEY_RW | REDISMODULE_CMD_KEY_UPDATE,
|
|
.begin_search_type = REDISMODULE_KSPEC_BS_INDEX,
|
|
.bs.index.pos = 2,
|
|
/* Omitted find_keys_type is shorthand for RANGE {0,1,0} */
|
|
},
|
|
{0}
|
|
}
|
|
};
|
|
if (RedisModule_SetCommandInfo(command, &info) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int createKspecTwoRangesWithGap(RedisModuleCtx *ctx) {
|
|
/* Test that two position/range-based key specs are combined to produce the
|
|
* legacy (first,last,step) values representing just one key. */
|
|
if (RedisModule_CreateCommand(ctx,"kspec.tworangeswithgap",kspec_impl,"",0,0,0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
|
|
RedisModuleCommand *command = RedisModule_GetCommand(ctx,"kspec.tworangeswithgap");
|
|
RedisModuleCommandInfo info = {
|
|
.version = REDISMODULE_COMMAND_INFO_VERSION,
|
|
.arity = -2,
|
|
.key_specs = (RedisModuleCommandKeySpec[]){
|
|
{
|
|
.flags = REDISMODULE_CMD_KEY_RO | REDISMODULE_CMD_KEY_ACCESS,
|
|
.begin_search_type = REDISMODULE_KSPEC_BS_INDEX,
|
|
.bs.index.pos = 1,
|
|
.find_keys_type = REDISMODULE_KSPEC_FK_RANGE,
|
|
.fk.range = {0,1,0}
|
|
},
|
|
{
|
|
.flags = REDISMODULE_CMD_KEY_RW | REDISMODULE_CMD_KEY_UPDATE,
|
|
.begin_search_type = REDISMODULE_KSPEC_BS_INDEX,
|
|
.bs.index.pos = 3,
|
|
/* Omitted find_keys_type is shorthand for RANGE {0,1,0} */
|
|
},
|
|
{0}
|
|
}
|
|
};
|
|
if (RedisModule_SetCommandInfo(command, &info) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int createKspecKeyword(RedisModuleCtx *ctx) {
|
|
/* Only keyword-based specs. The legacy triple is wiped and set to (0,0,0). */
|
|
if (RedisModule_CreateCommand(ctx,"kspec.keyword",kspec_impl,"",3,-1,1) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
|
|
RedisModuleCommand *command = RedisModule_GetCommand(ctx,"kspec.keyword");
|
|
RedisModuleCommandInfo info = {
|
|
.version = REDISMODULE_COMMAND_INFO_VERSION,
|
|
.key_specs = (RedisModuleCommandKeySpec[]){
|
|
{
|
|
.flags = REDISMODULE_CMD_KEY_RO | REDISMODULE_CMD_KEY_ACCESS,
|
|
.begin_search_type = REDISMODULE_KSPEC_BS_KEYWORD,
|
|
.bs.keyword.keyword = "KEYS",
|
|
.bs.keyword.startfrom = 1,
|
|
.find_keys_type = REDISMODULE_KSPEC_FK_RANGE,
|
|
.fk.range = {-1,1,0}
|
|
},
|
|
{0}
|
|
}
|
|
};
|
|
if (RedisModule_SetCommandInfo(command, &info) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int createKspecComplex1(RedisModuleCtx *ctx) {
|
|
/* First is a range a single key. The rest are keyword-based specs. */
|
|
if (RedisModule_CreateCommand(ctx,"kspec.complex1",kspec_impl,"",1,1,1) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
|
|
RedisModuleCommand *command = RedisModule_GetCommand(ctx,"kspec.complex1");
|
|
RedisModuleCommandInfo info = {
|
|
.version = REDISMODULE_COMMAND_INFO_VERSION,
|
|
.key_specs = (RedisModuleCommandKeySpec[]){
|
|
{
|
|
.flags = REDISMODULE_CMD_KEY_RO,
|
|
.begin_search_type = REDISMODULE_KSPEC_BS_INDEX,
|
|
.bs.index.pos = 1,
|
|
},
|
|
{
|
|
.flags = REDISMODULE_CMD_KEY_RW | REDISMODULE_CMD_KEY_UPDATE,
|
|
.begin_search_type = REDISMODULE_KSPEC_BS_KEYWORD,
|
|
.bs.keyword.keyword = "STORE",
|
|
.bs.keyword.startfrom = 2,
|
|
},
|
|
{
|
|
.flags = REDISMODULE_CMD_KEY_RO | REDISMODULE_CMD_KEY_ACCESS,
|
|
.begin_search_type = REDISMODULE_KSPEC_BS_KEYWORD,
|
|
.bs.keyword.keyword = "KEYS",
|
|
.bs.keyword.startfrom = 2,
|
|
.find_keys_type = REDISMODULE_KSPEC_FK_KEYNUM,
|
|
.fk.keynum = {0,1,1}
|
|
},
|
|
{0}
|
|
}
|
|
};
|
|
if (RedisModule_SetCommandInfo(command, &info) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int createKspecComplex2(RedisModuleCtx *ctx) {
|
|
/* First is not legacy, more than STATIC_KEYS_SPECS_NUM specs */
|
|
if (RedisModule_CreateCommand(ctx,"kspec.complex2",kspec_impl,"",0,0,0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
|
|
RedisModuleCommand *command = RedisModule_GetCommand(ctx,"kspec.complex2");
|
|
RedisModuleCommandInfo info = {
|
|
.version = REDISMODULE_COMMAND_INFO_VERSION,
|
|
.key_specs = (RedisModuleCommandKeySpec[]){
|
|
{
|
|
.flags = REDISMODULE_CMD_KEY_RW | REDISMODULE_CMD_KEY_UPDATE,
|
|
.begin_search_type = REDISMODULE_KSPEC_BS_KEYWORD,
|
|
.bs.keyword.keyword = "STORE",
|
|
.bs.keyword.startfrom = 5,
|
|
.find_keys_type = REDISMODULE_KSPEC_FK_RANGE,
|
|
.fk.range = {0,1,0}
|
|
},
|
|
{
|
|
.flags = REDISMODULE_CMD_KEY_RO | REDISMODULE_CMD_KEY_ACCESS,
|
|
.begin_search_type = REDISMODULE_KSPEC_BS_INDEX,
|
|
.bs.index.pos = 1,
|
|
.find_keys_type = REDISMODULE_KSPEC_FK_RANGE,
|
|
.fk.range = {0,1,0}
|
|
},
|
|
{
|
|
.flags = REDISMODULE_CMD_KEY_RO | REDISMODULE_CMD_KEY_ACCESS,
|
|
.begin_search_type = REDISMODULE_KSPEC_BS_INDEX,
|
|
.bs.index.pos = 2,
|
|
.find_keys_type = REDISMODULE_KSPEC_FK_RANGE,
|
|
.fk.range = {0,1,0}
|
|
},
|
|
{
|
|
.flags = REDISMODULE_CMD_KEY_RW | REDISMODULE_CMD_KEY_UPDATE,
|
|
.begin_search_type = REDISMODULE_KSPEC_BS_INDEX,
|
|
.bs.index.pos = 3,
|
|
.find_keys_type = REDISMODULE_KSPEC_FK_KEYNUM,
|
|
.fk.keynum = {0,1,1}
|
|
},
|
|
{
|
|
.flags = REDISMODULE_CMD_KEY_RW | REDISMODULE_CMD_KEY_UPDATE,
|
|
.begin_search_type = REDISMODULE_KSPEC_BS_KEYWORD,
|
|
.bs.keyword.keyword = "MOREKEYS",
|
|
.bs.keyword.startfrom = 5,
|
|
.find_keys_type = REDISMODULE_KSPEC_FK_RANGE,
|
|
.fk.range = {-1,1,0}
|
|
},
|
|
{0}
|
|
}
|
|
};
|
|
if (RedisModule_SetCommandInfo(command, &info) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
|
REDISMODULE_NOT_USED(argv);
|
|
REDISMODULE_NOT_USED(argc);
|
|
|
|
if (RedisModule_Init(ctx, "keyspecs", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
|
|
if (createKspecNone(ctx) == REDISMODULE_ERR) return REDISMODULE_ERR;
|
|
if (createKspecNoneWithGetkeys(ctx) == REDISMODULE_ERR) return REDISMODULE_ERR;
|
|
if (createKspecTwoRanges(ctx) == REDISMODULE_ERR) return REDISMODULE_ERR;
|
|
if (createKspecTwoRangesWithGap(ctx) == REDISMODULE_ERR) return REDISMODULE_ERR;
|
|
if (createKspecKeyword(ctx) == REDISMODULE_ERR) return REDISMODULE_ERR;
|
|
if (createKspecComplex1(ctx) == REDISMODULE_ERR) return REDISMODULE_ERR;
|
|
if (createKspecComplex2(ctx) == REDISMODULE_ERR) return REDISMODULE_ERR;
|
|
return REDISMODULE_OK;
|
|
}
|