Add API RedisModule_ClusterKeySlot and RedisModule_ClusterCanonicalKeyNameInSlot (#13069)

Sometimes it's useful to compute a key's cluster slot in a module.

This API function is just like the command CLUSTER KEYSLOT (but faster).

A "reverse" API is also added:
`RedisModule_ClusterCanonicalKeyNameInSlot`. Given a slot, it returns a
short string that we can call a canonical key for the slot.
This commit is contained in:
Viktor Söderqvist 2024-03-12 17:26:12 +01:00 committed by GitHub
parent 9c065c417d
commit 9efc6ad6a6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 42 additions and 0 deletions

View File

@ -59,6 +59,7 @@
#include "script.h"
#include "call_reply.h"
#include "hdr_histogram.h"
#include "crc16_slottable.h"
#include <dlfcn.h>
#include <sys/stat.h>
#include <sys/wait.h>
@ -9107,6 +9108,19 @@ void RM_SetClusterFlags(RedisModuleCtx *ctx, uint64_t flags) {
server.cluster_module_flags |= CLUSTER_MODULE_FLAG_NO_REDIRECTION;
}
/* Returns the cluster slot of a key, similar to the `CLUSTER KEYSLOT` command.
* This function works even if cluster mode is not enabled. */
unsigned int RM_ClusterKeySlot(RedisModuleString *key) {
return keyHashSlot(key->ptr, sdslen(key->ptr));
}
/* Returns a short string that can be used as a key or as a hash tag in a key,
* such that the key maps to the given cluster slot. Returns NULL if slot is not
* a valid slot. */
const char *RM_ClusterCanonicalKeyNameInSlot(unsigned int slot) {
return (slot < CLUSTER_SLOTS) ? crc16_slot_table[slot] : NULL;
}
/* --------------------------------------------------------------------------
* ## Modules Timers API
*
@ -13844,6 +13858,8 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API(SetDisconnectCallback);
REGISTER_API(GetBlockedClientHandle);
REGISTER_API(SetClusterFlags);
REGISTER_API(ClusterKeySlot);
REGISTER_API(ClusterCanonicalKeyNameInSlot);
REGISTER_API(CreateDict);
REGISTER_API(FreeDict);
REGISTER_API(DictSize);

View File

@ -1253,6 +1253,8 @@ REDISMODULE_API void (*RedisModule_GetRandomBytes)(unsigned char *dst, size_t le
REDISMODULE_API void (*RedisModule_GetRandomHexChars)(char *dst, size_t len) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_SetDisconnectCallback)(RedisModuleBlockedClient *bc, RedisModuleDisconnectFunc callback) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_SetClusterFlags)(RedisModuleCtx *ctx, uint64_t flags) REDISMODULE_ATTR;
REDISMODULE_API unsigned int (*RedisModule_ClusterKeySlot)(RedisModuleString *key) REDISMODULE_ATTR;
REDISMODULE_API const char *(*RedisModule_ClusterCanonicalKeyNameInSlot)(unsigned int slot) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ExportSharedAPI)(RedisModuleCtx *ctx, const char *apiname, void *func) REDISMODULE_ATTR;
REDISMODULE_API void * (*RedisModule_GetSharedAPI)(RedisModuleCtx *ctx, const char *apiname) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleCommandFilter * (*RedisModule_RegisterCommandFilter)(RedisModuleCtx *ctx, RedisModuleCommandFilterFunc cb, int flags) REDISMODULE_ATTR;
@ -1617,6 +1619,8 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API(GetRandomBytes);
REDISMODULE_GET_API(GetRandomHexChars);
REDISMODULE_GET_API(SetClusterFlags);
REDISMODULE_GET_API(ClusterKeySlot);
REDISMODULE_GET_API(ClusterCanonicalKeyNameInSlot);
REDISMODULE_GET_API(ExportSharedAPI);
REDISMODULE_GET_API(GetSharedAPI);
REDISMODULE_GET_API(RegisterCommandFilter);

View File

@ -524,6 +524,22 @@ int test_malloc_api(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
return REDISMODULE_OK;
}
int test_keyslot(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
/* Static check of the ClusterKeySlot + ClusterCanonicalKeyNameInSlot
* round-trip for all slots. */
for (unsigned int slot = 0; slot < 16384; slot++) {
const char *tag = RedisModule_ClusterCanonicalKeyNameInSlot(slot);
RedisModuleString *key = RedisModule_CreateStringPrintf(ctx, "x{%s}y", tag);
assert(slot == RedisModule_ClusterKeySlot(key));
RedisModule_FreeString(ctx, key);
}
if (argc != 2){
return RedisModule_WrongArity(ctx);
}
unsigned int slot = RedisModule_ClusterKeySlot(argv[1]);
return RedisModule_ReplyWithLongLong(ctx, slot);
}
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
REDISMODULE_NOT_USED(argv);
REDISMODULE_NOT_USED(argc);
@ -589,6 +605,8 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx, "test.malloc_api", test_malloc_api,"", 0, 0, 0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx, "test.keyslot", test_keyslot, "", 0, 0, 0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
return REDISMODULE_OK;
}

View File

@ -497,6 +497,10 @@ start_server {overrides {save {900 1}} tags {"modules"}} {
test "malloc API" {
assert_equal {OK} [r test.malloc_api 0]
}
test "Cluster keyslot" {
assert_equal 12182 [r test.keyslot foo]
}
}
start_server {tags {"modules"}} {