mirror of
https://github.com/redis/redis.git
synced 2026-04-21 03:01:35 -04:00
### Background The program runs normally in standalone mode, but migrating to cluster mode may cause errors, this is because some cross slot commands can not run in cluster mode. We should provide an approach to detect this issue when running in standalone mode, and need to expose a metric which indicates the usage of no incompatible commands. ### Solution To avoid perf impact, we introduce a new config `cluster-compatibility-sample-ratio` which define the sampling ratio (0-100) for checking command compatibility in cluster mode. When a command is executed, it is sampled at the specified ratio to determine if it complies with Redis cluster constraints, such as cross-slot restrictions. A new metric is exposed: `cluster_incompatible_ops` in `info stats` output. The following operations will be considered incompatible operations. - cross-slot command If a command has multiple cross slot keys, it is incompatible - `swap, copy, move, select` command These commands involve multi databases in some cases, we don't allow multiple DB in cluster mode, so there are not compatible - Module command with `no-cluster` flag If a module command has `no-cluster` flag, we will encounter an error when loading module, leading to fail to load module if cluster is enabled, so this is incompatible. - Script/function with `no-cluster` flag Similar with module command, if we declare `no-cluster` in shebang of script/function, we also can not run it in cluster mode - `sort` command by/get pattern When `sort` command has `by/get` pattern option, we must ask that the pattern slot is equal with the slot of keys, otherwise it is incompatible in cluster mode. - The script/function command accesses the keys and declared keys have different slots For the script/function command, we not only check the slot of declared keys, but only check the slot the accessing keys, if they are different, we think it is incompatible. **Besides**, commands like `keys, scan, flushall, script/function flush`, that in standalone mode iterate over all data to perform the operation, are only valid for the server that executes the command in cluster mode and are not broadcasted. However, this does not lead to errors, so we do not consider them as incompatible commands. ### Performance impact test **cross slot test** Below are the test commands and results. When using MSET with 8 keys, performance drops by approximately 3%. **single key test** It may be due to the overhead of the sampling function, and single-key commands could cause a 1-2% performance drop.
628 lines
22 KiB
C
628 lines
22 KiB
C
#include "redismodule.h"
|
|
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
|
|
#define UNUSED(x) (void)(x)
|
|
|
|
static int n_events = 0;
|
|
|
|
static int KeySpace_NotificationModuleKeyMissExpired(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key) {
|
|
UNUSED(ctx);
|
|
UNUSED(type);
|
|
UNUSED(event);
|
|
UNUSED(key);
|
|
n_events++;
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int test_clear_n_events(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
|
UNUSED(argv);
|
|
UNUSED(argc);
|
|
n_events = 0;
|
|
RedisModule_ReplyWithSimpleString(ctx, "OK");
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int test_get_n_events(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
|
UNUSED(argv);
|
|
UNUSED(argc);
|
|
RedisModule_ReplyWithLongLong(ctx, n_events);
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int test_open_key_no_effects(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
|
if (argc<2) {
|
|
RedisModule_WrongArity(ctx);
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int supportedMode = RedisModule_GetOpenKeyModesAll();
|
|
if (!(supportedMode & REDISMODULE_READ) || !(supportedMode & REDISMODULE_OPEN_KEY_NOEFFECTS)) {
|
|
RedisModule_ReplyWithError(ctx, "OpenKey modes are not supported");
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ | REDISMODULE_OPEN_KEY_NOEFFECTS);
|
|
if (!key) {
|
|
RedisModule_ReplyWithError(ctx, "key not found");
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
RedisModule_CloseKey(key);
|
|
RedisModule_ReplyWithSimpleString(ctx, "OK");
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int test_call_generic(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
|
{
|
|
if (argc<2) {
|
|
RedisModule_WrongArity(ctx);
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
const char* cmdname = RedisModule_StringPtrLen(argv[1], NULL);
|
|
RedisModuleCallReply *reply = RedisModule_Call(ctx, cmdname, "v", argv+2, argc-2);
|
|
if (reply) {
|
|
RedisModule_ReplyWithCallReply(ctx, reply);
|
|
RedisModule_FreeCallReply(reply);
|
|
} else {
|
|
RedisModule_ReplyWithError(ctx, strerror(errno));
|
|
}
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int test_call_info(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
|
{
|
|
RedisModuleCallReply *reply;
|
|
if (argc>1)
|
|
reply = RedisModule_Call(ctx, "info", "s", argv[1]);
|
|
else
|
|
reply = RedisModule_Call(ctx, "info", "");
|
|
if (reply) {
|
|
RedisModule_ReplyWithCallReply(ctx, reply);
|
|
RedisModule_FreeCallReply(reply);
|
|
} else {
|
|
RedisModule_ReplyWithError(ctx, strerror(errno));
|
|
}
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int test_ld_conv(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
|
UNUSED(argv);
|
|
UNUSED(argc);
|
|
long double ld = 0.00000000000000001L;
|
|
const char *ldstr = "0.00000000000000001";
|
|
RedisModuleString *s1 = RedisModule_CreateStringFromLongDouble(ctx, ld, 1);
|
|
RedisModuleString *s2 =
|
|
RedisModule_CreateString(ctx, ldstr, strlen(ldstr));
|
|
if (RedisModule_StringCompare(s1, s2) != 0) {
|
|
char err[4096];
|
|
snprintf(err, 4096,
|
|
"Failed to convert long double to string ('%s' != '%s')",
|
|
RedisModule_StringPtrLen(s1, NULL),
|
|
RedisModule_StringPtrLen(s2, NULL));
|
|
RedisModule_ReplyWithError(ctx, err);
|
|
goto final;
|
|
}
|
|
long double ld2 = 0;
|
|
if (RedisModule_StringToLongDouble(s2, &ld2) == REDISMODULE_ERR) {
|
|
RedisModule_ReplyWithError(ctx,
|
|
"Failed to convert string to long double");
|
|
goto final;
|
|
}
|
|
if (ld2 != ld) {
|
|
char err[4096];
|
|
snprintf(err, 4096,
|
|
"Failed to convert string to long double (%.40Lf != %.40Lf)",
|
|
ld2,
|
|
ld);
|
|
RedisModule_ReplyWithError(ctx, err);
|
|
goto final;
|
|
}
|
|
|
|
/* Make sure we can't convert a string that has \0 in it */
|
|
char buf[4] = "123";
|
|
buf[1] = '\0';
|
|
RedisModuleString *s3 = RedisModule_CreateString(ctx, buf, 3);
|
|
long double ld3;
|
|
if (RedisModule_StringToLongDouble(s3, &ld3) == REDISMODULE_OK) {
|
|
RedisModule_ReplyWithError(ctx, "Invalid string successfully converted to long double");
|
|
RedisModule_FreeString(ctx, s3);
|
|
goto final;
|
|
}
|
|
RedisModule_FreeString(ctx, s3);
|
|
|
|
RedisModule_ReplyWithLongDouble(ctx, ld2);
|
|
final:
|
|
RedisModule_FreeString(ctx, s1);
|
|
RedisModule_FreeString(ctx, s2);
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int test_flushall(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
|
{
|
|
REDISMODULE_NOT_USED(argv);
|
|
REDISMODULE_NOT_USED(argc);
|
|
RedisModule_ResetDataset(1, 0);
|
|
RedisModule_ReplyWithCString(ctx, "Ok");
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int test_dbsize(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
|
{
|
|
REDISMODULE_NOT_USED(argv);
|
|
REDISMODULE_NOT_USED(argc);
|
|
long long ll = RedisModule_DbSize(ctx);
|
|
RedisModule_ReplyWithLongLong(ctx, ll);
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int test_randomkey(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
|
{
|
|
REDISMODULE_NOT_USED(argv);
|
|
REDISMODULE_NOT_USED(argc);
|
|
RedisModuleString *str = RedisModule_RandomKey(ctx);
|
|
RedisModule_ReplyWithString(ctx, str);
|
|
RedisModule_FreeString(ctx, str);
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int test_keyexists(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
|
if (argc < 2) return RedisModule_WrongArity(ctx);
|
|
RedisModuleString *key = argv[1];
|
|
int exists = RedisModule_KeyExists(ctx, key);
|
|
return RedisModule_ReplyWithBool(ctx, exists);
|
|
}
|
|
|
|
RedisModuleKey *open_key_or_reply(RedisModuleCtx *ctx, RedisModuleString *keyname, int mode) {
|
|
RedisModuleKey *key = RedisModule_OpenKey(ctx, keyname, mode);
|
|
if (!key) {
|
|
RedisModule_ReplyWithError(ctx, "key not found");
|
|
return NULL;
|
|
}
|
|
return key;
|
|
}
|
|
|
|
int test_getlru(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
|
{
|
|
if (argc<2) {
|
|
RedisModule_WrongArity(ctx);
|
|
return REDISMODULE_OK;
|
|
}
|
|
RedisModuleKey *key = open_key_or_reply(ctx, argv[1], REDISMODULE_READ|REDISMODULE_OPEN_KEY_NOTOUCH);
|
|
mstime_t lru;
|
|
RedisModule_GetLRU(key, &lru);
|
|
RedisModule_ReplyWithLongLong(ctx, lru);
|
|
RedisModule_CloseKey(key);
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int test_setlru(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
|
{
|
|
if (argc<3) {
|
|
RedisModule_WrongArity(ctx);
|
|
return REDISMODULE_OK;
|
|
}
|
|
RedisModuleKey *key = open_key_or_reply(ctx, argv[1], REDISMODULE_READ|REDISMODULE_OPEN_KEY_NOTOUCH);
|
|
mstime_t lru;
|
|
if (RedisModule_StringToLongLong(argv[2], &lru) != REDISMODULE_OK) {
|
|
RedisModule_ReplyWithError(ctx, "invalid idle time");
|
|
return REDISMODULE_OK;
|
|
}
|
|
int was_set = RedisModule_SetLRU(key, lru)==REDISMODULE_OK;
|
|
RedisModule_ReplyWithLongLong(ctx, was_set);
|
|
RedisModule_CloseKey(key);
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int test_getlfu(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
|
{
|
|
if (argc<2) {
|
|
RedisModule_WrongArity(ctx);
|
|
return REDISMODULE_OK;
|
|
}
|
|
RedisModuleKey *key = open_key_or_reply(ctx, argv[1], REDISMODULE_READ|REDISMODULE_OPEN_KEY_NOTOUCH);
|
|
mstime_t lfu;
|
|
RedisModule_GetLFU(key, &lfu);
|
|
RedisModule_ReplyWithLongLong(ctx, lfu);
|
|
RedisModule_CloseKey(key);
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int test_setlfu(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
|
{
|
|
if (argc<3) {
|
|
RedisModule_WrongArity(ctx);
|
|
return REDISMODULE_OK;
|
|
}
|
|
RedisModuleKey *key = open_key_or_reply(ctx, argv[1], REDISMODULE_READ|REDISMODULE_OPEN_KEY_NOTOUCH);
|
|
mstime_t lfu;
|
|
if (RedisModule_StringToLongLong(argv[2], &lfu) != REDISMODULE_OK) {
|
|
RedisModule_ReplyWithError(ctx, "invalid freq");
|
|
return REDISMODULE_OK;
|
|
}
|
|
int was_set = RedisModule_SetLFU(key, lfu)==REDISMODULE_OK;
|
|
RedisModule_ReplyWithLongLong(ctx, was_set);
|
|
RedisModule_CloseKey(key);
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int test_redisversion(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){
|
|
(void) argv;
|
|
(void) argc;
|
|
|
|
int version = RedisModule_GetServerVersion();
|
|
int patch = version & 0x000000ff;
|
|
int minor = (version & 0x0000ff00) >> 8;
|
|
int major = (version & 0x00ff0000) >> 16;
|
|
|
|
RedisModuleString* vStr = RedisModule_CreateStringPrintf(ctx, "%d.%d.%d", major, minor, patch);
|
|
RedisModule_ReplyWithString(ctx, vStr);
|
|
RedisModule_FreeString(ctx, vStr);
|
|
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int test_getclientcert(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
|
{
|
|
(void) argv;
|
|
(void) argc;
|
|
|
|
RedisModuleString *cert = RedisModule_GetClientCertificate(ctx,
|
|
RedisModule_GetClientId(ctx));
|
|
if (!cert) {
|
|
RedisModule_ReplyWithNull(ctx);
|
|
} else {
|
|
RedisModule_ReplyWithString(ctx, cert);
|
|
RedisModule_FreeString(ctx, cert);
|
|
}
|
|
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int test_clientinfo(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
|
{
|
|
(void) argv;
|
|
(void) argc;
|
|
|
|
RedisModuleClientInfoV1 ci = REDISMODULE_CLIENTINFO_INITIALIZER_V1;
|
|
uint64_t client_id = RedisModule_GetClientId(ctx);
|
|
|
|
/* Check expected result from the V1 initializer. */
|
|
assert(ci.version == 1);
|
|
/* Trying to populate a future version of the struct should fail. */
|
|
ci.version = REDISMODULE_CLIENTINFO_VERSION + 1;
|
|
assert(RedisModule_GetClientInfoById(&ci, client_id) == REDISMODULE_ERR);
|
|
|
|
ci.version = 1;
|
|
if (RedisModule_GetClientInfoById(&ci, client_id) == REDISMODULE_ERR) {
|
|
RedisModule_ReplyWithError(ctx, "failed to get client info");
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
RedisModule_ReplyWithArray(ctx, 10);
|
|
char flags[512];
|
|
snprintf(flags, sizeof(flags) - 1, "%s:%s:%s:%s:%s:%s",
|
|
ci.flags & REDISMODULE_CLIENTINFO_FLAG_SSL ? "ssl" : "",
|
|
ci.flags & REDISMODULE_CLIENTINFO_FLAG_PUBSUB ? "pubsub" : "",
|
|
ci.flags & REDISMODULE_CLIENTINFO_FLAG_BLOCKED ? "blocked" : "",
|
|
ci.flags & REDISMODULE_CLIENTINFO_FLAG_TRACKING ? "tracking" : "",
|
|
ci.flags & REDISMODULE_CLIENTINFO_FLAG_UNIXSOCKET ? "unixsocket" : "",
|
|
ci.flags & REDISMODULE_CLIENTINFO_FLAG_MULTI ? "multi" : "");
|
|
|
|
RedisModule_ReplyWithCString(ctx, "flags");
|
|
RedisModule_ReplyWithCString(ctx, flags);
|
|
RedisModule_ReplyWithCString(ctx, "id");
|
|
RedisModule_ReplyWithLongLong(ctx, ci.id);
|
|
RedisModule_ReplyWithCString(ctx, "addr");
|
|
RedisModule_ReplyWithCString(ctx, ci.addr);
|
|
RedisModule_ReplyWithCString(ctx, "port");
|
|
RedisModule_ReplyWithLongLong(ctx, ci.port);
|
|
RedisModule_ReplyWithCString(ctx, "db");
|
|
RedisModule_ReplyWithLongLong(ctx, ci.db);
|
|
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int test_getname(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
|
(void)argv;
|
|
if (argc != 1) return RedisModule_WrongArity(ctx);
|
|
unsigned long long id = RedisModule_GetClientId(ctx);
|
|
RedisModuleString *name = RedisModule_GetClientNameById(ctx, id);
|
|
if (name == NULL)
|
|
return RedisModule_ReplyWithError(ctx, "-ERR No name");
|
|
RedisModule_ReplyWithString(ctx, name);
|
|
RedisModule_FreeString(ctx, name);
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int test_setname(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
|
if (argc != 2) return RedisModule_WrongArity(ctx);
|
|
unsigned long long id = RedisModule_GetClientId(ctx);
|
|
if (RedisModule_SetClientNameById(id, argv[1]) == REDISMODULE_OK)
|
|
return RedisModule_ReplyWithSimpleString(ctx, "OK");
|
|
else
|
|
return RedisModule_ReplyWithError(ctx, strerror(errno));
|
|
}
|
|
|
|
int test_log_tsctx(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
|
{
|
|
RedisModuleCtx *tsctx = RedisModule_GetDetachedThreadSafeContext(ctx);
|
|
|
|
if (argc != 3) {
|
|
RedisModule_WrongArity(ctx);
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
char level[50];
|
|
size_t level_len;
|
|
const char *level_str = RedisModule_StringPtrLen(argv[1], &level_len);
|
|
snprintf(level, sizeof(level) - 1, "%.*s", (int) level_len, level_str);
|
|
|
|
size_t msg_len;
|
|
const char *msg_str = RedisModule_StringPtrLen(argv[2], &msg_len);
|
|
|
|
RedisModule_Log(tsctx, level, "%.*s", (int) msg_len, msg_str);
|
|
RedisModule_FreeThreadSafeContext(tsctx);
|
|
|
|
RedisModule_ReplyWithSimpleString(ctx, "OK");
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int test_weird_cmd(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
|
REDISMODULE_NOT_USED(argv);
|
|
REDISMODULE_NOT_USED(argc);
|
|
|
|
RedisModule_ReplyWithSimpleString(ctx, "OK");
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int test_monotonic_time(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
|
REDISMODULE_NOT_USED(argv);
|
|
REDISMODULE_NOT_USED(argc);
|
|
|
|
RedisModule_ReplyWithLongLong(ctx, RedisModule_MonotonicMicroseconds());
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
/* wrapper for RM_Call */
|
|
int test_rm_call(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){
|
|
if(argc < 2){
|
|
return RedisModule_WrongArity(ctx);
|
|
}
|
|
|
|
const char* cmd = RedisModule_StringPtrLen(argv[1], NULL);
|
|
|
|
RedisModuleCallReply* rep = RedisModule_Call(ctx, cmd, "Ev", argv + 2, argc - 2);
|
|
if(!rep){
|
|
RedisModule_ReplyWithError(ctx, "NULL reply returned");
|
|
}else{
|
|
RedisModule_ReplyWithCallReply(ctx, rep);
|
|
RedisModule_FreeCallReply(rep);
|
|
}
|
|
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
/* wrapper for RM_Call which also replicates the module command */
|
|
int test_rm_call_replicate(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){
|
|
test_rm_call(ctx, argv, argc);
|
|
RedisModule_ReplicateVerbatim(ctx);
|
|
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
/* wrapper for RM_Call with flags */
|
|
int test_rm_call_flags(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){
|
|
if(argc < 3){
|
|
return RedisModule_WrongArity(ctx);
|
|
}
|
|
|
|
/* Append Ev to the provided flags. */
|
|
RedisModuleString *flags = RedisModule_CreateStringFromString(ctx, argv[1]);
|
|
RedisModule_StringAppendBuffer(ctx, flags, "Ev", 2);
|
|
|
|
const char* flg = RedisModule_StringPtrLen(flags, NULL);
|
|
const char* cmd = RedisModule_StringPtrLen(argv[2], NULL);
|
|
|
|
RedisModuleCallReply* rep = RedisModule_Call(ctx, cmd, flg, argv + 3, argc - 3);
|
|
if(!rep){
|
|
RedisModule_ReplyWithError(ctx, "NULL reply returned");
|
|
}else{
|
|
RedisModule_ReplyWithCallReply(ctx, rep);
|
|
RedisModule_FreeCallReply(rep);
|
|
}
|
|
RedisModule_FreeString(ctx, flags);
|
|
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int test_ull_conv(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
|
UNUSED(argv);
|
|
UNUSED(argc);
|
|
unsigned long long ull = 18446744073709551615ULL;
|
|
const char *ullstr = "18446744073709551615";
|
|
|
|
RedisModuleString *s1 = RedisModule_CreateStringFromULongLong(ctx, ull);
|
|
RedisModuleString *s2 =
|
|
RedisModule_CreateString(ctx, ullstr, strlen(ullstr));
|
|
if (RedisModule_StringCompare(s1, s2) != 0) {
|
|
char err[4096];
|
|
snprintf(err, 4096,
|
|
"Failed to convert unsigned long long to string ('%s' != '%s')",
|
|
RedisModule_StringPtrLen(s1, NULL),
|
|
RedisModule_StringPtrLen(s2, NULL));
|
|
RedisModule_ReplyWithError(ctx, err);
|
|
goto final;
|
|
}
|
|
unsigned long long ull2 = 0;
|
|
if (RedisModule_StringToULongLong(s2, &ull2) == REDISMODULE_ERR) {
|
|
RedisModule_ReplyWithError(ctx,
|
|
"Failed to convert string to unsigned long long");
|
|
goto final;
|
|
}
|
|
if (ull2 != ull) {
|
|
char err[4096];
|
|
snprintf(err, 4096,
|
|
"Failed to convert string to unsigned long long (%llu != %llu)",
|
|
ull2,
|
|
ull);
|
|
RedisModule_ReplyWithError(ctx, err);
|
|
goto final;
|
|
}
|
|
|
|
/* Make sure we can't convert a string more than ULLONG_MAX or less than 0 */
|
|
ullstr = "18446744073709551616";
|
|
RedisModuleString *s3 = RedisModule_CreateString(ctx, ullstr, strlen(ullstr));
|
|
unsigned long long ull3;
|
|
if (RedisModule_StringToULongLong(s3, &ull3) == REDISMODULE_OK) {
|
|
RedisModule_ReplyWithError(ctx, "Invalid string successfully converted to unsigned long long");
|
|
RedisModule_FreeString(ctx, s3);
|
|
goto final;
|
|
}
|
|
RedisModule_FreeString(ctx, s3);
|
|
ullstr = "-1";
|
|
RedisModuleString *s4 = RedisModule_CreateString(ctx, ullstr, strlen(ullstr));
|
|
unsigned long long ull4;
|
|
if (RedisModule_StringToULongLong(s4, &ull4) == REDISMODULE_OK) {
|
|
RedisModule_ReplyWithError(ctx, "Invalid string successfully converted to unsigned long long");
|
|
RedisModule_FreeString(ctx, s4);
|
|
goto final;
|
|
}
|
|
RedisModule_FreeString(ctx, s4);
|
|
|
|
RedisModule_ReplyWithSimpleString(ctx, "ok");
|
|
|
|
final:
|
|
RedisModule_FreeString(ctx, s1);
|
|
RedisModule_FreeString(ctx, s2);
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int test_malloc_api(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
|
UNUSED(argv);
|
|
UNUSED(argc);
|
|
|
|
void *p;
|
|
|
|
p = RedisModule_TryAlloc(1024);
|
|
memset(p, 0, 1024);
|
|
RedisModule_Free(p);
|
|
|
|
p = RedisModule_TryCalloc(1, 1024);
|
|
memset(p, 1, 1024);
|
|
|
|
p = RedisModule_TryRealloc(p, 5 * 1024);
|
|
memset(p, 1, 5 * 1024);
|
|
RedisModule_Free(p);
|
|
|
|
RedisModule_ReplyWithSimpleString(ctx, "OK");
|
|
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 only_reply_ok(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
|
REDISMODULE_NOT_USED(argv);
|
|
REDISMODULE_NOT_USED(argc);
|
|
|
|
RedisModule_ReplyWithSimpleString(ctx, "OK");
|
|
return REDISMODULE_OK;
|
|
}
|
|
|
|
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
|
REDISMODULE_NOT_USED(argv);
|
|
REDISMODULE_NOT_USED(argc);
|
|
if (RedisModule_Init(ctx,"misc",1,REDISMODULE_APIVER_1)== REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
|
|
if(RedisModule_SubscribeToKeyspaceEvents(ctx, REDISMODULE_NOTIFY_KEY_MISS | REDISMODULE_NOTIFY_EXPIRED, KeySpace_NotificationModuleKeyMissExpired) != REDISMODULE_OK){
|
|
return REDISMODULE_ERR;
|
|
}
|
|
|
|
if (RedisModule_CreateCommand(ctx,"test.call_generic", test_call_generic,"",0,0,0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
if (RedisModule_CreateCommand(ctx,"test.call_info", test_call_info,"",0,0,0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
if (RedisModule_CreateCommand(ctx,"test.ld_conversion", test_ld_conv, "",0,0,0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
if (RedisModule_CreateCommand(ctx,"test.ull_conversion", test_ull_conv, "",0,0,0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
if (RedisModule_CreateCommand(ctx,"test.flushall", test_flushall,"",0,0,0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
if (RedisModule_CreateCommand(ctx,"test.dbsize", test_dbsize,"",0,0,0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
if (RedisModule_CreateCommand(ctx,"test.randomkey", test_randomkey,"",0,0,0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
if (RedisModule_CreateCommand(ctx,"test.keyexists", test_keyexists,"",1,1,1) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
if (RedisModule_CreateCommand(ctx,"test.setlru", test_setlru,"",0,0,0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
if (RedisModule_CreateCommand(ctx,"test.getlru", test_getlru,"",0,0,0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
if (RedisModule_CreateCommand(ctx,"test.setlfu", test_setlfu,"",0,0,0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
if (RedisModule_CreateCommand(ctx,"test.getlfu", test_getlfu,"",0,0,0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
if (RedisModule_CreateCommand(ctx,"test.clientinfo", test_clientinfo,"",0,0,0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
if (RedisModule_CreateCommand(ctx,"test.getname", test_getname,"",0,0,0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
if (RedisModule_CreateCommand(ctx,"test.setname", test_setname,"",0,0,0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
if (RedisModule_CreateCommand(ctx,"test.redisversion", test_redisversion,"",0,0,0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
if (RedisModule_CreateCommand(ctx,"test.getclientcert", test_getclientcert,"",0,0,0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
if (RedisModule_CreateCommand(ctx,"test.log_tsctx", test_log_tsctx,"",0,0,0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
/* Add a command with ':' in it's name, so that we can check commandstats sanitization. */
|
|
if (RedisModule_CreateCommand(ctx,"test.weird:cmd", test_weird_cmd,"readonly",0,0,0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
if (RedisModule_CreateCommand(ctx,"test.monotonic_time", test_monotonic_time,"",0,0,0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
if (RedisModule_CreateCommand(ctx, "test.rm_call", test_rm_call,"allow-stale", 0, 0, 0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
if (RedisModule_CreateCommand(ctx, "test.rm_call_flags", test_rm_call_flags,"allow-stale", 0, 0, 0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
if (RedisModule_CreateCommand(ctx, "test.rm_call_replicate", test_rm_call_replicate,"allow-stale", 0, 0, 0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
if (RedisModule_CreateCommand(ctx, "test.silent_open_key", test_open_key_no_effects,"", 0, 0, 0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
if (RedisModule_CreateCommand(ctx, "test.get_n_events", test_get_n_events,"", 0, 0, 0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
if (RedisModule_CreateCommand(ctx, "test.clear_n_events", test_clear_n_events,"", 0, 0, 0) == REDISMODULE_ERR)
|
|
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;
|
|
if (RedisModule_CreateCommand(ctx, "test.incompatible_cluster_cmd", only_reply_ok, "", 1, -1, 2) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
if (RedisModule_CreateCommand(ctx, "test.no_cluster_cmd", NULL, "no-cluster", 0, 0, 0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
RedisModuleCommand *parent = RedisModule_GetCommand(ctx, "test.no_cluster_cmd");
|
|
if (RedisModule_CreateSubcommand(parent, "set", only_reply_ok, "no-cluster", 0, 0, 0) == REDISMODULE_ERR)
|
|
return REDISMODULE_ERR;
|
|
|
|
return REDISMODULE_OK;
|
|
}
|