diff options
Diffstat (limited to 'examples/redis-unstable/tests/modules')
45 files changed, 0 insertions, 13949 deletions
diff --git a/examples/redis-unstable/tests/modules/Makefile b/examples/redis-unstable/tests/modules/Makefile deleted file mode 100644 index f141234..0000000 --- a/examples/redis-unstable/tests/modules/Makefile +++ /dev/null @@ -1,106 +0,0 @@ - -# find the OS -uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') - -warning_cflags = -W -Wall -Wno-missing-field-initializers -ifeq ($(uname_S),Darwin) - SHOBJ_CFLAGS ?= $(warning_cflags) -dynamic -fno-common -g -ggdb -std=gnu11 -O2 - SHOBJ_LDFLAGS ?= -bundle -undefined dynamic_lookup -else # Linux, others - SHOBJ_CFLAGS ?= $(warning_cflags) -fno-common -g -ggdb -std=gnu11 -O2 - SHOBJ_LDFLAGS ?= -shared -endif - -CLANG := $(findstring clang,$(shell sh -c '$(CC) --version | head -1')) - -ifeq ($(SANITIZER),memory) -ifeq (clang, $(CLANG)) - LD=clang - MALLOC=libc - CFLAGS+=-fsanitize=memory -fsanitize-memory-track-origins=2 -fno-sanitize-recover=all -fno-omit-frame-pointer - LDFLAGS+=-fsanitize=memory -else - $(error "MemorySanitizer needs to be compiled and linked with clang. Please use CC=clang") -endif -endif - - -# This is a hack to override the default CC. When running with SANITIZER=memory -# tough we want to keep the compiler as clang as MSan is not supported for gcc -ifeq ($(uname_S),Linux) -ifneq ($(SANITIZER),memory) - LD = gcc - CC = gcc -endif -endif - -# OS X 11.x doesn't have /usr/lib/libSystem.dylib and needs an explicit setting. -ifeq ($(uname_S),Darwin) -ifeq ("$(wildcard /usr/lib/libSystem.dylib)","") -LIBS = -L /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib -lsystem -endif -endif - -TEST_MODULES = \ - commandfilter.so \ - basics.so \ - testrdb.so \ - fork.so \ - infotest.so \ - propagate.so \ - misc.so \ - hooks.so \ - blockonkeys.so \ - blockonbackground.so \ - scan.so \ - datatype.so \ - datatype2.so \ - auth.so \ - keyspace_events.so \ - blockedclient.so \ - getkeys.so \ - getchannels.so \ - test_lazyfree.so \ - timer.so \ - defragtest.so \ - keyspecs.so \ - hash.so \ - zset.so \ - stream.so \ - mallocsize.so \ - aclcheck.so \ - list.so \ - subcommands.so \ - reply.so \ - cmdintrospection.so \ - eventloop.so \ - moduleconfigs.so \ - moduleconfigstwo.so \ - publish.so \ - usercall.so \ - postnotifications.so \ - moduleauthtwo.so \ - rdbloadsave.so \ - crash.so \ - internalsecret.so \ - configaccess.so \ - test_keymeta.so \ - atomicslotmigration.so - -.PHONY: all - -all: $(TEST_MODULES) - -32bit: - $(MAKE) CFLAGS="-m32" LDFLAGS="-m32" - -%.xo: %.c ../../src/redismodule.h - $(CC) -I../../src $(CFLAGS) $(SHOBJ_CFLAGS) -fPIC -c $< -o $@ - -%.so: %.xo - $(LD) -o $@ $^ $(SHOBJ_LDFLAGS) $(LDFLAGS) $(LIBS) - -.PHONY: clean - -clean: - rm -f $(TEST_MODULES) $(TEST_MODULES:.so=.xo) diff --git a/examples/redis-unstable/tests/modules/aclcheck.c b/examples/redis-unstable/tests/modules/aclcheck.c deleted file mode 100644 index d7e4e3b..0000000 --- a/examples/redis-unstable/tests/modules/aclcheck.c +++ /dev/null @@ -1,365 +0,0 @@ - -#include "redismodule.h" -#include <errno.h> -#include <assert.h> -#include <string.h> -#include <strings.h> - -/* A wrap for SET command with ACL check on the key. */ -int set_aclcheck_key(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc < 4) { - return RedisModule_WrongArity(ctx); - } - - int permissions; - const char *flags = RedisModule_StringPtrLen(argv[1], NULL); - - if (!strcasecmp(flags, "W")) { - permissions = REDISMODULE_CMD_KEY_UPDATE; - } else if (!strcasecmp(flags, "R")) { - permissions = REDISMODULE_CMD_KEY_ACCESS; - } else if (!strcasecmp(flags, "*")) { - permissions = REDISMODULE_CMD_KEY_UPDATE | REDISMODULE_CMD_KEY_ACCESS; - } else if (!strcasecmp(flags, "~")) { - permissions = 0; /* Requires either read or write */ - } else { - RedisModule_ReplyWithError(ctx, "INVALID FLAGS"); - return REDISMODULE_OK; - } - - /* Check that the key can be accessed */ - RedisModuleString *user_name = RedisModule_GetCurrentUserName(ctx); - RedisModuleUser *user = RedisModule_GetModuleUserFromUserName(user_name); - int ret = RedisModule_ACLCheckKeyPermissions(user, argv[2], permissions); - if (ret != 0) { - RedisModule_ReplyWithError(ctx, "DENIED KEY"); - RedisModule_FreeModuleUser(user); - RedisModule_FreeString(ctx, user_name); - return REDISMODULE_OK; - } - - RedisModuleCallReply *rep = RedisModule_Call(ctx, "SET", "v", argv + 2, (size_t)argc - 2); - if (!rep) { - RedisModule_ReplyWithError(ctx, "NULL reply returned"); - } else { - RedisModule_ReplyWithCallReply(ctx, rep); - RedisModule_FreeCallReply(rep); - } - - RedisModule_FreeModuleUser(user); - RedisModule_FreeString(ctx, user_name); - return REDISMODULE_OK; -} - -/* A wrap for SET command with ACL check on the key. */ -int set_aclcheck_prefixkey(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc < 4) { - return RedisModule_WrongArity(ctx); - } - - int permissions; - const char *flags = RedisModule_StringPtrLen(argv[1], NULL); - - if (!strcasecmp(flags, "W")) { - permissions = REDISMODULE_CMD_KEY_UPDATE; - } else if (!strcasecmp(flags, "R")) { - permissions = REDISMODULE_CMD_KEY_ACCESS; - } else if (!strcasecmp(flags, "*")) { - permissions = REDISMODULE_CMD_KEY_UPDATE | REDISMODULE_CMD_KEY_ACCESS; - } else if (!strcasecmp(flags, "~")) { - permissions = 0; /* Requires either read or write */ - } else { - RedisModule_ReplyWithError(ctx, "INVALID FLAGS"); - return REDISMODULE_OK; - } - - /* Check that the key can be accessed */ - RedisModuleString *user_name = RedisModule_GetCurrentUserName(ctx); - RedisModuleUser *user = RedisModule_GetModuleUserFromUserName(user_name); - int ret = RedisModule_ACLCheckKeyPrefixPermissions(user, argv[2], permissions); - if (ret != 0) { - RedisModule_ReplyWithError(ctx, "DENIED KEY"); - RedisModule_FreeModuleUser(user); - RedisModule_FreeString(ctx, user_name); - return REDISMODULE_OK; - } - - RedisModuleCallReply *rep = RedisModule_Call(ctx, "SET", "v", argv + 3, (size_t)argc - 3); - if (!rep) { - RedisModule_ReplyWithError(ctx, "NULL reply returned"); - } else { - RedisModule_ReplyWithCallReply(ctx, rep); - RedisModule_FreeCallReply(rep); - } - - RedisModule_FreeModuleUser(user); - RedisModule_FreeString(ctx, user_name); - return REDISMODULE_OK; -} - -/* A wrap for PUBLISH command with ACL check on the channel. */ -int publish_aclcheck_channel(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 3) { - return RedisModule_WrongArity(ctx); - } - - /* Check that the pubsub channel can be accessed */ - RedisModuleString *user_name = RedisModule_GetCurrentUserName(ctx); - RedisModuleUser *user = RedisModule_GetModuleUserFromUserName(user_name); - int ret = RedisModule_ACLCheckChannelPermissions(user, argv[1], REDISMODULE_CMD_CHANNEL_SUBSCRIBE); - if (ret != 0) { - RedisModule_ReplyWithError(ctx, "DENIED CHANNEL"); - RedisModule_FreeModuleUser(user); - RedisModule_FreeString(ctx, user_name); - return REDISMODULE_OK; - } - - RedisModuleCallReply *rep = RedisModule_Call(ctx, "PUBLISH", "v", argv + 1, (size_t)argc - 1); - if (!rep) { - RedisModule_ReplyWithError(ctx, "NULL reply returned"); - } else { - RedisModule_ReplyWithCallReply(ctx, rep); - RedisModule_FreeCallReply(rep); - } - - RedisModule_FreeModuleUser(user); - RedisModule_FreeString(ctx, user_name); - return REDISMODULE_OK; -} - -/* A wrap for RM_Call that check first that the command can be executed */ -int rm_call_aclcheck_cmd(RedisModuleCtx *ctx, RedisModuleUser *user, RedisModuleString **argv, int argc) { - if (argc < 2) { - return RedisModule_WrongArity(ctx); - } - - /* Check that the command can be executed */ - int ret = RedisModule_ACLCheckCommandPermissions(user, argv + 1, argc - 1); - if (ret != 0) { - RedisModule_ReplyWithError(ctx, "DENIED CMD"); - /* Add entry to ACL log */ - RedisModule_ACLAddLogEntry(ctx, user, argv[1], REDISMODULE_ACL_LOG_CMD); - return REDISMODULE_OK; - } - - const char* cmd = RedisModule_StringPtrLen(argv[1], NULL); - - RedisModuleCallReply* rep = RedisModule_Call(ctx, cmd, "v", argv + 2, (size_t)argc - 2); - if(!rep){ - RedisModule_ReplyWithError(ctx, "NULL reply returned"); - }else{ - RedisModule_ReplyWithCallReply(ctx, rep); - RedisModule_FreeCallReply(rep); - } - - return REDISMODULE_OK; -} - -int rm_call_aclcheck_cmd_default_user(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - RedisModuleString *user_name = RedisModule_GetCurrentUserName(ctx); - RedisModuleUser *user = RedisModule_GetModuleUserFromUserName(user_name); - - int res = rm_call_aclcheck_cmd(ctx, user, argv, argc); - - RedisModule_FreeModuleUser(user); - RedisModule_FreeString(ctx, user_name); - return res; -} - -int rm_call_aclcheck_cmd_module_user(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - /* Create a user and authenticate */ - RedisModuleUser *user = RedisModule_CreateModuleUser("testuser1"); - RedisModule_SetModuleUserACL(user, "allcommands"); - RedisModule_SetModuleUserACL(user, "allkeys"); - RedisModule_SetModuleUserACL(user, "on"); - RedisModule_AuthenticateClientWithUser(ctx, user, NULL, NULL, NULL); - - int res = rm_call_aclcheck_cmd(ctx, user, argv, argc); - - /* authenticated back to "default" user (so once we free testuser1 we will not disconnected */ - RedisModule_AuthenticateClientWithACLUser(ctx, "default", 7, NULL, NULL, NULL); - RedisModule_FreeModuleUser(user); - return res; -} - -int rm_call_aclcheck_with_errors(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){ - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - if(argc < 2){ - return RedisModule_WrongArity(ctx); - } - - const char* cmd = RedisModule_StringPtrLen(argv[1], NULL); - - RedisModuleCallReply* rep = RedisModule_Call(ctx, cmd, "vEC", argv + 2, (size_t)argc - 2); - RedisModule_ReplyWithCallReply(ctx, rep); - RedisModule_FreeCallReply(rep); - return REDISMODULE_OK; -} - -/* A wrap for RM_Call that pass the 'C' flag to do ACL check on the command. */ -int rm_call_aclcheck(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){ - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - if(argc < 2){ - return RedisModule_WrongArity(ctx); - } - - const char* cmd = RedisModule_StringPtrLen(argv[1], NULL); - - RedisModuleCallReply* rep = RedisModule_Call(ctx, cmd, "vC", argv + 2, (size_t)argc - 2); - if(!rep) { - char err[100]; - switch (errno) { - case EACCES: - RedisModule_ReplyWithError(ctx, "ERR NOPERM"); - break; - default: - snprintf(err, sizeof(err) - 1, "ERR errno=%d", errno); - RedisModule_ReplyWithError(ctx, err); - break; - } - } else { - RedisModule_ReplyWithCallReply(ctx, rep); - RedisModule_FreeCallReply(rep); - } - - return REDISMODULE_OK; -} - -int module_test_acl_category(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - RedisModule_ReplyWithSimpleString(ctx, "OK"); - return REDISMODULE_OK; -} - -int commandBlockCheck(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - int response_ok = 0; - int result = RedisModule_CreateCommand(ctx,"command.that.should.fail", module_test_acl_category, "", 0, 0, 0); - response_ok |= (result == REDISMODULE_OK); - - result = RedisModule_AddACLCategory(ctx,"blockedcategory"); - response_ok |= (result == REDISMODULE_OK); - - RedisModuleCommand *parent = RedisModule_GetCommand(ctx,"block.commands.outside.onload"); - result = RedisModule_SetCommandACLCategories(parent, "write"); - response_ok |= (result == REDISMODULE_OK); - - result = RedisModule_CreateSubcommand(parent,"subcommand.that.should.fail",module_test_acl_category,"",0,0,0); - response_ok |= (result == REDISMODULE_OK); - - /* This validates that it's not possible to create commands or add - * a new ACL Category outside OnLoad function. - * thus returns an error if they succeed. */ - if (response_ok) { - RedisModule_ReplyWithError(ctx, "UNEXPECTEDOK"); - } else { - RedisModule_ReplyWithSimpleString(ctx, "OK"); - } - return REDISMODULE_OK; -} - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - - if (RedisModule_Init(ctx,"aclcheck",1,REDISMODULE_APIVER_1)== REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (argc > 1) return RedisModule_WrongArity(ctx); - - /* When that flag is passed, we try to create too many categories, - * and the test expects this to fail. In this case redis returns REDISMODULE_ERR - * and set errno to ENOMEM*/ - if (argc == 1) { - long long fail_flag = 0; - RedisModule_StringToLongLong(argv[0], &fail_flag); - if (fail_flag) { - for (size_t j = 0; j < 45; j++) { - RedisModuleString* name = RedisModule_CreateStringPrintf(ctx, "customcategory%zu", j); - if (RedisModule_AddACLCategory(ctx, RedisModule_StringPtrLen(name, NULL)) == REDISMODULE_ERR) { - RedisModule_Assert(errno == ENOMEM); - RedisModule_FreeString(ctx, name); - return REDISMODULE_ERR; - } - RedisModule_FreeString(ctx, name); - } - } - } - - if (RedisModule_CreateCommand(ctx,"aclcheck.set.check.key", set_aclcheck_key,"write",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"aclcheck.set.check.prefixkey", set_aclcheck_prefixkey,"write",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"block.commands.outside.onload", commandBlockCheck,"write",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"aclcheck.module.command.aclcategories.write", module_test_acl_category,"write",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - RedisModuleCommand *aclcategories_write = RedisModule_GetCommand(ctx,"aclcheck.module.command.aclcategories.write"); - - if (RedisModule_SetCommandACLCategories(aclcategories_write, "write") == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"aclcheck.module.command.aclcategories.write.function.read.category", module_test_acl_category,"write",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - RedisModuleCommand *read_category = RedisModule_GetCommand(ctx,"aclcheck.module.command.aclcategories.write.function.read.category"); - - if (RedisModule_SetCommandACLCategories(read_category, "read") == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"aclcheck.module.command.aclcategories.read.only.category", module_test_acl_category,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - RedisModuleCommand *read_only_category = RedisModule_GetCommand(ctx,"aclcheck.module.command.aclcategories.read.only.category"); - - if (RedisModule_SetCommandACLCategories(read_only_category, "read") == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"aclcheck.publish.check.channel", publish_aclcheck_channel,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"aclcheck.rm_call.check.cmd", rm_call_aclcheck_cmd_default_user,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"aclcheck.rm_call.check.cmd.module.user", rm_call_aclcheck_cmd_module_user,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"aclcheck.rm_call", rm_call_aclcheck, - "write",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"aclcheck.rm_call_with_errors", rm_call_aclcheck_with_errors, - "write",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - /* This validates that, when module tries to add a category with invalid characters, - * redis returns REDISMODULE_ERR and set errno to `EINVAL` */ - if (RedisModule_AddACLCategory(ctx,"!nval!dch@r@cter$") == REDISMODULE_ERR) - RedisModule_Assert(errno == EINVAL); - else - return REDISMODULE_ERR; - - /* This validates that, when module tries to add a category that already exists, - * redis returns REDISMODULE_ERR and set errno to `EBUSY` */ - if (RedisModule_AddACLCategory(ctx,"write") == REDISMODULE_ERR) - RedisModule_Assert(errno == EBUSY); - else - return REDISMODULE_ERR; - - if (RedisModule_AddACLCategory(ctx,"foocategory") == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"aclcheck.module.command.test.add.new.aclcategories", module_test_acl_category,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - RedisModuleCommand *test_add_new_aclcategories = RedisModule_GetCommand(ctx,"aclcheck.module.command.test.add.new.aclcategories"); - - if (RedisModule_SetCommandACLCategories(test_add_new_aclcategories, "foocategory") == REDISMODULE_ERR) - return REDISMODULE_ERR; - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/atomicslotmigration.c b/examples/redis-unstable/tests/modules/atomicslotmigration.c deleted file mode 100644 index 83393cd..0000000 --- a/examples/redis-unstable/tests/modules/atomicslotmigration.c +++ /dev/null @@ -1,594 +0,0 @@ -#include "redismodule.h" - -#include <stdlib.h> -#include <memory.h> -#include <errno.h> - -#define MAX_EVENTS 1024 - -/* Log of cluster events. */ -const char *clusterEventLog[MAX_EVENTS]; -int numClusterEvents = 0; - -/* Log of cluster trim events. */ -const char *clusterTrimEventLog[MAX_EVENTS]; -int numClusterTrimEvents = 0; - -/* Log of last deleted key event. */ -const char *lastDeletedKeyLog = NULL; - -/* Flag to disable trim. */ -int disableTrimFlag = 0; - -int replicateModuleCommand = 0; /* Enable or disable module command replication. */ -RedisModuleString *moduleCommandKeyName = NULL; /* Key name to replicate. */ -RedisModuleString *moduleCommandKeyVal = NULL; /* Key value to replicate. */ - -/* Enable or disable module command replication. */ -int replicate_module_command(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 4) { - RedisModule_ReplyWithError(ctx, "ERR wrong number of arguments"); - return REDISMODULE_OK; - } - - long long enable = 0; - if (RedisModule_StringToLongLong(argv[1], &enable) != REDISMODULE_OK) { - RedisModule_ReplyWithError(ctx, "ERR enable value"); - return REDISMODULE_OK; - } - replicateModuleCommand = (enable != 0); - - /* Set the key name and value to replicate. */ - if (moduleCommandKeyName) RedisModule_FreeString(ctx, moduleCommandKeyName); - if (moduleCommandKeyVal) RedisModule_FreeString(ctx, moduleCommandKeyVal); - moduleCommandKeyName = RedisModule_CreateStringFromString(ctx, argv[2]); - moduleCommandKeyVal = RedisModule_CreateStringFromString(ctx, argv[3]); - - RedisModule_ReplyWithSimpleString(ctx, "OK"); - return REDISMODULE_OK; -} - -int lpush_and_replicate_crossslot_command(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 3) return RedisModule_WrongArity(ctx); - - /* LPUSH */ - RedisModuleCallReply *rep = RedisModule_Call(ctx, "LPUSH", "!ss", argv[1], argv[2]); - RedisModule_Assert(RedisModule_CallReplyType(rep) != REDISMODULE_REPLY_ERROR); - RedisModule_FreeCallReply(rep); - - /* Replicate cross slot command */ - int ret = RedisModule_Replicate(ctx, "MSET", "cccccc", "key1", "val1", "key2", "val2", "key3", "val3"); - RedisModule_Assert(ret == REDISMODULE_OK); - - RedisModule_ReplyWithSimpleString(ctx, "OK"); - return REDISMODULE_OK; -} - -int testClusterGetLocalSlotRanges(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - static int use_auto_memory = 0; - use_auto_memory = !use_auto_memory; - - RedisModuleSlotRangeArray *slots; - if (use_auto_memory) { - RedisModule_AutoMemory(ctx); - slots = RedisModule_ClusterGetLocalSlotRanges(ctx); - } else { - slots = RedisModule_ClusterGetLocalSlotRanges(NULL); - } - - RedisModule_ReplyWithArray(ctx, slots->num_ranges); - for (int i = 0; i < slots->num_ranges; i++) { - RedisModule_ReplyWithArray(ctx, 2); - RedisModule_ReplyWithLongLong(ctx, slots->ranges[i].start); - RedisModule_ReplyWithLongLong(ctx, slots->ranges[i].end); - } - if (!use_auto_memory) - RedisModule_ClusterFreeSlotRanges(NULL, slots); - return REDISMODULE_OK; -} - -/* Helper function to check if a slot range array contains a given slot. */ -int slotRangeArrayContains(RedisModuleSlotRangeArray *sra, unsigned int slot) { - for (int i = 0; i < sra->num_ranges; i++) - if (sra->ranges[i].start <= slot && sra->ranges[i].end >= slot) - return 1; - return 0; -} - -/* Sanity check. */ -int sanity(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModule_Assert(RedisModule_ClusterCanAccessKeysInSlot(-1) == 0); - RedisModule_Assert(RedisModule_ClusterCanAccessKeysInSlot(16384) == 0); - RedisModule_Assert(RedisModule_ClusterCanAccessKeysInSlot(100000) == 0); - - /* Call with invalid args. */ - errno = 0; - RedisModule_Assert(RedisModule_ClusterPropagateForSlotMigration(NULL, NULL, NULL) == REDISMODULE_ERR); - RedisModule_Assert(errno == EINVAL); - - /* Call with invalid args. */ - errno = 0; - RedisModule_Assert(RedisModule_ClusterPropagateForSlotMigration(ctx, NULL, NULL) == REDISMODULE_ERR); - RedisModule_Assert(errno == EINVAL); - - /* Call with invalid args. */ - errno = 0; - RedisModule_Assert(RedisModule_ClusterPropagateForSlotMigration(NULL, "asm.keyless_cmd", "") == REDISMODULE_ERR); - RedisModule_Assert(errno == EINVAL); - - /* Call outside of slot migration. */ - errno = 0; - RedisModule_Assert(RedisModule_ClusterPropagateForSlotMigration(ctx, "asm.keyless_cmd", "") == REDISMODULE_ERR); - RedisModule_Assert(errno == EBADF); - - RedisModule_ReplyWithSimpleString(ctx, "OK"); - return REDISMODULE_OK; -} - -/* Command to test RM_ClusterCanAccessKeysInSlot(). */ -int testClusterCanAccessKeysInSlot(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argc); - long long slot = 0; - - if (RedisModule_StringToLongLong(argv[1],&slot) != REDISMODULE_OK) { - return RedisModule_ReplyWithError(ctx,"ERR invalid slot"); - } - RedisModule_ReplyWithLongLong(ctx, RedisModule_ClusterCanAccessKeysInSlot(slot)); - return REDISMODULE_OK; -} - -/* Generate a string representation of the info struct and subevent. - e.g. 'sub: cluster-slot-migration-import-started, task_id: aeBd..., slots: 0-100,200-300' */ -const char *clusterAsmInfoToString(RedisModuleClusterSlotMigrationInfo *info, uint64_t sub) { - char buf[1024] = {0}; - - if (sub == REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_IMPORT_STARTED) - snprintf(buf, sizeof(buf), "sub: cluster-slot-migration-import-started, "); - else if (sub == REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_IMPORT_FAILED) - snprintf(buf, sizeof(buf), "sub: cluster-slot-migration-import-failed, "); - else if (sub == REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_IMPORT_COMPLETED) - snprintf(buf, sizeof(buf), "sub: cluster-slot-migration-import-completed, "); - else if (sub == REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_MIGRATE_STARTED) - snprintf(buf, sizeof(buf), "sub: cluster-slot-migration-migrate-started, "); - else if (sub == REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_MIGRATE_FAILED) - snprintf(buf, sizeof(buf), "sub: cluster-slot-migration-migrate-failed, "); - else if (sub == REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_MIGRATE_COMPLETED) - snprintf(buf, sizeof(buf), "sub: cluster-slot-migration-migrate-completed, "); - else { - RedisModule_Assert(0); - } - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "source_node_id:%.40s, destination_node_id:%.40s, ", - info->source_node_id, info->destination_node_id); - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "task_id:%s, slots:", info->task_id); - for (int i = 0; i < info->slots->num_ranges; i++) { - RedisModuleSlotRange *sr = &info->slots->ranges[i]; - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%d-%d", sr->start, sr->end); - if (i != info->slots->num_ranges - 1) - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ","); - } - return RedisModule_Strdup(buf); -} - -/* Generate a string representation of the info struct and subevent. - e.g. 'sub: cluster-slot-migration-trim-started, task_id: aeBd..., slots:0-100,200-300' */ -const char *clusterTrimInfoToString(RedisModuleClusterSlotMigrationTrimInfo *info, uint64_t sub) { - RedisModule_Assert(info); - char buf[1024] = {0}; - - if (sub == REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_TRIM_BACKGROUND) - snprintf(buf, sizeof(buf), "sub: cluster-slot-migration-trim-background, "); - else if (sub == REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_TRIM_STARTED) - snprintf(buf, sizeof(buf), "sub: cluster-slot-migration-trim-started, "); - else if (sub == REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_TRIM_COMPLETED) - snprintf(buf, sizeof(buf), "sub: cluster-slot-migration-trim-completed, "); - else { - RedisModule_Assert(0); - } - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "slots:"); - for (int i = 0; i < info->slots->num_ranges; i++) { - RedisModuleSlotRange *sr = &info->slots->ranges[i]; - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%d-%d", sr->start, sr->end); - if (i != info->slots->num_ranges - 1) - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ","); - } - return RedisModule_Strdup(buf); -} - -static void testReplicatingOutsideSlotRange(RedisModuleCtx *ctx, RedisModuleClusterSlotMigrationInfo *info) { - int slot = 0; - while (slot >= 0 && slot <= 16383) { - if (!slotRangeArrayContains(info->slots, slot)) { - break; - } - slot++; - } - char buf[128] = {0}; - const char *prefix = RedisModule_ClusterCanonicalKeyNameInSlot(slot); - snprintf(buf, sizeof(buf), "{%s}%s", prefix, "modulekey"); - errno = 0; - int ret = RedisModule_ClusterPropagateForSlotMigration(ctx, "SET", "cc", buf, "value"); - RedisModule_Assert(ret == REDISMODULE_ERR); - RedisModule_Assert(errno == ERANGE); -} - -static void testReplicatingCrossslotCommand(RedisModuleCtx *ctx) { - errno = 0; - int ret = RedisModule_ClusterPropagateForSlotMigration(ctx, "MSET", "cccccc", "key1", "val1", "key2", "val2", "key3", "val3"); - RedisModule_Assert(ret == REDISMODULE_ERR); - RedisModule_Assert(errno == ENOTSUP); -} - -static void testReplicatingUnknownCommand(RedisModuleCtx *ctx) { - errno = 0; - int ret = RedisModule_ClusterPropagateForSlotMigration(ctx, "unknowncommand", ""); - RedisModule_Assert(ret == REDISMODULE_ERR); - RedisModule_Assert(errno == ENOENT); -} - -static void testNonFatalScenarios(RedisModuleCtx *ctx, RedisModuleClusterSlotMigrationInfo *info) { - testReplicatingOutsideSlotRange(ctx, info); - testReplicatingCrossslotCommand(ctx); - testReplicatingUnknownCommand(ctx); -} - -int disableTrimCmd(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - disableTrimFlag = 1; - /* Only disable when MIGRATE_COMPLETED for simulating recommended usage. */ - // RedisModule_ClusterDisableTrim(ctx) - RedisModule_ReplyWithSimpleString(ctx, "OK"); - return REDISMODULE_OK; -} - -int enableTrimCmd(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - disableTrimFlag = 0; - RedisModule_Assert(RedisModule_ClusterEnableTrim(ctx) == REDISMODULE_OK); - RedisModule_ReplyWithSimpleString(ctx, "OK"); - return REDISMODULE_OK; -} - -int trimInProgressCmd(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - uint64_t flags = RedisModule_GetContextFlags(ctx); - RedisModule_ReplyWithLongLong(ctx, !!(flags & REDISMODULE_CTX_FLAGS_TRIM_IN_PROGRESS)); - return REDISMODULE_OK; -} - -void clusterEventCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data) { - REDISMODULE_NOT_USED(ctx); - int ret; - - RedisModule_Assert(RedisModule_IsSubEventSupported(e, sub)); - - if (e.id == REDISMODULE_EVENT_CLUSTER_SLOT_MIGRATION) { - RedisModuleClusterSlotMigrationInfo *info = data; - - if (sub == REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_MIGRATE_MODULE_PROPAGATE) { - /* Test some non-fatal scenarios. */ - testNonFatalScenarios(ctx, info); - - if (replicateModuleCommand == 0) return; - - /* Replicate a keyless command. */ - ret = RedisModule_ClusterPropagateForSlotMigration(ctx, "asm.keyless_cmd", ""); - RedisModule_Assert(ret == REDISMODULE_OK); - - /* Propagate configured key and value. */ - ret = RedisModule_ClusterPropagateForSlotMigration(ctx, "SET", "ss", moduleCommandKeyName, moduleCommandKeyVal); - RedisModule_Assert(ret == REDISMODULE_OK); - } else { - /* Log the event. */ - if (numClusterEvents >= MAX_EVENTS) return; - clusterEventLog[numClusterEvents++] = clusterAsmInfoToString(info, sub); - - if (sub == REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_MIGRATE_COMPLETED) { - /* If users ask to disable trim, we disable trim. */ - if (disableTrimFlag) { - RedisModule_Assert(RedisModule_ClusterDisableTrim(ctx) == REDISMODULE_OK); - } - } - } - } -} - -int getPendingTrimKeyCmd(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) { - RedisModule_ReplyWithError(ctx, "ERR wrong number of arguments"); - return REDISMODULE_ERR; - } - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], - REDISMODULE_READ | REDISMODULE_OPEN_KEY_ACCESS_TRIMMED); - if (!key) { - RedisModule_ReplyWithNull(ctx); - return REDISMODULE_OK; - } - if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_STRING) { - RedisModule_ReplyWithError(ctx, "key is not a string"); - return REDISMODULE_ERR; - } - size_t len; - const char *value = RedisModule_StringDMA(key, &len, 0); - RedisModule_ReplyWithStringBuffer(ctx, value, len); - RedisModule_CloseKey(key); - return REDISMODULE_OK; -} - -void clusterTrimEventCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data) { - REDISMODULE_NOT_USED(ctx); - - RedisModule_Assert(RedisModule_IsSubEventSupported(e, sub)); - - if (e.id == REDISMODULE_EVENT_CLUSTER_SLOT_MIGRATION_TRIM) { - /* Log the event. */ - if (numClusterTrimEvents >= MAX_EVENTS) return; - RedisModuleClusterSlotMigrationTrimInfo *info = data; - clusterTrimEventLog[numClusterTrimEvents++] = clusterTrimInfoToString(info, sub); - } -} - -static int keyspaceNotificationTrimmedCallback(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key) { - REDISMODULE_NOT_USED(ctx); - - RedisModule_Assert(type == REDISMODULE_NOTIFY_KEY_TRIMMED); - RedisModule_Assert(strcmp(event, "key_trimmed") == 0); - - if (numClusterTrimEvents >= MAX_EVENTS) return REDISMODULE_OK; - - /* Log the trimmed key event. */ - size_t len; - const char *key_str = RedisModule_StringPtrLen(key, &len); - - char buf[1024] = {0}; - snprintf(buf, sizeof(buf), "keyspace: key_trimmed, key: %s", key_str); - - clusterTrimEventLog[numClusterTrimEvents++] = RedisModule_Strdup(buf); - return REDISMODULE_OK; -} - -/* ASM.PARENT SET key value (just proxy to Redis SET) */ -static int asmParentSet(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 4) return RedisModule_WrongArity(ctx); - RedisModuleCallReply *reply = RedisModule_Call(ctx, "SET", "ss", argv[2], argv[3]); - if (!reply) return RedisModule_ReplyWithError(ctx, "ERR internal"); - RedisModule_ReplyWithCallReply(ctx, reply); - RedisModule_FreeCallReply(reply); - RedisModule_ReplicateVerbatim(ctx); - return REDISMODULE_OK; -} - -/* Clear both the cluster and trim event logs. */ -int clearEventLog(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - for (int i = 0; i < numClusterEvents; i++) - RedisModule_Free((void *)clusterEventLog[i]); - numClusterEvents = 0; - - for (int i = 0; i < numClusterTrimEvents; i++) - RedisModule_Free((void *)clusterTrimEventLog[i]); - numClusterTrimEvents = 0; - - RedisModule_ReplyWithSimpleString(ctx, "OK"); - return REDISMODULE_OK; -} - -/* Reply with the cluster event log. */ -int getClusterEventLog(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(ctx); - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModule_ReplyWithArray(ctx, numClusterEvents); - for (int i = 0; i < numClusterEvents; i++) - RedisModule_ReplyWithStringBuffer(ctx, clusterEventLog[i], strlen(clusterEventLog[i])); - return REDISMODULE_OK; -} - -/* Reply with the cluster trim event log. */ -int getClusterTrimEventLog(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(ctx); - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModule_ReplyWithArray(ctx, numClusterTrimEvents); - for (int i = 0; i < numClusterTrimEvents; i++) - RedisModule_ReplyWithStringBuffer(ctx, clusterTrimEventLog[i], strlen(clusterTrimEventLog[i])); - return REDISMODULE_OK; -} - -/* A keyless command to test module command replication. */ -int moduledata = 0; -int keylessCmd(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(ctx); - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - moduledata++; - RedisModule_ReplyWithLongLong(ctx, moduledata); - return REDISMODULE_OK; -} -int readkeylessCmdVal(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(ctx); - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - RedisModule_ReplyWithLongLong(ctx, moduledata); - return REDISMODULE_OK; -} - -int subscribeTrimmedEvent(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(ctx); - if (argc != 2) - return RedisModule_WrongArity(ctx); - - long long subscribe = 0; - if (RedisModule_StringToLongLong(argv[1], &subscribe) != REDISMODULE_OK) { - RedisModule_ReplyWithError(ctx, "ERR subscribe value"); - return REDISMODULE_OK; - } - - if (subscribe) { - /* Unsubscribe first to avoid duplicate subscription. */ - RedisModule_UnsubscribeFromKeyspaceEvents(ctx, REDISMODULE_NOTIFY_KEY_TRIMMED, keyspaceNotificationTrimmedCallback); - int ret = RedisModule_SubscribeToKeyspaceEvents(ctx, REDISMODULE_NOTIFY_KEY_TRIMMED, keyspaceNotificationTrimmedCallback); - RedisModule_Assert(ret == REDISMODULE_OK); - } else { - int ret = RedisModule_UnsubscribeFromKeyspaceEvents(ctx, REDISMODULE_NOTIFY_KEY_TRIMMED, keyspaceNotificationTrimmedCallback); - RedisModule_Assert(ret == REDISMODULE_OK); - } - RedisModule_ReplyWithSimpleString(ctx, "OK"); - return REDISMODULE_OK; -} - -void keyEventCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data) { - REDISMODULE_NOT_USED(ctx); - REDISMODULE_NOT_USED(e); - - if (sub == REDISMODULE_SUBEVENT_KEY_DELETED) { - RedisModuleKeyInfoV1 *ei = data; - RedisModuleKey *kp = ei->key; - RedisModuleString *key = (RedisModuleString *) RedisModule_GetKeyNameFromModuleKey(kp); - size_t keylen; - const char *keyname = RedisModule_StringPtrLen(key, &keylen); - - /* Verify value can be read. It will be used to verify key's value can - * be read in a trim callback. */ - size_t valuelen = 0; - const char *value = ""; - RedisModuleKey *mk = RedisModule_OpenKey(ctx, key, REDISMODULE_READ); - if (RedisModule_KeyType(mk) == REDISMODULE_KEYTYPE_STRING) { - value = RedisModule_StringDMA(mk, &valuelen, 0); - } - RedisModule_CloseKey(mk); - - char buf[1024] = {0}; - snprintf(buf, sizeof(buf), "keyevent: key: %.*s, value: %.*s", (int) keylen, keyname, (int)valuelen, value); - - if (lastDeletedKeyLog) RedisModule_Free((void *)lastDeletedKeyLog); - lastDeletedKeyLog = RedisModule_Strdup(buf); - } -} - -int getLastDeletedKey(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(ctx); - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - if (lastDeletedKeyLog) { - RedisModule_ReplyWithStringBuffer(ctx, lastDeletedKeyLog, strlen(lastDeletedKeyLog)); - } else { - RedisModule_ReplyWithNull(ctx); - } - return REDISMODULE_OK; -} - -int asmGetCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(ctx); - - if (argc != 2) return RedisModule_WrongArity(ctx); - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ); - if (key == NULL) { - RedisModule_ReplyWithNull(ctx); - return REDISMODULE_OK; - } - - RedisModule_Assert(RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_STRING); - size_t len; - const char *value = RedisModule_StringDMA(key, &len, 0); - RedisModule_ReplyWithStringBuffer(ctx, value, len); - RedisModule_CloseKey(key); - - return REDISMODULE_OK; -} - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - if (RedisModule_Init(ctx, "asm", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "asm.cluster_can_access_keys_in_slot", testClusterCanAccessKeysInSlot, "", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "asm.clear_event_log", clearEventLog, "", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "asm.get_cluster_event_log", getClusterEventLog, "", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "asm.get_cluster_trim_event_log", getClusterTrimEventLog, "", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "asm.keyless_cmd", keylessCmd, "write", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "asm.disable_trim", disableTrimCmd, "", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "asm.enable_trim", enableTrimCmd, "", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "asm.read_pending_trim_key", getPendingTrimKeyCmd, "readonly", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "asm.trim_in_progress", trimInProgressCmd, "", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "asm.read_keyless_cmd_val", readkeylessCmdVal, "", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "asm.sanity", sanity, "", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "asm.subscribe_trimmed_event", subscribeTrimmedEvent, "", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "asm.replicate_module_command", replicate_module_command, "", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "asm.lpush_replicate_crossslot_command", lpush_and_replicate_crossslot_command, "write", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "asm.cluster_get_local_slot_ranges", testClusterGetLocalSlotRanges, "", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "asm.get_last_deleted_key", getLastDeletedKey, "", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "asm.get", asmGetCommand, "", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "asm.parent", NULL, "", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - RedisModuleCommand *parent = RedisModule_GetCommand(ctx, "asm.parent"); - if (!parent) return REDISMODULE_ERR; - - /* Subcommand: ASM.PARENT SET (write) */ - if (RedisModule_CreateSubcommand(parent, "set", asmParentSet, "write fast", 2, 2, 1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_SubscribeToServerEvent(ctx, RedisModuleEvent_ClusterSlotMigration, clusterEventCallback) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_SubscribeToServerEvent(ctx, RedisModuleEvent_ClusterSlotMigrationTrim, clusterTrimEventCallback) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_SubscribeToKeyspaceEvents(ctx, REDISMODULE_NOTIFY_KEY_TRIMMED, keyspaceNotificationTrimmedCallback) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_SubscribeToServerEvent(ctx, RedisModuleEvent_Key, keyEventCallback) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/auth.c b/examples/redis-unstable/tests/modules/auth.c deleted file mode 100644 index cc2378e..0000000 --- a/examples/redis-unstable/tests/modules/auth.c +++ /dev/null @@ -1,286 +0,0 @@ -/* define macros for having usleep */ -#define _BSD_SOURCE -#define _DEFAULT_SOURCE - -#include "redismodule.h" - -#include <string.h> -#include <unistd.h> -#include <pthread.h> - -#define UNUSED(V) ((void) V) - -// A simple global user -static RedisModuleUser *global = NULL; -static long long client_change_delta = 0; -static pthread_t tid; - -void UserChangedCallback(uint64_t client_id, void *privdata) { - REDISMODULE_NOT_USED(privdata); - REDISMODULE_NOT_USED(client_id); - client_change_delta++; -} - -int Auth_CreateModuleUser(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - if (global) { - RedisModule_FreeModuleUser(global); - } - - global = RedisModule_CreateModuleUser("global"); - RedisModule_SetModuleUserACL(global, "allcommands"); - RedisModule_SetModuleUserACL(global, "allkeys"); - RedisModule_SetModuleUserACL(global, "on"); - - return RedisModule_ReplyWithSimpleString(ctx, "OK"); -} - -int Auth_AuthModuleUser(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - uint64_t client_id; - RedisModule_AuthenticateClientWithUser(ctx, global, UserChangedCallback, NULL, &client_id); - - return RedisModule_ReplyWithLongLong(ctx, (uint64_t) client_id); -} - -int Auth_AuthRealUser(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) return RedisModule_WrongArity(ctx); - - size_t length; - uint64_t client_id; - - RedisModuleString *user_string = argv[1]; - const char *name = RedisModule_StringPtrLen(user_string, &length); - - if (RedisModule_AuthenticateClientWithACLUser(ctx, name, length, - UserChangedCallback, NULL, &client_id) == REDISMODULE_ERR) { - return RedisModule_ReplyWithError(ctx, "Invalid user"); - } - - return RedisModule_ReplyWithLongLong(ctx, (uint64_t) client_id); -} - -/* This command redacts every other arguments and returns OK */ -int Auth_RedactedAPI(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - for(int i = argc - 1; i > 0; i -= 2) { - int result = RedisModule_RedactClientCommandArgument(ctx, i); - RedisModule_Assert(result == REDISMODULE_OK); - } - return RedisModule_ReplyWithSimpleString(ctx, "OK"); -} - -int Auth_ChangeCount(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - long long result = client_change_delta; - client_change_delta = 0; - return RedisModule_ReplyWithLongLong(ctx, result); -} - -/* The Module functionality below validates that module authentication callbacks can be registered - * to support both non-blocking and blocking module based authentication. */ - -/* Non Blocking Module Auth callback / implementation. */ -int auth_cb(RedisModuleCtx *ctx, RedisModuleString *username, RedisModuleString *password, RedisModuleString **err) { - const char *user = RedisModule_StringPtrLen(username, NULL); - const char *pwd = RedisModule_StringPtrLen(password, NULL); - if (!strcmp(user,"foo") && !strcmp(pwd,"allow")) { - RedisModule_AuthenticateClientWithACLUser(ctx, "foo", 3, NULL, NULL, NULL); - return REDISMODULE_AUTH_HANDLED; - } - else if (!strcmp(user,"foo") && !strcmp(pwd,"deny")) { - RedisModuleString *log = RedisModule_CreateString(ctx, "Module Auth", 11); - RedisModule_ACLAddLogEntryByUserName(ctx, username, log, REDISMODULE_ACL_LOG_AUTH); - RedisModule_FreeString(ctx, log); - const char *err_msg = "Auth denied by Misc Module."; - *err = RedisModule_CreateString(ctx, err_msg, strlen(err_msg)); - return REDISMODULE_AUTH_HANDLED; - } - return REDISMODULE_AUTH_NOT_HANDLED; -} - -int test_rm_register_auth_cb(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - RedisModule_RegisterAuthCallback(ctx, auth_cb); - RedisModule_ReplyWithSimpleString(ctx, "OK"); - return REDISMODULE_OK; -} - -/* - * The thread entry point that actually executes the blocking part of the AUTH command. - * This function sleeps for 0.5 seconds and then unblocks the client which will later call - * `AuthBlock_Reply`. - * `arg` is expected to contain the RedisModuleBlockedClient, username, and password. - */ -void *AuthBlock_ThreadMain(void *arg) { - usleep(500000); - void **targ = arg; - RedisModuleBlockedClient *bc = targ[0]; - int result = 2; - const char *user = RedisModule_StringPtrLen(targ[1], NULL); - const char *pwd = RedisModule_StringPtrLen(targ[2], NULL); - if (!strcmp(user,"foo") && !strcmp(pwd,"block_allow")) { - result = 1; - } - else if (!strcmp(user,"foo") && !strcmp(pwd,"block_deny")) { - result = 0; - } - else if (!strcmp(user,"foo") && !strcmp(pwd,"block_abort")) { - RedisModule_BlockedClientMeasureTimeEnd(bc); - RedisModule_AbortBlock(bc); - goto cleanup; - } - /* Provide the result to the blocking reply cb. */ - void **replyarg = RedisModule_Alloc(sizeof(void*)); - replyarg[0] = (void *) (uintptr_t) result; - RedisModule_BlockedClientMeasureTimeEnd(bc); - RedisModule_UnblockClient(bc, replyarg); -cleanup: - /* Free the username and password and thread / arg data. */ - RedisModule_FreeString(NULL, targ[1]); - RedisModule_FreeString(NULL, targ[2]); - RedisModule_Free(targ); - return NULL; -} - -/* - * Reply callback for a blocking AUTH command. This is called when the client is unblocked. - */ -int AuthBlock_Reply(RedisModuleCtx *ctx, RedisModuleString *username, RedisModuleString *password, RedisModuleString **err) { - REDISMODULE_NOT_USED(password); - void **targ = RedisModule_GetBlockedClientPrivateData(ctx); - int result = (uintptr_t) targ[0]; - size_t userlen = 0; - const char *user = RedisModule_StringPtrLen(username, &userlen); - /* Handle the success case by authenticating. */ - if (result == 1) { - RedisModule_AuthenticateClientWithACLUser(ctx, user, userlen, NULL, NULL, NULL); - return REDISMODULE_AUTH_HANDLED; - } - /* Handle the Error case by denying auth */ - else if (result == 0) { - RedisModuleString *log = RedisModule_CreateString(ctx, "Module Auth", 11); - RedisModule_ACLAddLogEntryByUserName(ctx, username, log, REDISMODULE_ACL_LOG_AUTH); - RedisModule_FreeString(ctx, log); - const char *err_msg = "Auth denied by Misc Module."; - *err = RedisModule_CreateString(ctx, err_msg, strlen(err_msg)); - return REDISMODULE_AUTH_HANDLED; - } - /* "Skip" Authentication */ - return REDISMODULE_AUTH_NOT_HANDLED; -} - -/* Private data freeing callback for Module Auth. */ -void AuthBlock_FreeData(RedisModuleCtx *ctx, void *privdata) { - REDISMODULE_NOT_USED(ctx); - RedisModule_Free(privdata); -} - -/* Callback triggered when the engine attempts module auth - * Return code here is one of the following: Auth succeeded, Auth denied, - * Auth not handled, Auth blocked. - * The Module can have auth succeed / denied here itself, but this is an example - * of blocking module auth. - */ -int blocking_auth_cb(RedisModuleCtx *ctx, RedisModuleString *username, RedisModuleString *password, RedisModuleString **err) { - REDISMODULE_NOT_USED(username); - REDISMODULE_NOT_USED(password); - REDISMODULE_NOT_USED(err); - /* Block the client from the Module. */ - RedisModuleBlockedClient *bc = RedisModule_BlockClientOnAuth(ctx, AuthBlock_Reply, AuthBlock_FreeData); - int ctx_flags = RedisModule_GetContextFlags(ctx); - if (ctx_flags & REDISMODULE_CTX_FLAGS_MULTI || ctx_flags & REDISMODULE_CTX_FLAGS_LUA) { - /* Clean up by using RedisModule_UnblockClient since we attempted blocking the client. */ - RedisModule_UnblockClient(bc, NULL); - return REDISMODULE_AUTH_HANDLED; - } - - /* Another blocking auth cb may have spawned a thread, we'll just wait for it - * to finish here */ - if (tid) pthread_join(tid, NULL); - - RedisModule_BlockedClientMeasureTimeStart(bc); - - /* Allocate memory for information needed. */ - void **targ = RedisModule_Alloc(sizeof(void*)*3); - targ[0] = bc; - targ[1] = RedisModule_CreateStringFromString(NULL, username); - targ[2] = RedisModule_CreateStringFromString(NULL, password); - - /* Create bg thread and pass the blockedclient, username and password to it. */ - if (pthread_create(&tid, NULL, AuthBlock_ThreadMain, targ) != 0) { - RedisModule_AbortBlock(bc); - - /* These are freed in AuthBlock_ThreadMain but since we failed to spawn - * the thread need to free them here. */ - RedisModule_FreeString(NULL, targ[1]); - RedisModule_FreeString(NULL, targ[2]); - RedisModule_Free(targ); - } - - return REDISMODULE_AUTH_HANDLED; -} - -int test_rm_register_blocking_auth_cb(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - RedisModule_RegisterAuthCallback(ctx, blocking_auth_cb); - RedisModule_ReplyWithSimpleString(ctx, "OK"); - return REDISMODULE_OK; -} - -/* This function must be present on each Redis module. It is used in order to - * register the commands into the Redis server. */ -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - if (RedisModule_Init(ctx,"testacl",1,REDISMODULE_APIVER_1) - == REDISMODULE_ERR) return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"auth.authrealuser", - Auth_AuthRealUser,"no-auth",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"auth.createmoduleuser", - Auth_CreateModuleUser,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"auth.authmoduleuser", - Auth_AuthModuleUser,"no-auth",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"auth.changecount", - Auth_ChangeCount,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"auth.redact", - Auth_RedactedAPI,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"testmoduleone.rm_register_auth_cb", - test_rm_register_auth_cb,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"testmoduleone.rm_register_blocking_auth_cb", - test_rm_register_blocking_auth_cb,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - return REDISMODULE_OK; -} - -int RedisModule_OnUnload(RedisModuleCtx *ctx) { - UNUSED(ctx); - - if (tid) pthread_join(tid, NULL); - - if (global) - RedisModule_FreeModuleUser(global); - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/basics.c b/examples/redis-unstable/tests/modules/basics.c deleted file mode 100644 index 85c6196..0000000 --- a/examples/redis-unstable/tests/modules/basics.c +++ /dev/null @@ -1,1051 +0,0 @@ -/* Module designed to test the Redis modules subsystem. - * - * ----------------------------------------------------------------------------- - * - * Copyright (c) 2016-Present, Redis Ltd. - * All rights reserved. - * - * Licensed under your choice of (a) the Redis Source Available License 2.0 - * (RSALv2); or (b) the Server Side Public License v1 (SSPLv1); or (c) the - * GNU Affero General Public License v3 (AGPLv3). - */ - -#include "redismodule.h" -#include <string.h> -#include <stdlib.h> - -/* --------------------------------- Helpers -------------------------------- */ - -/* Return true if the reply and the C null term string matches. */ -int TestMatchReply(RedisModuleCallReply *reply, char *str) { - RedisModuleString *mystr; - mystr = RedisModule_CreateStringFromCallReply(reply); - if (!mystr) return 0; - const char *ptr = RedisModule_StringPtrLen(mystr,NULL); - return strcmp(ptr,str) == 0; -} - -/* ------------------------------- Test units ------------------------------- */ - -/* TEST.CALL -- Test Call() API. */ -int TestCall(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModule_AutoMemory(ctx); - RedisModuleCallReply *reply; - - RedisModule_Call(ctx,"DEL","c","mylist"); - RedisModuleString *mystr = RedisModule_CreateString(ctx,"foo",3); - RedisModule_Call(ctx,"RPUSH","csl","mylist",mystr,(long long)1234); - reply = RedisModule_Call(ctx,"LRANGE","ccc","mylist","0","-1"); - long long items = RedisModule_CallReplyLength(reply); - if (items != 2) goto fail; - - RedisModuleCallReply *item0, *item1; - - item0 = RedisModule_CallReplyArrayElement(reply,0); - item1 = RedisModule_CallReplyArrayElement(reply,1); - if (!TestMatchReply(item0,"foo")) goto fail; - if (!TestMatchReply(item1,"1234")) goto fail; - - RedisModule_ReplyWithSimpleString(ctx,"OK"); - return REDISMODULE_OK; - -fail: - RedisModule_ReplyWithSimpleString(ctx,"ERR"); - return REDISMODULE_OK; -} - -int TestCallResp3Attribute(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModule_AutoMemory(ctx); - RedisModuleCallReply *reply; - - reply = RedisModule_Call(ctx,"DEBUG","3cc" ,"PROTOCOL", "attrib"); /* 3 stands for resp 3 reply */ - if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_STRING) goto fail; - - /* make sure we can not reply to resp2 client with resp3 (it might be a string but it contains attribute) */ - if (RedisModule_ReplyWithCallReply(ctx, reply) != REDISMODULE_ERR) goto fail; - - if (!TestMatchReply(reply,"Some real reply following the attribute")) goto fail; - - reply = RedisModule_CallReplyAttribute(reply); - if (!reply || RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_ATTRIBUTE) goto fail; - /* make sure we can not reply to resp2 client with resp3 attribute */ - if (RedisModule_ReplyWithCallReply(ctx, reply) != REDISMODULE_ERR) goto fail; - if (RedisModule_CallReplyLength(reply) != 1) goto fail; - - RedisModuleCallReply *key, *val; - if (RedisModule_CallReplyAttributeElement(reply,0,&key,&val) != REDISMODULE_OK) goto fail; - if (!TestMatchReply(key,"key-popularity")) goto fail; - if (RedisModule_CallReplyType(val) != REDISMODULE_REPLY_ARRAY) goto fail; - if (RedisModule_CallReplyLength(val) != 2) goto fail; - if (!TestMatchReply(RedisModule_CallReplyArrayElement(val, 0),"key:123")) goto fail; - if (!TestMatchReply(RedisModule_CallReplyArrayElement(val, 1),"90")) goto fail; - - RedisModule_ReplyWithSimpleString(ctx,"OK"); - return REDISMODULE_OK; - -fail: - RedisModule_ReplyWithSimpleString(ctx,"ERR"); - return REDISMODULE_OK; -} - -int TestGetResp(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - int flags = RedisModule_GetContextFlags(ctx); - - if (flags & REDISMODULE_CTX_FLAGS_RESP3) { - RedisModule_ReplyWithLongLong(ctx, 3); - } else { - RedisModule_ReplyWithLongLong(ctx, 2); - } - - return REDISMODULE_OK; -} - -int TestCallRespAutoMode(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModule_AutoMemory(ctx); - RedisModuleCallReply *reply; - - RedisModule_Call(ctx,"DEL","c","myhash"); - RedisModule_Call(ctx,"HSET","ccccc","myhash", "f1", "v1", "f2", "v2"); - /* 0 stands for auto mode, we will get the reply in the same format as the client */ - reply = RedisModule_Call(ctx,"HGETALL","0c" ,"myhash"); - RedisModule_ReplyWithCallReply(ctx, reply); - return REDISMODULE_OK; -} - -int TestCallResp3Map(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModule_AutoMemory(ctx); - RedisModuleCallReply *reply; - - RedisModule_Call(ctx,"DEL","c","myhash"); - RedisModule_Call(ctx,"HSET","ccccc","myhash", "f1", "v1", "f2", "v2"); - reply = RedisModule_Call(ctx,"HGETALL","3c" ,"myhash"); /* 3 stands for resp 3 reply */ - if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_MAP) goto fail; - - /* make sure we can not reply to resp2 client with resp3 map */ - if (RedisModule_ReplyWithCallReply(ctx, reply) != REDISMODULE_ERR) goto fail; - - long long items = RedisModule_CallReplyLength(reply); - if (items != 2) goto fail; - - RedisModuleCallReply *key0, *key1; - RedisModuleCallReply *val0, *val1; - if (RedisModule_CallReplyMapElement(reply,0,&key0,&val0) != REDISMODULE_OK) goto fail; - if (RedisModule_CallReplyMapElement(reply,1,&key1,&val1) != REDISMODULE_OK) goto fail; - if (!TestMatchReply(key0,"f1")) goto fail; - if (!TestMatchReply(key1,"f2")) goto fail; - if (!TestMatchReply(val0,"v1")) goto fail; - if (!TestMatchReply(val1,"v2")) goto fail; - - RedisModule_ReplyWithSimpleString(ctx,"OK"); - return REDISMODULE_OK; - -fail: - RedisModule_ReplyWithSimpleString(ctx,"ERR"); - return REDISMODULE_OK; -} - -int TestCallResp3Bool(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModule_AutoMemory(ctx); - RedisModuleCallReply *reply; - - reply = RedisModule_Call(ctx,"DEBUG","3cc" ,"PROTOCOL", "true"); /* 3 stands for resp 3 reply */ - if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_BOOL) goto fail; - /* make sure we can not reply to resp2 client with resp3 bool */ - if (RedisModule_ReplyWithCallReply(ctx, reply) != REDISMODULE_ERR) goto fail; - - if (!RedisModule_CallReplyBool(reply)) goto fail; - reply = RedisModule_Call(ctx,"DEBUG","3cc" ,"PROTOCOL", "false"); /* 3 stands for resp 3 reply */ - if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_BOOL) goto fail; - if (RedisModule_CallReplyBool(reply)) goto fail; - - RedisModule_ReplyWithSimpleString(ctx,"OK"); - return REDISMODULE_OK; - -fail: - RedisModule_ReplyWithSimpleString(ctx,"ERR"); - return REDISMODULE_OK; -} - -int TestCallResp3Null(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModule_AutoMemory(ctx); - RedisModuleCallReply *reply; - - reply = RedisModule_Call(ctx,"DEBUG","3cc" ,"PROTOCOL", "null"); /* 3 stands for resp 3 reply */ - if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_NULL) goto fail; - - /* make sure we can not reply to resp2 client with resp3 null */ - if (RedisModule_ReplyWithCallReply(ctx, reply) != REDISMODULE_ERR) goto fail; - - RedisModule_ReplyWithSimpleString(ctx,"OK"); - return REDISMODULE_OK; - -fail: - RedisModule_ReplyWithSimpleString(ctx,"ERR"); - return REDISMODULE_OK; -} - -int TestCallReplyWithNestedReply(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModule_AutoMemory(ctx); - RedisModuleCallReply *reply; - - RedisModule_Call(ctx,"DEL","c","mylist"); - RedisModule_Call(ctx,"RPUSH","ccl","mylist","test",(long long)1234); - reply = RedisModule_Call(ctx,"LRANGE","ccc","mylist","0","-1"); - if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_ARRAY) goto fail; - if (RedisModule_CallReplyLength(reply) < 1) goto fail; - RedisModuleCallReply *nestedReply = RedisModule_CallReplyArrayElement(reply, 0); - - RedisModule_ReplyWithCallReply(ctx,nestedReply); - return REDISMODULE_OK; - -fail: - RedisModule_ReplyWithSimpleString(ctx,"ERR"); - return REDISMODULE_OK; -} - -int TestCallReplyWithArrayReply(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModule_AutoMemory(ctx); - RedisModuleCallReply *reply; - - RedisModule_Call(ctx,"DEL","c","mylist"); - RedisModule_Call(ctx,"RPUSH","ccl","mylist","test",(long long)1234); - reply = RedisModule_Call(ctx,"LRANGE","ccc","mylist","0","-1"); - if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_ARRAY) goto fail; - - RedisModule_ReplyWithCallReply(ctx,reply); - return REDISMODULE_OK; - -fail: - RedisModule_ReplyWithSimpleString(ctx,"ERR"); - return REDISMODULE_OK; -} - -int TestCallResp3Double(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModule_AutoMemory(ctx); - RedisModuleCallReply *reply; - - reply = RedisModule_Call(ctx,"DEBUG","3cc" ,"PROTOCOL", "double"); /* 3 stands for resp 3 reply */ - if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_DOUBLE) goto fail; - - /* make sure we can not reply to resp2 client with resp3 double*/ - if (RedisModule_ReplyWithCallReply(ctx, reply) != REDISMODULE_ERR) goto fail; - - double d = RedisModule_CallReplyDouble(reply); - /* we compare strings, since comparing doubles directly can fail in various architectures, e.g. 32bit */ - char got[30], expected[30]; - snprintf(got, sizeof(got), "%.17g", d); - snprintf(expected, sizeof(expected), "%.17g", 3.141); - if (strcmp(got, expected) != 0) goto fail; - RedisModule_ReplyWithSimpleString(ctx,"OK"); - return REDISMODULE_OK; - -fail: - RedisModule_ReplyWithSimpleString(ctx,"ERR"); - return REDISMODULE_OK; -} - -int TestCallResp3BigNumber(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModule_AutoMemory(ctx); - RedisModuleCallReply *reply; - - reply = RedisModule_Call(ctx,"DEBUG","3cc" ,"PROTOCOL", "bignum"); /* 3 stands for resp 3 reply */ - if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_BIG_NUMBER) goto fail; - - /* make sure we can not reply to resp2 client with resp3 big number */ - if (RedisModule_ReplyWithCallReply(ctx, reply) != REDISMODULE_ERR) goto fail; - - size_t len; - const char* big_num = RedisModule_CallReplyBigNumber(reply, &len); - RedisModule_ReplyWithStringBuffer(ctx,big_num,len); - return REDISMODULE_OK; - -fail: - RedisModule_ReplyWithSimpleString(ctx,"ERR"); - return REDISMODULE_OK; -} - -int TestCallResp3Verbatim(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModule_AutoMemory(ctx); - RedisModuleCallReply *reply; - - reply = RedisModule_Call(ctx,"DEBUG","3cc" ,"PROTOCOL", "verbatim"); /* 3 stands for resp 3 reply */ - if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_VERBATIM_STRING) goto fail; - - /* make sure we can not reply to resp2 client with resp3 verbatim string */ - if (RedisModule_ReplyWithCallReply(ctx, reply) != REDISMODULE_ERR) goto fail; - - const char* format; - size_t len; - const char* str = RedisModule_CallReplyVerbatim(reply, &len, &format); - RedisModuleString *s = RedisModule_CreateStringPrintf(ctx, "%.*s:%.*s", 3, format, (int)len, str); - RedisModule_ReplyWithString(ctx,s); - return REDISMODULE_OK; - -fail: - RedisModule_ReplyWithSimpleString(ctx,"ERR"); - return REDISMODULE_OK; -} - -int TestCallResp3Set(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModule_AutoMemory(ctx); - RedisModuleCallReply *reply; - - RedisModule_Call(ctx,"DEL","c","myset"); - RedisModule_Call(ctx,"sadd","ccc","myset", "v1", "v2"); - reply = RedisModule_Call(ctx,"smembers","3c" ,"myset"); // N stands for resp 3 reply - if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_SET) goto fail; - - /* make sure we can not reply to resp2 client with resp3 set */ - if (RedisModule_ReplyWithCallReply(ctx, reply) != REDISMODULE_ERR) goto fail; - - long long items = RedisModule_CallReplyLength(reply); - if (items != 2) goto fail; - - RedisModuleCallReply *val0, *val1; - - val0 = RedisModule_CallReplySetElement(reply,0); - val1 = RedisModule_CallReplySetElement(reply,1); - - /* - * The order of elements on sets are not promised so we just - * veridy that the reply matches one of the elements. - */ - if (!TestMatchReply(val0,"v1") && !TestMatchReply(val0,"v2")) goto fail; - if (!TestMatchReply(val1,"v1") && !TestMatchReply(val1,"v2")) goto fail; - - RedisModule_ReplyWithSimpleString(ctx,"OK"); - return REDISMODULE_OK; - -fail: - RedisModule_ReplyWithSimpleString(ctx,"ERR"); - return REDISMODULE_OK; -} - -/* TEST.STRING.APPEND -- Test appending to an existing string object. */ -int TestStringAppend(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModuleString *s = RedisModule_CreateString(ctx,"foo",3); - RedisModule_StringAppendBuffer(ctx,s,"bar",3); - RedisModule_ReplyWithString(ctx,s); - RedisModule_FreeString(ctx,s); - return REDISMODULE_OK; -} - -/* TEST.STRING.APPEND.AM -- Test append with retain when auto memory is on. */ -int TestStringAppendAM(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModule_AutoMemory(ctx); - RedisModuleString *s = RedisModule_CreateString(ctx,"foo",3); - RedisModule_RetainString(ctx,s); - RedisModule_TrimStringAllocation(s); /* Mostly NOP, but exercises the API function */ - RedisModule_StringAppendBuffer(ctx,s,"bar",3); - RedisModule_ReplyWithString(ctx,s); - RedisModule_FreeString(ctx,s); - return REDISMODULE_OK; -} - -/* TEST.STRING.TRIM -- Test we trim a string with free space. */ -int TestTrimString(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - RedisModuleString *s = RedisModule_CreateString(ctx,"foo",3); - char *tmp = RedisModule_Alloc(1024); - RedisModule_StringAppendBuffer(ctx,s,tmp,1024); - size_t string_len = RedisModule_MallocSizeString(s); - RedisModule_TrimStringAllocation(s); - size_t len_after_trim = RedisModule_MallocSizeString(s); - - /* Determine if using jemalloc memory allocator. */ - RedisModuleServerInfoData *info = RedisModule_GetServerInfo(ctx, "memory"); - const char *field = RedisModule_ServerInfoGetFieldC(info, "mem_allocator"); - int use_jemalloc = !strncmp(field, "jemalloc", 8); - - /* Jemalloc will reallocate `s` from 2k to 1k after RedisModule_TrimStringAllocation(), - * but non-jemalloc memory allocators may keep the old size. */ - if ((use_jemalloc && len_after_trim < string_len) || - (!use_jemalloc && len_after_trim <= string_len)) - { - RedisModule_ReplyWithSimpleString(ctx, "OK"); - } else { - RedisModule_ReplyWithError(ctx, "String was not trimmed as expected."); - } - RedisModule_FreeServerInfo(ctx, info); - RedisModule_Free(tmp); - RedisModule_FreeString(ctx,s); - return REDISMODULE_OK; -} - -/* TEST.STRING.PRINTF -- Test string formatting. */ -int TestStringPrintf(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - RedisModule_AutoMemory(ctx); - if (argc < 3) { - return RedisModule_WrongArity(ctx); - } - RedisModuleString *s = RedisModule_CreateStringPrintf(ctx, - "Got %d args. argv[1]: %s, argv[2]: %s", - argc, - RedisModule_StringPtrLen(argv[1], NULL), - RedisModule_StringPtrLen(argv[2], NULL) - ); - - RedisModule_ReplyWithString(ctx,s); - - return REDISMODULE_OK; -} - -int failTest(RedisModuleCtx *ctx, const char *msg) { - RedisModule_ReplyWithError(ctx, msg); - return REDISMODULE_ERR; -} - -int TestUnlink(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - RedisModule_AutoMemory(ctx); - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModuleKey *k = RedisModule_OpenKey(ctx, RedisModule_CreateStringPrintf(ctx, "unlinked"), REDISMODULE_WRITE | REDISMODULE_READ); - if (!k) return failTest(ctx, "Could not create key"); - - if (REDISMODULE_ERR == RedisModule_StringSet(k, RedisModule_CreateStringPrintf(ctx, "Foobar"))) { - return failTest(ctx, "Could not set string value"); - } - - RedisModuleCallReply *rep = RedisModule_Call(ctx, "EXISTS", "c", "unlinked"); - if (!rep || RedisModule_CallReplyInteger(rep) != 1) { - return failTest(ctx, "Key does not exist before unlink"); - } - - if (REDISMODULE_ERR == RedisModule_UnlinkKey(k)) { - return failTest(ctx, "Could not unlink key"); - } - - rep = RedisModule_Call(ctx, "EXISTS", "c", "unlinked"); - if (!rep || RedisModule_CallReplyInteger(rep) != 0) { - return failTest(ctx, "Could not verify key to be unlinked"); - } - return RedisModule_ReplyWithSimpleString(ctx, "OK"); -} - -int TestNestedCallReplyArrayElement(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - RedisModule_AutoMemory(ctx); - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModuleString *expect_key = RedisModule_CreateString(ctx, "mykey", strlen("mykey")); - RedisModule_SelectDb(ctx, 1); - RedisModule_Call(ctx, "LPUSH", "sc", expect_key, "myvalue"); - - RedisModuleCallReply *scan_reply = RedisModule_Call(ctx, "SCAN", "l", (long long)0); - RedisModule_Assert(scan_reply != NULL && RedisModule_CallReplyType(scan_reply) == REDISMODULE_REPLY_ARRAY); - RedisModule_Assert(RedisModule_CallReplyLength(scan_reply) == 2); - - long long scan_cursor; - RedisModuleCallReply *cursor_reply = RedisModule_CallReplyArrayElement(scan_reply, 0); - RedisModule_Assert(RedisModule_CallReplyType(cursor_reply) == REDISMODULE_REPLY_STRING); - RedisModule_Assert(RedisModule_StringToLongLong(RedisModule_CreateStringFromCallReply(cursor_reply), &scan_cursor) == REDISMODULE_OK); - RedisModule_Assert(scan_cursor == 0); - - RedisModuleCallReply *keys_reply = RedisModule_CallReplyArrayElement(scan_reply, 1); - RedisModule_Assert(RedisModule_CallReplyType(keys_reply) == REDISMODULE_REPLY_ARRAY); - RedisModule_Assert( RedisModule_CallReplyLength(keys_reply) == 1); - - RedisModuleCallReply *key_reply = RedisModule_CallReplyArrayElement(keys_reply, 0); - RedisModule_Assert(RedisModule_CallReplyType(key_reply) == REDISMODULE_REPLY_STRING); - RedisModuleString *key = RedisModule_CreateStringFromCallReply(key_reply); - RedisModule_Assert(RedisModule_StringCompare(key, expect_key) == 0); - - RedisModule_ReplyWithSimpleString(ctx, "OK"); - return REDISMODULE_OK; -} - -/* TEST.STRING.TRUNCATE -- Test truncating an existing string object. */ -int TestStringTruncate(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - RedisModule_AutoMemory(ctx); - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModule_Call(ctx, "SET", "cc", "foo", "abcde"); - RedisModuleKey *k = RedisModule_OpenKey(ctx, RedisModule_CreateStringPrintf(ctx, "foo"), REDISMODULE_READ | REDISMODULE_WRITE); - if (!k) return failTest(ctx, "Could not create key"); - - size_t len = 0; - char* s; - - /* expand from 5 to 8 and check null pad */ - if (REDISMODULE_ERR == RedisModule_StringTruncate(k, 8)) { - return failTest(ctx, "Could not truncate string value (8)"); - } - s = RedisModule_StringDMA(k, &len, REDISMODULE_READ); - if (!s) { - return failTest(ctx, "Failed to read truncated string (8)"); - } else if (len != 8) { - return failTest(ctx, "Failed to expand string value (8)"); - } else if (0 != strncmp(s, "abcde\0\0\0", 8)) { - return failTest(ctx, "Failed to null pad string value (8)"); - } - - /* shrink from 8 to 4 */ - if (REDISMODULE_ERR == RedisModule_StringTruncate(k, 4)) { - return failTest(ctx, "Could not truncate string value (4)"); - } - s = RedisModule_StringDMA(k, &len, REDISMODULE_READ); - if (!s) { - return failTest(ctx, "Failed to read truncated string (4)"); - } else if (len != 4) { - return failTest(ctx, "Failed to shrink string value (4)"); - } else if (0 != strncmp(s, "abcd", 4)) { - return failTest(ctx, "Failed to truncate string value (4)"); - } - - /* shrink to 0 */ - if (REDISMODULE_ERR == RedisModule_StringTruncate(k, 0)) { - return failTest(ctx, "Could not truncate string value (0)"); - } - s = RedisModule_StringDMA(k, &len, REDISMODULE_READ); - if (!s) { - return failTest(ctx, "Failed to read truncated string (0)"); - } else if (len != 0) { - return failTest(ctx, "Failed to shrink string value to (0)"); - } - - return RedisModule_ReplyWithSimpleString(ctx, "OK"); -} - -int NotifyCallback(RedisModuleCtx *ctx, int type, const char *event, - RedisModuleString *key) { - RedisModule_AutoMemory(ctx); - /* Increment a counter on the notifications: for each key notified we - * increment a counter */ - RedisModule_Log(ctx, "notice", "Got event type %d, event %s, key %s", type, - event, RedisModule_StringPtrLen(key, NULL)); - - RedisModule_Call(ctx, "HINCRBY", "csc", "notifications", key, "1"); - return REDISMODULE_OK; -} - -/* TEST.NOTIFICATIONS -- Test Keyspace Notifications. */ -int TestNotifications(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - RedisModule_AutoMemory(ctx); - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - -#define FAIL(msg, ...) \ - { \ - RedisModule_Log(ctx, "warning", "Failed NOTIFY Test. Reason: " #msg, ##__VA_ARGS__); \ - goto err; \ - } - RedisModule_Call(ctx, "FLUSHDB", ""); - - RedisModule_Call(ctx, "SET", "cc", "foo", "bar"); - RedisModule_Call(ctx, "SET", "cc", "foo", "baz"); - RedisModule_Call(ctx, "SADD", "cc", "bar", "x"); - RedisModule_Call(ctx, "SADD", "cc", "bar", "y"); - - RedisModule_Call(ctx, "HSET", "ccc", "baz", "x", "y"); - /* LPUSH should be ignored and not increment any counters */ - RedisModule_Call(ctx, "LPUSH", "cc", "l", "y"); - RedisModule_Call(ctx, "LPUSH", "cc", "l", "y"); - - /* Miss some keys intentionally so we will get a "keymiss" notification. */ - RedisModule_Call(ctx, "GET", "c", "nosuchkey"); - RedisModule_Call(ctx, "SMEMBERS", "c", "nosuchkey"); - - size_t sz; - const char *rep; - RedisModuleCallReply *r = RedisModule_Call(ctx, "HGET", "cc", "notifications", "foo"); - if (r == NULL || RedisModule_CallReplyType(r) != REDISMODULE_REPLY_STRING) { - FAIL("Wrong or no reply for foo"); - } else { - rep = RedisModule_CallReplyStringPtr(r, &sz); - if (sz != 1 || *rep != '2') { - FAIL("Got reply '%s'. expected '2'", RedisModule_CallReplyStringPtr(r, NULL)); - } - } - - r = RedisModule_Call(ctx, "HGET", "cc", "notifications", "bar"); - if (r == NULL || RedisModule_CallReplyType(r) != REDISMODULE_REPLY_STRING) { - FAIL("Wrong or no reply for bar"); - } else { - rep = RedisModule_CallReplyStringPtr(r, &sz); - if (sz != 1 || *rep != '2') { - FAIL("Got reply '%s'. expected '2'", rep); - } - } - - r = RedisModule_Call(ctx, "HGET", "cc", "notifications", "baz"); - if (r == NULL || RedisModule_CallReplyType(r) != REDISMODULE_REPLY_STRING) { - FAIL("Wrong or no reply for baz"); - } else { - rep = RedisModule_CallReplyStringPtr(r, &sz); - if (sz != 1 || *rep != '1') { - FAIL("Got reply '%.*s'. expected '1'", (int)sz, rep); - } - } - /* For l we expect nothing since we didn't subscribe to list events */ - r = RedisModule_Call(ctx, "HGET", "cc", "notifications", "l"); - if (r == NULL || RedisModule_CallReplyType(r) != REDISMODULE_REPLY_NULL) { - FAIL("Wrong reply for l"); - } - - r = RedisModule_Call(ctx, "HGET", "cc", "notifications", "nosuchkey"); - if (r == NULL || RedisModule_CallReplyType(r) != REDISMODULE_REPLY_STRING) { - FAIL("Wrong or no reply for nosuchkey"); - } else { - rep = RedisModule_CallReplyStringPtr(r, &sz); - if (sz != 1 || *rep != '2') { - FAIL("Got reply '%.*s'. expected '2'", (int)sz, rep); - } - } - - RedisModule_Call(ctx, "FLUSHDB", ""); - - return RedisModule_ReplyWithSimpleString(ctx, "OK"); -err: - RedisModule_Call(ctx, "FLUSHDB", ""); - - return RedisModule_ReplyWithSimpleString(ctx, "ERR"); -} - -/* TEST.CTXFLAGS -- Test GetContextFlags. */ -int TestCtxFlags(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argc); - REDISMODULE_NOT_USED(argv); - - RedisModule_AutoMemory(ctx); - - int ok = 1; - const char *errString = NULL; -#undef FAIL -#define FAIL(msg) \ - { \ - ok = 0; \ - errString = msg; \ - goto end; \ - } - - int flags = RedisModule_GetContextFlags(ctx); - if (flags == 0) { - FAIL("Got no flags"); - } - - if (flags & REDISMODULE_CTX_FLAGS_LUA) FAIL("Lua flag was set"); - if (flags & REDISMODULE_CTX_FLAGS_MULTI) FAIL("Multi flag was set"); - - if (flags & REDISMODULE_CTX_FLAGS_AOF) FAIL("AOF Flag was set") - /* Enable AOF to test AOF flags */ - RedisModule_Call(ctx, "config", "ccc", "set", "appendonly", "yes"); - flags = RedisModule_GetContextFlags(ctx); - if (!(flags & REDISMODULE_CTX_FLAGS_AOF)) FAIL("AOF Flag not set after config set"); - - /* Disable RDB saving and test the flag. */ - RedisModule_Call(ctx, "config", "ccc", "set", "save", ""); - flags = RedisModule_GetContextFlags(ctx); - if (flags & REDISMODULE_CTX_FLAGS_RDB) FAIL("RDB Flag was set"); - /* Enable RDB to test RDB flags */ - RedisModule_Call(ctx, "config", "ccc", "set", "save", "900 1"); - flags = RedisModule_GetContextFlags(ctx); - if (!(flags & REDISMODULE_CTX_FLAGS_RDB)) FAIL("RDB Flag was not set after config set"); - - if (!(flags & REDISMODULE_CTX_FLAGS_MASTER)) FAIL("Master flag was not set"); - if (flags & REDISMODULE_CTX_FLAGS_SLAVE) FAIL("Slave flag was set"); - if (flags & REDISMODULE_CTX_FLAGS_READONLY) FAIL("Read-only flag was set"); - if (flags & REDISMODULE_CTX_FLAGS_CLUSTER) FAIL("Cluster flag was set"); - - /* Disable maxmemory and test the flag. (it is implicitly set in 32bit builds. */ - RedisModule_Call(ctx, "config", "ccc", "set", "maxmemory", "0"); - flags = RedisModule_GetContextFlags(ctx); - if (flags & REDISMODULE_CTX_FLAGS_MAXMEMORY) FAIL("Maxmemory flag was set"); - - /* Enable maxmemory and test the flag. */ - RedisModule_Call(ctx, "config", "ccc", "set", "maxmemory", "100000000"); - flags = RedisModule_GetContextFlags(ctx); - if (!(flags & REDISMODULE_CTX_FLAGS_MAXMEMORY)) - FAIL("Maxmemory flag was not set after config set"); - - if (flags & REDISMODULE_CTX_FLAGS_EVICT) FAIL("Eviction flag was set"); - RedisModule_Call(ctx, "config", "ccc", "set", "maxmemory-policy", "allkeys-lru"); - flags = RedisModule_GetContextFlags(ctx); - if (!(flags & REDISMODULE_CTX_FLAGS_EVICT)) FAIL("Eviction flag was not set after config set"); - -end: - /* Revert config changes */ - RedisModule_Call(ctx, "config", "ccc", "set", "appendonly", "no"); - RedisModule_Call(ctx, "config", "ccc", "set", "save", ""); - RedisModule_Call(ctx, "config", "ccc", "set", "maxmemory", "0"); - RedisModule_Call(ctx, "config", "ccc", "set", "maxmemory-policy", "noeviction"); - - if (!ok) { - RedisModule_Log(ctx, "warning", "Failed CTXFLAGS Test. Reason: %s", errString); - return RedisModule_ReplyWithSimpleString(ctx, "ERR"); - } - - return RedisModule_ReplyWithSimpleString(ctx, "OK"); -} - -/* ----------------------------- Test framework ----------------------------- */ - -/* Return 1 if the reply matches the specified string, otherwise log errors - * in the server log and return 0. */ -int TestAssertErrorReply(RedisModuleCtx *ctx, RedisModuleCallReply *reply, char *str, size_t len) { - RedisModuleString *mystr, *expected; - if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_ERROR) { - return 0; - } - - mystr = RedisModule_CreateStringFromCallReply(reply); - expected = RedisModule_CreateString(ctx,str,len); - if (RedisModule_StringCompare(mystr,expected) != 0) { - const char *mystr_ptr = RedisModule_StringPtrLen(mystr,NULL); - const char *expected_ptr = RedisModule_StringPtrLen(expected,NULL); - RedisModule_Log(ctx,"warning", - "Unexpected Error reply reply '%s' (instead of '%s')", - mystr_ptr, expected_ptr); - return 0; - } - return 1; -} - -int TestAssertStringReply(RedisModuleCtx *ctx, RedisModuleCallReply *reply, char *str, size_t len) { - RedisModuleString *mystr, *expected; - - if (RedisModule_CallReplyType(reply) == REDISMODULE_REPLY_ERROR) { - RedisModule_Log(ctx,"warning","Test error reply: %s", - RedisModule_CallReplyStringPtr(reply, NULL)); - return 0; - } else if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_STRING) { - RedisModule_Log(ctx,"warning","Unexpected reply type %d", - RedisModule_CallReplyType(reply)); - return 0; - } - mystr = RedisModule_CreateStringFromCallReply(reply); - expected = RedisModule_CreateString(ctx,str,len); - if (RedisModule_StringCompare(mystr,expected) != 0) { - const char *mystr_ptr = RedisModule_StringPtrLen(mystr,NULL); - const char *expected_ptr = RedisModule_StringPtrLen(expected,NULL); - RedisModule_Log(ctx,"warning", - "Unexpected string reply '%s' (instead of '%s')", - mystr_ptr, expected_ptr); - return 0; - } - return 1; -} - -/* Return 1 if the reply matches the specified integer, otherwise log errors - * in the server log and return 0. */ -int TestAssertIntegerReply(RedisModuleCtx *ctx, RedisModuleCallReply *reply, long long expected) { - if (RedisModule_CallReplyType(reply) == REDISMODULE_REPLY_ERROR) { - RedisModule_Log(ctx,"warning","Test error reply: %s", - RedisModule_CallReplyStringPtr(reply, NULL)); - return 0; - } else if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_INTEGER) { - RedisModule_Log(ctx,"warning","Unexpected reply type %d", - RedisModule_CallReplyType(reply)); - return 0; - } - long long val = RedisModule_CallReplyInteger(reply); - if (val != expected) { - RedisModule_Log(ctx,"warning", - "Unexpected integer reply '%lld' (instead of '%lld')", - val, expected); - return 0; - } - return 1; -} - -/* Replies "yes", "no" otherwise if the context may execute debug commands */ -int TestCanDebug(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - int flags = RedisModule_GetContextFlags(ctx); - int allFlags = RedisModule_GetContextFlagsAll(); - if ((allFlags & REDISMODULE_CTX_FLAGS_DEBUG_ENABLED) && - (flags & REDISMODULE_CTX_FLAGS_DEBUG_ENABLED)) { - RedisModule_ReplyWithSimpleString(ctx, "yes"); - } else { - RedisModule_ReplyWithSimpleString(ctx, "no"); - } - return REDISMODULE_OK; -} - -#define T(name,...) \ - do { \ - RedisModule_Log(ctx,"warning","Testing %s", name); \ - reply = RedisModule_Call(ctx,name,__VA_ARGS__); \ - } while (0) - -/* TEST.BASICS -- Run all the tests. - * Note: it is useful to run these tests from the module rather than TCL - * since it's easier to check the reply types like that make a distinction - * between 0 and "0", etc. */ -int TestBasics(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModule_AutoMemory(ctx); - RedisModuleCallReply *reply; - - /* Make sure the DB is empty before to proceed. */ - T("dbsize",""); - if (!TestAssertIntegerReply(ctx,reply,0)) goto fail; - - T("ping",""); - if (!TestAssertStringReply(ctx,reply,"PONG",4)) goto fail; - - T("test.call",""); - if (!TestAssertStringReply(ctx,reply,"OK",2)) goto fail; - - T("test.callresp3map",""); - if (!TestAssertStringReply(ctx,reply,"OK",2)) goto fail; - - T("test.callresp3set",""); - if (!TestAssertStringReply(ctx,reply,"OK",2)) goto fail; - - T("test.callresp3double",""); - if (!TestAssertStringReply(ctx,reply,"OK",2)) goto fail; - - T("test.callresp3bool",""); - if (!TestAssertStringReply(ctx,reply,"OK",2)) goto fail; - - T("test.callresp3null",""); - if (!TestAssertStringReply(ctx,reply,"OK",2)) goto fail; - - T("test.callreplywithnestedreply",""); - if (!TestAssertStringReply(ctx,reply,"test",4)) goto fail; - - T("test.callreplywithbignumberreply",""); - if (!TestAssertStringReply(ctx,reply,"1234567999999999999999999999999999999",37)) goto fail; - - T("test.callreplywithverbatimstringreply",""); - if (!TestAssertStringReply(ctx,reply,"txt:This is a verbatim\nstring",29)) goto fail; - - T("test.ctxflags",""); - if (!TestAssertStringReply(ctx,reply,"OK",2)) goto fail; - - T("test.string.append",""); - if (!TestAssertStringReply(ctx,reply,"foobar",6)) goto fail; - - T("test.string.truncate",""); - if (!TestAssertStringReply(ctx,reply,"OK",2)) goto fail; - - T("test.unlink",""); - if (!TestAssertStringReply(ctx,reply,"OK",2)) goto fail; - - T("test.nestedcallreplyarray",""); - if (!TestAssertStringReply(ctx,reply,"OK",2)) goto fail; - - T("test.string.append.am",""); - if (!TestAssertStringReply(ctx,reply,"foobar",6)) goto fail; - - T("test.string.trim",""); - if (!TestAssertStringReply(ctx,reply,"OK",2)) goto fail; - - T("test.string.printf", "cc", "foo", "bar"); - if (!TestAssertStringReply(ctx,reply,"Got 3 args. argv[1]: foo, argv[2]: bar",38)) goto fail; - - T("test.notify", ""); - if (!TestAssertStringReply(ctx,reply,"OK",2)) goto fail; - - T("test.callreplywitharrayreply", ""); - if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_ARRAY) goto fail; - if (RedisModule_CallReplyLength(reply) != 2) goto fail; - if (!TestAssertStringReply(ctx,RedisModule_CallReplyArrayElement(reply, 0),"test",4)) goto fail; - if (!TestAssertStringReply(ctx,RedisModule_CallReplyArrayElement(reply, 1),"1234",4)) goto fail; - - T("foo", "E"); - if (!TestAssertErrorReply(ctx,reply,"ERR unknown command 'foo'",25)) goto fail; - - T("set", "Ec", "x"); - if (!TestAssertErrorReply(ctx,reply,"ERR wrong number of arguments for 'set' command",47)) goto fail; - - T("shutdown", "SE"); - if (!TestAssertErrorReply(ctx,reply,"ERR command 'shutdown' is not allowed on script mode",52)) goto fail; - - T("set", "WEcc", "x", "1"); - if (!TestAssertErrorReply(ctx,reply,"ERR Write command 'set' was called while write is not allowed.",62)) goto fail; - - RedisModule_ReplyWithSimpleString(ctx,"ALL TESTS PASSED"); - return REDISMODULE_OK; - -fail: - RedisModule_ReplyWithSimpleString(ctx, - "SOME TEST DID NOT PASS! Check server logs"); - return REDISMODULE_OK; -} - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - if (RedisModule_Init(ctx,"test",1,REDISMODULE_APIVER_1) - == REDISMODULE_ERR) return REDISMODULE_ERR; - - /* Perform RM_Call inside the RedisModule_OnLoad - * to verify that it works as expected without crashing. - * The tests will verify it on different configurations - * options (cluster/no cluster). A simple ping command - * is enough for this test. */ - RedisModuleCallReply *reply = RedisModule_Call(ctx, "ping", ""); - if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_STRING) { - RedisModule_FreeCallReply(reply); - return REDISMODULE_ERR; - } - size_t len; - const char *reply_str = RedisModule_CallReplyStringPtr(reply, &len); - if (len != 4) { - RedisModule_FreeCallReply(reply); - return REDISMODULE_ERR; - } - if (memcmp(reply_str, "PONG", 4) != 0) { - RedisModule_FreeCallReply(reply); - return REDISMODULE_ERR; - } - RedisModule_FreeCallReply(reply); - - if (RedisModule_CreateCommand(ctx,"test.call", - TestCall,"write deny-oom",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"test.callresp3map", - TestCallResp3Map,"write deny-oom",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"test.callresp3attribute", - TestCallResp3Attribute,"write deny-oom",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"test.callresp3set", - TestCallResp3Set,"write deny-oom",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"test.callresp3double", - TestCallResp3Double,"write deny-oom",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"test.callresp3bool", - TestCallResp3Bool,"write deny-oom",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"test.callresp3null", - TestCallResp3Null,"write deny-oom",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"test.callreplywitharrayreply", - TestCallReplyWithArrayReply,"write deny-oom",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"test.callreplywithnestedreply", - TestCallReplyWithNestedReply,"write deny-oom",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"test.callreplywithbignumberreply", - TestCallResp3BigNumber,"write deny-oom",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"test.callreplywithverbatimstringreply", - TestCallResp3Verbatim,"write deny-oom",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"test.string.append", - TestStringAppend,"write deny-oom",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"test.string.trim", - TestTrimString,"write deny-oom",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"test.string.append.am", - TestStringAppendAM,"write deny-oom",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"test.string.truncate", - TestStringTruncate,"write deny-oom",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"test.string.printf", - TestStringPrintf,"write deny-oom",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"test.ctxflags", - TestCtxFlags,"readonly",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"test.unlink", - TestUnlink,"write deny-oom",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"test.nestedcallreplyarray", - TestNestedCallReplyArrayElement,"write deny-oom",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"test.basics", - TestBasics,"write",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - /* the following commands are used by an external test and should not be added to TestBasics */ - if (RedisModule_CreateCommand(ctx,"test.rmcallautomode", - TestCallRespAutoMode,"write",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"test.getresp", - TestGetResp,"readonly",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"test.candebug", - TestCanDebug,"readonly",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - RedisModule_SubscribeToKeyspaceEvents(ctx, - REDISMODULE_NOTIFY_HASH | - REDISMODULE_NOTIFY_SET | - REDISMODULE_NOTIFY_STRING | - REDISMODULE_NOTIFY_KEY_MISS, - NotifyCallback); - if (RedisModule_CreateCommand(ctx,"test.notify", - TestNotifications,"write deny-oom",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/blockedclient.c b/examples/redis-unstable/tests/modules/blockedclient.c deleted file mode 100644 index dc226ee..0000000 --- a/examples/redis-unstable/tests/modules/blockedclient.c +++ /dev/null @@ -1,723 +0,0 @@ -/* define macros for having usleep */ -#define _BSD_SOURCE -#define _DEFAULT_SOURCE -#include <unistd.h> - -#include "redismodule.h" -#include <assert.h> -#include <stdio.h> -#include <pthread.h> -#include <strings.h> - -#define UNUSED(V) ((void) V) - -/* used to test processing events during slow bg operation */ -static volatile int g_slow_bg_operation = 0; -static volatile int g_is_in_slow_bg_operation = 0; - -void *sub_worker(void *arg) { - // Get Redis module context - RedisModuleCtx *ctx = (RedisModuleCtx *)arg; - - // Try acquiring GIL - int res = RedisModule_ThreadSafeContextTryLock(ctx); - - // GIL is already taken by the calling thread expecting to fail. - assert(res != REDISMODULE_OK); - - return NULL; -} - -void *worker(void *arg) { - // Retrieve blocked client - RedisModuleBlockedClient *bc = (RedisModuleBlockedClient *)arg; - - // Get Redis module context - RedisModuleCtx *ctx = RedisModule_GetThreadSafeContext(bc); - - // Acquire GIL - RedisModule_ThreadSafeContextLock(ctx); - - // Create another thread which will try to acquire the GIL - pthread_t tid; - int res = pthread_create(&tid, NULL, sub_worker, ctx); - assert(res == 0); - - // Wait for thread - pthread_join(tid, NULL); - - // Release GIL - RedisModule_ThreadSafeContextUnlock(ctx); - - // Reply to client - RedisModule_ReplyWithSimpleString(ctx, "OK"); - - // Unblock client - RedisModule_UnblockClient(bc, NULL); - - // Free the Redis module context - RedisModule_FreeThreadSafeContext(ctx); - - return NULL; -} - -int acquire_gil(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - UNUSED(argv); - UNUSED(argc); - - int flags = RedisModule_GetContextFlags(ctx); - int allFlags = RedisModule_GetContextFlagsAll(); - if ((allFlags & REDISMODULE_CTX_FLAGS_MULTI) && - (flags & REDISMODULE_CTX_FLAGS_MULTI)) { - RedisModule_ReplyWithSimpleString(ctx, "Blocked client is not supported inside multi"); - return REDISMODULE_OK; - } - - if ((allFlags & REDISMODULE_CTX_FLAGS_DENY_BLOCKING) && - (flags & REDISMODULE_CTX_FLAGS_DENY_BLOCKING)) { - RedisModule_ReplyWithSimpleString(ctx, "Blocked client is not allowed"); - return REDISMODULE_OK; - } - - /* This command handler tries to acquire the GIL twice - * once in the worker thread using "RedisModule_ThreadSafeContextLock" - * second in the sub-worker thread - * using "RedisModule_ThreadSafeContextTryLock" - * as the GIL is already locked. */ - RedisModuleBlockedClient *bc = RedisModule_BlockClient(ctx, NULL, NULL, NULL, 0); - - pthread_t tid; - int res = pthread_create(&tid, NULL, worker, bc); - assert(res == 0); - pthread_detach(tid); - - return REDISMODULE_OK; -} - -typedef struct { - RedisModuleString **argv; - int argc; - RedisModuleBlockedClient *bc; -} bg_call_data; - -void *bg_call_worker(void *arg) { - bg_call_data *bg = arg; - RedisModuleBlockedClient *bc = bg->bc; - - // Get Redis module context - RedisModuleCtx *ctx = RedisModule_GetThreadSafeContext(bg->bc); - - // Acquire GIL - RedisModule_ThreadSafeContextLock(ctx); - - // Test slow operation yielding - if (g_slow_bg_operation) { - g_is_in_slow_bg_operation = 1; - while (g_slow_bg_operation) { - RedisModule_Yield(ctx, REDISMODULE_YIELD_FLAG_CLIENTS, "Slow module operation"); - usleep(1000); - } - g_is_in_slow_bg_operation = 0; - } - - // Call the command - const char *module_cmd = RedisModule_StringPtrLen(bg->argv[0], NULL); - int cmd_pos = 1; - RedisModuleString *format_redis_str = RedisModule_CreateString(NULL, "v", 1); - if (!strcasecmp(module_cmd, "do_bg_rm_call_format")) { - cmd_pos = 2; - size_t format_len; - const char *format = RedisModule_StringPtrLen(bg->argv[1], &format_len); - RedisModule_StringAppendBuffer(NULL, format_redis_str, format, format_len); - RedisModule_StringAppendBuffer(NULL, format_redis_str, "E", 1); - } - const char *format = RedisModule_StringPtrLen(format_redis_str, NULL); - const char *cmd = RedisModule_StringPtrLen(bg->argv[cmd_pos], NULL); - RedisModuleCallReply *rep = RedisModule_Call(ctx, cmd, format, bg->argv + cmd_pos + 1, (size_t)bg->argc - cmd_pos - 1); - RedisModule_FreeString(NULL, format_redis_str); - - /* Free the arguments within GIL to prevent simultaneous freeing in main thread. */ - for (int i=0; i<bg->argc; i++) - RedisModule_FreeString(ctx, bg->argv[i]); - RedisModule_Free(bg->argv); - RedisModule_Free(bg); - - // Release GIL - RedisModule_ThreadSafeContextUnlock(ctx); - - // Reply to client - if (!rep) { - RedisModule_ReplyWithError(ctx, "NULL reply returned"); - } else { - RedisModule_ReplyWithCallReply(ctx, rep); - RedisModule_FreeCallReply(rep); - } - - // Unblock client - RedisModule_UnblockClient(bc, NULL); - - // Free the Redis module context - RedisModule_FreeThreadSafeContext(ctx); - - return NULL; -} - -int do_bg_rm_call(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - UNUSED(argv); - UNUSED(argc); - - /* Make sure we're not trying to block a client when we shouldn't */ - int flags = RedisModule_GetContextFlags(ctx); - int allFlags = RedisModule_GetContextFlagsAll(); - if ((allFlags & REDISMODULE_CTX_FLAGS_MULTI) && - (flags & REDISMODULE_CTX_FLAGS_MULTI)) { - RedisModule_ReplyWithSimpleString(ctx, "Blocked client is not supported inside multi"); - return REDISMODULE_OK; - } - if ((allFlags & REDISMODULE_CTX_FLAGS_DENY_BLOCKING) && - (flags & REDISMODULE_CTX_FLAGS_DENY_BLOCKING)) { - RedisModule_ReplyWithSimpleString(ctx, "Blocked client is not allowed"); - return REDISMODULE_OK; - } - - /* Make a copy of the arguments and pass them to the thread. */ - bg_call_data *bg = RedisModule_Alloc(sizeof(bg_call_data)); - bg->argv = RedisModule_Alloc(sizeof(RedisModuleString*)*argc); - bg->argc = argc; - for (int i=0; i<argc; i++) - bg->argv[i] = RedisModule_HoldString(ctx, argv[i]); - - /* Block the client */ - bg->bc = RedisModule_BlockClient(ctx, NULL, NULL, NULL, 0); - - /* Start a thread to handle the request */ - pthread_t tid; - int res = pthread_create(&tid, NULL, bg_call_worker, bg); - assert(res == 0); - pthread_detach(tid); - - return REDISMODULE_OK; -} - -int do_rm_call(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){ - UNUSED(argv); - UNUSED(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, (size_t)argc - 2); - if(!rep){ - RedisModule_ReplyWithError(ctx, "NULL reply returned"); - }else{ - RedisModule_ReplyWithCallReply(ctx, rep); - RedisModule_FreeCallReply(rep); - } - - return REDISMODULE_OK; -} - -static void rm_call_async_send_reply(RedisModuleCtx *ctx, RedisModuleCallReply *reply) { - RedisModule_ReplyWithCallReply(ctx, reply); - RedisModule_FreeCallReply(reply); -} - -/* Called when the command that was blocked on 'RM_Call' gets unblocked - * and send the reply to the blocked client. */ -static void rm_call_async_on_unblocked(RedisModuleCtx *ctx, RedisModuleCallReply *reply, void *private_data) { - UNUSED(ctx); - RedisModuleBlockedClient *bc = private_data; - RedisModuleCtx *bctx = RedisModule_GetThreadSafeContext(bc); - rm_call_async_send_reply(bctx, reply); - RedisModule_FreeThreadSafeContext(bctx); - RedisModule_UnblockClient(bc, RedisModule_BlockClientGetPrivateData(bc)); -} - -int do_rm_call_async_fire_and_forget(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){ - UNUSED(argv); - UNUSED(argc); - - if(argc < 2){ - return RedisModule_WrongArity(ctx); - } - const char* cmd = RedisModule_StringPtrLen(argv[1], NULL); - - RedisModuleCallReply* rep = RedisModule_Call(ctx, cmd, "!KEv", argv + 2, (size_t)argc - 2); - - if(RedisModule_CallReplyType(rep) != REDISMODULE_REPLY_PROMISE) { - RedisModule_ReplyWithCallReply(ctx, rep); - } else { - RedisModule_ReplyWithSimpleString(ctx, "Blocked"); - } - RedisModule_FreeCallReply(rep); - - return REDISMODULE_OK; -} - -static void do_rm_call_async_free_pd(RedisModuleCtx * ctx, void *pd) { - UNUSED(ctx); - RedisModule_FreeCallReply(pd); -} - -static void do_rm_call_async_disconnect(RedisModuleCtx *ctx, struct RedisModuleBlockedClient *bc) { - UNUSED(ctx); - RedisModuleCallReply* rep = RedisModule_BlockClientGetPrivateData(bc); - RedisModule_CallReplyPromiseAbort(rep, NULL); - RedisModule_FreeCallReply(rep); - RedisModule_AbortBlock(bc); -} - -/* - * Callback for do_rm_call_async / do_rm_call_async_script_mode - * Gets the command to invoke as the first argument to the command and runs it, - * passing the rest of the arguments to the command invocation. - * If the command got blocked, blocks the client and unblock it when the command gets unblocked, - * this allows check the K (allow blocking) argument to RM_Call. - */ -int do_rm_call_async(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){ - UNUSED(argv); - UNUSED(argc); - - if(argc < 2){ - return RedisModule_WrongArity(ctx); - } - - size_t format_len = 0; - char format[6] = {0}; - - if (!(RedisModule_GetContextFlags(ctx) & REDISMODULE_CTX_FLAGS_DENY_BLOCKING)) { - /* We are allowed to block the client so we can allow RM_Call to also block us */ - format[format_len++] = 'K'; - } - - const char* invoked_cmd = RedisModule_StringPtrLen(argv[0], NULL); - if (strcasecmp(invoked_cmd, "do_rm_call_async_script_mode") == 0) { - format[format_len++] = 'S'; - } - - format[format_len++] = 'E'; - format[format_len++] = 'v'; - if (strcasecmp(invoked_cmd, "do_rm_call_async_no_replicate") != 0) { - /* Notice, without the '!' flag we will have inconsistency between master and replica. - * This is used only to check '!' flag correctness on blocked commands. */ - format[format_len++] = '!'; - } - - const char* cmd = RedisModule_StringPtrLen(argv[1], NULL); - - RedisModuleCallReply* rep = RedisModule_Call(ctx, cmd, format, argv + 2, (size_t)argc - 2); - - if(RedisModule_CallReplyType(rep) != REDISMODULE_REPLY_PROMISE) { - rm_call_async_send_reply(ctx, rep); - } else { - RedisModuleBlockedClient *bc = RedisModule_BlockClient(ctx, NULL, NULL, do_rm_call_async_free_pd, 0); - RedisModule_SetDisconnectCallback(bc, do_rm_call_async_disconnect); - RedisModule_BlockClientSetPrivateData(bc, rep); - RedisModule_CallReplyPromiseSetUnblockHandler(rep, rm_call_async_on_unblocked, bc); - } - - return REDISMODULE_OK; -} - -typedef struct ThreadedAsyncRMCallCtx{ - RedisModuleBlockedClient *bc; - RedisModuleCallReply *reply; -} ThreadedAsyncRMCallCtx; - -void *send_async_reply(void *arg) { - ThreadedAsyncRMCallCtx *ta_rm_call_ctx = arg; - rm_call_async_on_unblocked(NULL, ta_rm_call_ctx->reply, ta_rm_call_ctx->bc); - RedisModule_Free(ta_rm_call_ctx); - return NULL; -} - -/* Called when the command that was blocked on 'RM_Call' gets unblocked - * and schedule a thread to send the reply to the blocked client. */ -static void rm_call_async_reply_on_thread(RedisModuleCtx *ctx, RedisModuleCallReply *reply, void *private_data) { - UNUSED(ctx); - ThreadedAsyncRMCallCtx *ta_rm_call_ctx = RedisModule_Alloc(sizeof(*ta_rm_call_ctx)); - ta_rm_call_ctx->bc = private_data; - ta_rm_call_ctx->reply = reply; - pthread_t tid; - int res = pthread_create(&tid, NULL, send_async_reply, ta_rm_call_ctx); - assert(res == 0); - pthread_detach(tid); -} - -/* - * Callback for do_rm_call_async_on_thread. - * Gets the command to invoke as the first argument to the command and runs it, - * passing the rest of the arguments to the command invocation. - * If the command got blocked, blocks the client and unblock on a background thread. - * this allows check the K (allow blocking) argument to RM_Call, and make sure that the reply - * that passes to unblock handler is owned by the handler and are not attached to any - * context that might be freed after the callback ends. - */ -int do_rm_call_async_on_thread(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){ - UNUSED(argv); - UNUSED(argc); - - if(argc < 2){ - return RedisModule_WrongArity(ctx); - } - - const char* cmd = RedisModule_StringPtrLen(argv[1], NULL); - - RedisModuleCallReply* rep = RedisModule_Call(ctx, cmd, "KEv", argv + 2, (size_t)argc - 2); - - if(RedisModule_CallReplyType(rep) != REDISMODULE_REPLY_PROMISE) { - rm_call_async_send_reply(ctx, rep); - } else { - RedisModuleBlockedClient *bc = RedisModule_BlockClient(ctx, NULL, NULL, NULL, 0); - RedisModule_CallReplyPromiseSetUnblockHandler(rep, rm_call_async_reply_on_thread, bc); - RedisModule_FreeCallReply(rep); - } - - return REDISMODULE_OK; -} - -/* Private data for wait_and_do_rm_call_async that holds information about: - * 1. the block client, to unblock when done. - * 2. the arguments, contains the command to run using RM_Call */ -typedef struct WaitAndDoRMCallCtx { - RedisModuleBlockedClient *bc; - RedisModuleString **argv; - int argc; -} WaitAndDoRMCallCtx; - -/* - * This callback will be called when the 'wait' command invoke on 'wait_and_do_rm_call_async' will finish. - * This callback will continue the execution flow just like 'do_rm_call_async' command. - */ -static void wait_and_do_rm_call_async_on_unblocked(RedisModuleCtx *ctx, RedisModuleCallReply *reply, void *private_data) { - WaitAndDoRMCallCtx *wctx = private_data; - if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_INTEGER) { - goto done; - } - - if (RedisModule_CallReplyInteger(reply) != 1) { - goto done; - } - - RedisModule_FreeCallReply(reply); - reply = NULL; - - const char* cmd = RedisModule_StringPtrLen(wctx->argv[0], NULL); - reply = RedisModule_Call(ctx, cmd, "!EKv", wctx->argv + 1, (size_t)wctx->argc - 1); - -done: - if(RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_PROMISE) { - RedisModuleCtx *bctx = RedisModule_GetThreadSafeContext(wctx->bc); - rm_call_async_send_reply(bctx, reply); - RedisModule_FreeThreadSafeContext(bctx); - RedisModule_UnblockClient(wctx->bc, NULL); - } else { - RedisModule_CallReplyPromiseSetUnblockHandler(reply, rm_call_async_on_unblocked, wctx->bc); - RedisModule_FreeCallReply(reply); - } - for (int i = 0 ; i < wctx->argc ; ++i) { - RedisModule_FreeString(NULL, wctx->argv[i]); - } - RedisModule_Free(wctx->argv); - RedisModule_Free(wctx); -} - -/* - * Callback for wait_and_do_rm_call - * Gets the command to invoke as the first argument, runs 'wait' - * command (using the K flag to RM_Call). Once the wait finished, runs the - * command that was given (just like 'do_rm_call_async'). - */ -int wait_and_do_rm_call_async(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - UNUSED(argv); - UNUSED(argc); - - if(argc < 2){ - return RedisModule_WrongArity(ctx); - } - - int flags = RedisModule_GetContextFlags(ctx); - if (flags & REDISMODULE_CTX_FLAGS_DENY_BLOCKING) { - return RedisModule_ReplyWithError(ctx, "Err can not run wait, blocking is not allowed."); - } - - RedisModuleCallReply* rep = RedisModule_Call(ctx, "wait", "!EKcc", "1", "0"); - if(RedisModule_CallReplyType(rep) != REDISMODULE_REPLY_PROMISE) { - rm_call_async_send_reply(ctx, rep); - } else { - RedisModuleBlockedClient *bc = RedisModule_BlockClient(ctx, NULL, NULL, NULL, 0); - WaitAndDoRMCallCtx *wctx = RedisModule_Alloc(sizeof(*wctx)); - *wctx = (WaitAndDoRMCallCtx){ - .bc = bc, - .argv = RedisModule_Alloc((argc - 1) * sizeof(RedisModuleString*)), - .argc = argc - 1, - }; - - for (int i = 1 ; i < argc ; ++i) { - wctx->argv[i - 1] = RedisModule_HoldString(NULL, argv[i]); - } - RedisModule_CallReplyPromiseSetUnblockHandler(rep, wait_and_do_rm_call_async_on_unblocked, wctx); - RedisModule_FreeCallReply(rep); - } - - return REDISMODULE_OK; -} - -static void blpop_and_set_multiple_keys_on_unblocked(RedisModuleCtx *ctx, RedisModuleCallReply *reply, void *private_data) { - /* ignore the reply */ - RedisModule_FreeCallReply(reply); - WaitAndDoRMCallCtx *wctx = private_data; - for (int i = 0 ; i < wctx->argc ; i += 2) { - RedisModuleCallReply* rep = RedisModule_Call(ctx, "set", "!ss", wctx->argv[i], wctx->argv[i + 1]); - RedisModule_FreeCallReply(rep); - } - - RedisModuleCtx *bctx = RedisModule_GetThreadSafeContext(wctx->bc); - RedisModule_ReplyWithSimpleString(bctx, "OK"); - RedisModule_FreeThreadSafeContext(bctx); - RedisModule_UnblockClient(wctx->bc, NULL); - - for (int i = 0 ; i < wctx->argc ; ++i) { - RedisModule_FreeString(NULL, wctx->argv[i]); - } - RedisModule_Free(wctx->argv); - RedisModule_Free(wctx); - -} - -/* - * Performs a blpop command on a given list and when unblocked set multiple string keys. - * This command allows checking that the unblock callback is performed as a unit - * and its effect are replicated to the replica and AOF wrapped with multi exec. - */ -int blpop_and_set_multiple_keys(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - UNUSED(argv); - UNUSED(argc); - - if(argc < 2 || argc % 2 != 0){ - return RedisModule_WrongArity(ctx); - } - - int flags = RedisModule_GetContextFlags(ctx); - if (flags & REDISMODULE_CTX_FLAGS_DENY_BLOCKING) { - return RedisModule_ReplyWithError(ctx, "Err can not run wait, blocking is not allowed."); - } - - RedisModuleCallReply* rep = RedisModule_Call(ctx, "blpop", "!EKsc", argv[1], "0"); - if(RedisModule_CallReplyType(rep) != REDISMODULE_REPLY_PROMISE) { - rm_call_async_send_reply(ctx, rep); - } else { - RedisModuleBlockedClient *bc = RedisModule_BlockClient(ctx, NULL, NULL, NULL, 0); - WaitAndDoRMCallCtx *wctx = RedisModule_Alloc(sizeof(*wctx)); - *wctx = (WaitAndDoRMCallCtx){ - .bc = bc, - .argv = RedisModule_Alloc((argc - 2) * sizeof(RedisModuleString*)), - .argc = argc - 2, - }; - - for (int i = 0 ; i < argc - 2 ; ++i) { - wctx->argv[i] = RedisModule_HoldString(NULL, argv[i + 2]); - } - RedisModule_CallReplyPromiseSetUnblockHandler(rep, blpop_and_set_multiple_keys_on_unblocked, wctx); - RedisModule_FreeCallReply(rep); - } - - return REDISMODULE_OK; -} - -/* simulate a blocked client replying to a thread safe context without creating a thread */ -int do_fake_bg_true(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - UNUSED(argv); - UNUSED(argc); - - RedisModuleBlockedClient *bc = RedisModule_BlockClient(ctx, NULL, NULL, NULL, 0); - RedisModuleCtx *bctx = RedisModule_GetThreadSafeContext(bc); - - RedisModule_ReplyWithBool(bctx, 1); - - RedisModule_FreeThreadSafeContext(bctx); - RedisModule_UnblockClient(bc, NULL); - - return REDISMODULE_OK; -} - - -/* this flag is used to work with busy commands, that might take a while - * and ability to stop the busy work with a different command*/ -static volatile int abort_flag = 0; - -int slow_fg_command(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - long long block_time = 0; - if (RedisModule_StringToLongLong(argv[1], &block_time) != REDISMODULE_OK) { - RedisModule_ReplyWithError(ctx, "Invalid integer value"); - return REDISMODULE_OK; - } - - uint64_t start_time = RedisModule_MonotonicMicroseconds(); - /* when not blocking indefinitely, we don't process client commands in this test. */ - int yield_flags = block_time? REDISMODULE_YIELD_FLAG_NONE: REDISMODULE_YIELD_FLAG_CLIENTS; - while (!abort_flag) { - RedisModule_Yield(ctx, yield_flags, "Slow module operation"); - usleep(1000); - if (block_time && RedisModule_MonotonicMicroseconds() - start_time > (uint64_t)block_time) - break; - } - - abort_flag = 0; - RedisModule_ReplyWithLongLong(ctx, 1); - return REDISMODULE_OK; -} - -int stop_slow_fg_command(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - abort_flag = 1; - RedisModule_ReplyWithLongLong(ctx, 1); - return REDISMODULE_OK; -} - -/* used to enable or disable slow operation in do_bg_rm_call */ -static int set_slow_bg_operation(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - long long ll; - if (RedisModule_StringToLongLong(argv[1], &ll) != REDISMODULE_OK) { - RedisModule_ReplyWithError(ctx, "Invalid integer value"); - return REDISMODULE_OK; - } - g_slow_bg_operation = ll; - RedisModule_ReplyWithSimpleString(ctx, "OK"); - return REDISMODULE_OK; -} - -/* used to test if we reached the slow operation in do_bg_rm_call */ -static int is_in_slow_bg_operation(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - UNUSED(argv); - if (argc != 1) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - RedisModule_ReplyWithLongLong(ctx, g_is_in_slow_bg_operation); - return REDISMODULE_OK; -} - -static void timer_callback(RedisModuleCtx *ctx, void *data) -{ - UNUSED(ctx); - - RedisModuleBlockedClient *bc = data; - - // Get Redis module context - RedisModuleCtx *reply_ctx = RedisModule_GetThreadSafeContext(bc); - - // Reply to client - RedisModule_ReplyWithSimpleString(reply_ctx, "OK"); - - // Unblock client - RedisModule_UnblockClient(bc, NULL); - - // Free the Redis module context - RedisModule_FreeThreadSafeContext(reply_ctx); -} - -/* unblock_by_timer <period_ms> <timeout_ms> - * period_ms is the period of the timer. - * timeout_ms is the blocking timeout. */ -int unblock_by_timer(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - if (argc != 3) - return RedisModule_WrongArity(ctx); - - long long period; - long long timeout; - if (RedisModule_StringToLongLong(argv[1],&period) != REDISMODULE_OK) - return RedisModule_ReplyWithError(ctx,"ERR invalid period"); - if (RedisModule_StringToLongLong(argv[2],&timeout) != REDISMODULE_OK) { - return RedisModule_ReplyWithError(ctx,"ERR invalid timeout"); - } - - RedisModuleBlockedClient *bc = RedisModule_BlockClient(ctx, NULL, NULL, NULL, timeout); - RedisModule_CreateTimer(ctx, period, timer_callback, bc); - return REDISMODULE_OK; -} - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - if (RedisModule_Init(ctx, "blockedclient", 1, REDISMODULE_APIVER_1)== REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "acquire_gil", acquire_gil, "", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "do_rm_call", do_rm_call, - "write", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "do_rm_call_async", do_rm_call_async, - "write", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "do_rm_call_async_on_thread", do_rm_call_async_on_thread, - "write", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "do_rm_call_async_script_mode", do_rm_call_async, - "write", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "do_rm_call_async_no_replicate", do_rm_call_async, - "write", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "do_rm_call_fire_and_forget", do_rm_call_async_fire_and_forget, - "write", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "wait_and_do_rm_call", wait_and_do_rm_call_async, - "write", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "blpop_and_set_multiple_keys", blpop_and_set_multiple_keys, - "write", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "do_bg_rm_call", do_bg_rm_call, "", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "do_bg_rm_call_format", do_bg_rm_call, "", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "do_fake_bg_true", do_fake_bg_true, "", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "slow_fg_command", slow_fg_command,"", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "stop_slow_fg_command", stop_slow_fg_command,"allow-busy", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "set_slow_bg_operation", set_slow_bg_operation, "allow-busy", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "is_in_slow_bg_operation", is_in_slow_bg_operation, "allow-busy", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "unblock_by_timer", unblock_by_timer, "", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/blockonbackground.c b/examples/redis-unstable/tests/modules/blockonbackground.c deleted file mode 100644 index 7aeb011..0000000 --- a/examples/redis-unstable/tests/modules/blockonbackground.c +++ /dev/null @@ -1,333 +0,0 @@ -#define _XOPEN_SOURCE 700 -#include "redismodule.h" -#include <stdio.h> -#include <stdlib.h> -#include <pthread.h> -#include <time.h> - -#define UNUSED(x) (void)(x) - -typedef struct { - /* Mutex for protecting RedisModule_BlockedClientMeasureTime*() API from race - * conditions due to timeout callback triggered in the main thread. */ - pthread_mutex_t measuretime_mutex; - int measuretime_completed; /* Indicates that time measure has ended and will not continue further */ - int myint; /* Used for replying */ -} BlockPrivdata; - -void blockClientPrivdataInit(RedisModuleBlockedClient *bc) { - BlockPrivdata *block_privdata = RedisModule_Calloc(1, sizeof(*block_privdata)); - block_privdata->measuretime_mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER; - RedisModule_BlockClientSetPrivateData(bc, block_privdata); -} - -void blockClientMeasureTimeStart(RedisModuleBlockedClient *bc, BlockPrivdata *block_privdata) { - pthread_mutex_lock(&block_privdata->measuretime_mutex); - RedisModule_BlockedClientMeasureTimeStart(bc); - pthread_mutex_unlock(&block_privdata->measuretime_mutex); -} - -void blockClientMeasureTimeEnd(RedisModuleBlockedClient *bc, BlockPrivdata *block_privdata, int completed) { - pthread_mutex_lock(&block_privdata->measuretime_mutex); - if (!block_privdata->measuretime_completed) { - RedisModule_BlockedClientMeasureTimeEnd(bc); - if (completed) block_privdata->measuretime_completed = 1; - } - pthread_mutex_unlock(&block_privdata->measuretime_mutex); -} - -/* Reply callback for blocking command BLOCK.DEBUG */ -int HelloBlock_Reply(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - UNUSED(argv); - UNUSED(argc); - BlockPrivdata *block_privdata = RedisModule_GetBlockedClientPrivateData(ctx); - return RedisModule_ReplyWithLongLong(ctx,block_privdata->myint); -} - -/* Timeout callback for blocking command BLOCK.DEBUG */ -int HelloBlock_Timeout(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - UNUSED(argv); - UNUSED(argc); - RedisModuleBlockedClient *bc = RedisModule_GetBlockedClientHandle(ctx); - BlockPrivdata *block_privdata = RedisModule_GetBlockedClientPrivateData(ctx); - blockClientMeasureTimeEnd(bc, block_privdata, 1); - return RedisModule_ReplyWithSimpleString(ctx,"Request timedout"); -} - -/* Private data freeing callback for BLOCK.DEBUG command. */ -void HelloBlock_FreeData(RedisModuleCtx *ctx, void *privdata) { - UNUSED(ctx); - BlockPrivdata *block_privdata = privdata; - pthread_mutex_destroy(&block_privdata->measuretime_mutex); - RedisModule_Free(privdata); -} - -/* Private data freeing callback for BLOCK.BLOCK command. */ -void HelloBlock_FreeStringData(RedisModuleCtx *ctx, void *privdata) { - RedisModule_FreeString(ctx, (RedisModuleString*)privdata); -} - -/* The thread entry point that actually executes the blocking part - * of the command BLOCK.DEBUG. */ -void *BlockDebug_ThreadMain(void *arg) { - void **targ = arg; - RedisModuleBlockedClient *bc = targ[0]; - long long delay = (unsigned long)targ[1]; - long long enable_time_track = (unsigned long)targ[2]; - BlockPrivdata *block_privdata = RedisModule_BlockClientGetPrivateData(bc); - - if (enable_time_track) - blockClientMeasureTimeStart(bc, block_privdata); - RedisModule_Free(targ); - - struct timespec ts; - ts.tv_sec = delay / 1000; - ts.tv_nsec = (delay % 1000) * 1000000; - nanosleep(&ts, NULL); - if (enable_time_track) - blockClientMeasureTimeEnd(bc, block_privdata, 0); - block_privdata->myint = rand(); - RedisModule_UnblockClient(bc,block_privdata); - return NULL; -} - -/* The thread entry point that actually executes the blocking part - * of the command BLOCK.DOUBLE_DEBUG. */ -void *DoubleBlock_ThreadMain(void *arg) { - void **targ = arg; - RedisModuleBlockedClient *bc = targ[0]; - long long delay = (unsigned long)targ[1]; - BlockPrivdata *block_privdata = RedisModule_BlockClientGetPrivateData(bc); - blockClientMeasureTimeStart(bc, block_privdata); - RedisModule_Free(targ); - struct timespec ts; - ts.tv_sec = delay / 1000; - ts.tv_nsec = (delay % 1000) * 1000000; - nanosleep(&ts, NULL); - blockClientMeasureTimeEnd(bc, block_privdata, 0); - /* call again RedisModule_BlockedClientMeasureTimeStart() and - * RedisModule_BlockedClientMeasureTimeEnd and ensure that the - * total execution time is 2x the delay. */ - blockClientMeasureTimeStart(bc, block_privdata); - nanosleep(&ts, NULL); - blockClientMeasureTimeEnd(bc, block_privdata, 0); - block_privdata->myint = rand(); - RedisModule_UnblockClient(bc,block_privdata); - return NULL; -} - -void HelloBlock_Disconnected(RedisModuleCtx *ctx, RedisModuleBlockedClient *bc) { - RedisModule_Log(ctx,"warning","Blocked client %p disconnected!", - (void*)bc); -} - -/* BLOCK.DEBUG <delay_ms> <timeout_ms> -- Block for <count> milliseconds, then reply with - * a random number. Timeout is the command timeout, so that you can test - * what happens when the delay is greater than the timeout. */ -int HelloBlock_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 3) return RedisModule_WrongArity(ctx); - long long delay; - long long timeout; - - if (RedisModule_StringToLongLong(argv[1],&delay) != REDISMODULE_OK) { - return RedisModule_ReplyWithError(ctx,"ERR invalid count"); - } - - if (RedisModule_StringToLongLong(argv[2],&timeout) != REDISMODULE_OK) { - return RedisModule_ReplyWithError(ctx,"ERR invalid count"); - } - - pthread_t tid; - RedisModuleBlockedClient *bc = RedisModule_BlockClient(ctx,HelloBlock_Reply,HelloBlock_Timeout,HelloBlock_FreeData,timeout); - blockClientPrivdataInit(bc); - - /* Here we set a disconnection handler, however since this module will - * block in sleep() in a thread, there is not much we can do in the - * callback, so this is just to show you the API. */ - RedisModule_SetDisconnectCallback(bc,HelloBlock_Disconnected); - - /* Now that we setup a blocking client, we need to pass the control - * to the thread. However we need to pass arguments to the thread: - * the delay and a reference to the blocked client handle. */ - void **targ = RedisModule_Alloc(sizeof(void*)*3); - targ[0] = bc; - targ[1] = (void*)(unsigned long) delay; - // pass 1 as flag to enable time tracking - targ[2] = (void*)(unsigned long) 1; - - if (pthread_create(&tid,NULL,BlockDebug_ThreadMain,targ) != 0) { - RedisModule_AbortBlock(bc); - return RedisModule_ReplyWithError(ctx,"-ERR Can't start thread"); - } - pthread_detach(tid); - return REDISMODULE_OK; -} - -/* BLOCK.DEBUG_NOTRACKING <delay_ms> <timeout_ms> -- Block for <count> milliseconds, then reply with - * a random number. Timeout is the command timeout, so that you can test - * what happens when the delay is greater than the timeout. - * this command does not track background time so the background time should no appear in stats*/ -int HelloBlockNoTracking_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 3) return RedisModule_WrongArity(ctx); - long long delay; - long long timeout; - - if (RedisModule_StringToLongLong(argv[1],&delay) != REDISMODULE_OK) { - return RedisModule_ReplyWithError(ctx,"ERR invalid count"); - } - - if (RedisModule_StringToLongLong(argv[2],&timeout) != REDISMODULE_OK) { - return RedisModule_ReplyWithError(ctx,"ERR invalid count"); - } - - pthread_t tid; - RedisModuleBlockedClient *bc = RedisModule_BlockClient(ctx,HelloBlock_Reply,HelloBlock_Timeout,HelloBlock_FreeData,timeout); - blockClientPrivdataInit(bc); - - /* Here we set a disconnection handler, however since this module will - * block in sleep() in a thread, there is not much we can do in the - * callback, so this is just to show you the API. */ - RedisModule_SetDisconnectCallback(bc,HelloBlock_Disconnected); - - /* Now that we setup a blocking client, we need to pass the control - * to the thread. However we need to pass arguments to the thread: - * the delay and a reference to the blocked client handle. */ - void **targ = RedisModule_Alloc(sizeof(void*)*3); - targ[0] = bc; - targ[1] = (void*)(unsigned long) delay; - // pass 0 as flag to enable time tracking - targ[2] = (void*)(unsigned long) 0; - - if (pthread_create(&tid,NULL,BlockDebug_ThreadMain,targ) != 0) { - RedisModule_AbortBlock(bc); - return RedisModule_ReplyWithError(ctx,"-ERR Can't start thread"); - } - pthread_detach(tid); - return REDISMODULE_OK; -} - -/* BLOCK.DOUBLE_DEBUG <delay_ms> -- Block for 2 x <count> milliseconds, - * then reply with a random number. - * This command is used to test multiple calls to RedisModule_BlockedClientMeasureTimeStart() - * and RedisModule_BlockedClientMeasureTimeEnd() within the same execution. */ -int HelloDoubleBlock_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) return RedisModule_WrongArity(ctx); - long long delay; - - if (RedisModule_StringToLongLong(argv[1],&delay) != REDISMODULE_OK) { - return RedisModule_ReplyWithError(ctx,"ERR invalid count"); - } - - pthread_t tid; - RedisModuleBlockedClient *bc = RedisModule_BlockClient(ctx,HelloBlock_Reply,HelloBlock_Timeout,HelloBlock_FreeData,0); - blockClientPrivdataInit(bc); - - /* Now that we setup a blocking client, we need to pass the control - * to the thread. However we need to pass arguments to the thread: - * the delay and a reference to the blocked client handle. */ - void **targ = RedisModule_Alloc(sizeof(void*)*2); - targ[0] = bc; - targ[1] = (void*)(unsigned long) delay; - - if (pthread_create(&tid,NULL,DoubleBlock_ThreadMain,targ) != 0) { - RedisModule_AbortBlock(bc); - return RedisModule_ReplyWithError(ctx,"-ERR Can't start thread"); - } - pthread_detach(tid); - return REDISMODULE_OK; -} - -RedisModuleBlockedClient *blocked_client = NULL; - -/* BLOCK.BLOCK [TIMEOUT] -- Blocks the current client until released - * or TIMEOUT seconds. If TIMEOUT is zero, no timeout function is - * registered. - */ -int Block_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (RedisModule_IsBlockedReplyRequest(ctx)) { - RedisModuleString *r = RedisModule_GetBlockedClientPrivateData(ctx); - return RedisModule_ReplyWithString(ctx, r); - } else if (RedisModule_IsBlockedTimeoutRequest(ctx)) { - RedisModule_UnblockClient(blocked_client, NULL); /* Must be called to avoid leaks. */ - blocked_client = NULL; - return RedisModule_ReplyWithSimpleString(ctx, "Timed out"); - } - - if (argc != 2) return RedisModule_WrongArity(ctx); - long long timeout; - - if (RedisModule_StringToLongLong(argv[1], &timeout) != REDISMODULE_OK) { - return RedisModule_ReplyWithError(ctx, "ERR invalid timeout"); - } - if (blocked_client) { - return RedisModule_ReplyWithError(ctx, "ERR another client already blocked"); - } - - /* Block client. We use this function as both a reply and optional timeout - * callback and differentiate the different code flows above. - */ - blocked_client = RedisModule_BlockClient(ctx, Block_RedisCommand, - timeout > 0 ? Block_RedisCommand : NULL, HelloBlock_FreeStringData, timeout); - return REDISMODULE_OK; -} - -/* BLOCK.IS_BLOCKED -- Returns 1 if we have a blocked client, or 0 otherwise. - */ -int IsBlocked_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - UNUSED(argv); - UNUSED(argc); - RedisModule_ReplyWithLongLong(ctx, blocked_client ? 1 : 0); - return REDISMODULE_OK; -} - -/* BLOCK.RELEASE [reply] -- Releases the blocked client and produce the specified reply. - */ -int Release_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) return RedisModule_WrongArity(ctx); - if (!blocked_client) { - return RedisModule_ReplyWithError(ctx, "ERR No blocked client"); - } - - RedisModuleString *replystr = argv[1]; - RedisModule_RetainString(ctx, replystr); - RedisModule_UnblockClient(blocked_client, replystr); - blocked_client = NULL; - - RedisModule_ReplyWithSimpleString(ctx, "OK"); - - return REDISMODULE_OK; -} - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - UNUSED(argv); - UNUSED(argc); - - if (RedisModule_Init(ctx,"block",1,REDISMODULE_APIVER_1) - == REDISMODULE_ERR) return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"block.debug", - HelloBlock_RedisCommand,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"block.double_debug", - HelloDoubleBlock_RedisCommand,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"block.debug_no_track", - HelloBlockNoTracking_RedisCommand,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "block.block", - Block_RedisCommand, "", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"block.is_blocked", - IsBlocked_RedisCommand,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"block.release", - Release_RedisCommand,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/blockonkeys.c b/examples/redis-unstable/tests/modules/blockonkeys.c deleted file mode 100644 index 94bb361..0000000 --- a/examples/redis-unstable/tests/modules/blockonkeys.c +++ /dev/null @@ -1,645 +0,0 @@ -#include "redismodule.h" - -#include <string.h> -#include <strings.h> -#include <assert.h> -#include <unistd.h> - -#define UNUSED(V) ((void) V) - -#define LIST_SIZE 1024 - -/* The FSL (Fixed-Size List) data type is a low-budget imitation of the - * native Redis list, in order to test list-like commands implemented - * by a module. - * Examples: FSL.PUSH, FSL.BPOP, etc. */ - -typedef struct { - long long list[LIST_SIZE]; - long long length; -} fsl_t; /* Fixed-size list */ - -static RedisModuleType *fsltype = NULL; - -fsl_t *fsl_type_create(void) { - fsl_t *o; - o = RedisModule_Alloc(sizeof(*o)); - o->length = 0; - return o; -} - -void fsl_type_free(fsl_t *o) { - RedisModule_Free(o); -} - -/* ========================== "fsltype" type methods ======================= */ - -void *fsl_rdb_load(RedisModuleIO *rdb, int encver) { - if (encver != 0) { - return NULL; - } - fsl_t *fsl = fsl_type_create(); - fsl->length = RedisModule_LoadUnsigned(rdb); - for (long long i = 0; i < fsl->length; i++) - fsl->list[i] = RedisModule_LoadSigned(rdb); - return fsl; -} - -void fsl_rdb_save(RedisModuleIO *rdb, void *value) { - fsl_t *fsl = value; - RedisModule_SaveUnsigned(rdb,fsl->length); - for (long long i = 0; i < fsl->length; i++) - RedisModule_SaveSigned(rdb, fsl->list[i]); -} - -void fsl_aofrw(RedisModuleIO *aof, RedisModuleString *key, void *value) { - fsl_t *fsl = value; - for (long long i = 0; i < fsl->length; i++) - RedisModule_EmitAOF(aof, "FSL.PUSH","sl", key, fsl->list[i]); -} - -void fsl_free(void *value) { - fsl_type_free(value); -} - -/* ========================== helper methods ======================= */ - -/* Wrapper to the boilerplate code of opening a key, checking its type, etc. - * Returns 0 if `keyname` exists in the dataset, but it's of the wrong type (i.e. not FSL) */ -int get_fsl(RedisModuleCtx *ctx, RedisModuleString *keyname, int mode, int create, fsl_t **fsl, int reply_on_failure) { - *fsl = NULL; - RedisModuleKey *key = RedisModule_OpenKey(ctx, keyname, mode); - - if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_EMPTY) { - /* Key exists */ - if (RedisModule_ModuleTypeGetType(key) != fsltype) { - /* Key is not FSL */ - RedisModule_CloseKey(key); - if (reply_on_failure) - RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); - RedisModuleCallReply *reply = RedisModule_Call(ctx, "INCR", "c", "fsl_wrong_type"); - RedisModule_FreeCallReply(reply); - return 0; - } - - *fsl = RedisModule_ModuleTypeGetValue(key); - if (*fsl && !(*fsl)->length && mode & REDISMODULE_WRITE) { - /* Key exists, but it's logically empty */ - if (create) { - create = 0; /* No need to create, key exists in its basic state */ - } else { - RedisModule_DeleteKey(key); - *fsl = NULL; - } - } else { - /* Key exists, and has elements in it - no need to create anything */ - create = 0; - } - } - - if (create) { - *fsl = fsl_type_create(); - RedisModule_ModuleTypeSetValue(key, fsltype, *fsl); - } - - RedisModule_CloseKey(key); - return 1; -} - -/* ========================== commands ======================= */ - -/* FSL.PUSH <key> <int> - Push an integer to the fixed-size list (to the right). - * It must be greater than the element in the head of the list. */ -int fsl_push(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 3) - return RedisModule_WrongArity(ctx); - - long long ele; - if (RedisModule_StringToLongLong(argv[2],&ele) != REDISMODULE_OK) - return RedisModule_ReplyWithError(ctx,"ERR invalid integer"); - - fsl_t *fsl; - if (!get_fsl(ctx, argv[1], REDISMODULE_WRITE, 1, &fsl, 1)) - return REDISMODULE_OK; - - if (fsl->length == LIST_SIZE) - return RedisModule_ReplyWithError(ctx,"ERR list is full"); - - if (fsl->length != 0 && fsl->list[fsl->length-1] >= ele) - return RedisModule_ReplyWithError(ctx,"ERR new element has to be greater than the head element"); - - fsl->list[fsl->length++] = ele; - RedisModule_SignalKeyAsReady(ctx, argv[1]); - - RedisModule_ReplicateVerbatim(ctx); - - return RedisModule_ReplyWithSimpleString(ctx, "OK"); -} - -typedef struct { - RedisModuleString *keyname; - long long ele; -} timer_data_t; - -static void timer_callback(RedisModuleCtx *ctx, void *data) -{ - timer_data_t *td = data; - - fsl_t *fsl; - if (!get_fsl(ctx, td->keyname, REDISMODULE_WRITE, 1, &fsl, 1)) - return; - - if (fsl->length == LIST_SIZE) - return; /* list is full */ - - if (fsl->length != 0 && fsl->list[fsl->length-1] >= td->ele) - return; /* new element has to be greater than the head element */ - - fsl->list[fsl->length++] = td->ele; - RedisModule_SignalKeyAsReady(ctx, td->keyname); - - RedisModule_Replicate(ctx, "FSL.PUSH", "sl", td->keyname, td->ele); - - RedisModule_FreeString(ctx, td->keyname); - RedisModule_Free(td); -} - -/* FSL.PUSHTIMER <key> <int> <period-in-ms> - Push the number 9000 to the fixed-size list (to the right). - * It must be greater than the element in the head of the list. */ -int fsl_pushtimer(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - if (argc != 4) - return RedisModule_WrongArity(ctx); - - long long ele; - if (RedisModule_StringToLongLong(argv[2],&ele) != REDISMODULE_OK) - return RedisModule_ReplyWithError(ctx,"ERR invalid integer"); - - long long period; - if (RedisModule_StringToLongLong(argv[3],&period) != REDISMODULE_OK) - return RedisModule_ReplyWithError(ctx,"ERR invalid period"); - - fsl_t *fsl; - if (!get_fsl(ctx, argv[1], REDISMODULE_WRITE, 1, &fsl, 1)) - return REDISMODULE_OK; - - if (fsl->length == LIST_SIZE) - return RedisModule_ReplyWithError(ctx,"ERR list is full"); - - timer_data_t *td = RedisModule_Alloc(sizeof(*td)); - td->keyname = argv[1]; - RedisModule_RetainString(ctx, td->keyname); - td->ele = ele; - - RedisModuleTimerID id = RedisModule_CreateTimer(ctx, period, timer_callback, td); - RedisModule_ReplyWithLongLong(ctx, id); - - return REDISMODULE_OK; -} - -int bpop_reply_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - RedisModuleString *keyname = RedisModule_GetBlockedClientReadyKey(ctx); - - fsl_t *fsl; - if (!get_fsl(ctx, keyname, REDISMODULE_WRITE, 0, &fsl, 0) || !fsl) - return REDISMODULE_ERR; - - RedisModule_Assert(fsl->length); - RedisModule_ReplyWithLongLong(ctx, fsl->list[--fsl->length]); - - /* I'm lazy so i'll replicate a potentially blocking command, it shouldn't block in this flow. */ - RedisModule_ReplicateVerbatim(ctx); - return REDISMODULE_OK; -} - -int bpop_timeout_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - return RedisModule_ReplyWithSimpleString(ctx, "Request timedout"); -} - -/* FSL.BPOP <key> <timeout> [NO_TO_CB]- Block clients until list has two or more elements. - * When that happens, unblock client and pop the last two elements (from the right). */ -int fsl_bpop(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc < 3) - return RedisModule_WrongArity(ctx); - - long long timeout; - if (RedisModule_StringToLongLong(argv[2],&timeout) != REDISMODULE_OK || timeout < 0) - return RedisModule_ReplyWithError(ctx,"ERR invalid timeout"); - - int to_cb = 1; - if (argc == 4) { - if (strcasecmp("NO_TO_CB", RedisModule_StringPtrLen(argv[3], NULL))) - return RedisModule_ReplyWithError(ctx,"ERR invalid argument"); - to_cb = 0; - } - - fsl_t *fsl; - if (!get_fsl(ctx, argv[1], REDISMODULE_WRITE, 0, &fsl, 1)) - return REDISMODULE_OK; - - if (!fsl) { - RedisModule_BlockClientOnKeys(ctx, bpop_reply_callback, to_cb ? bpop_timeout_callback : NULL, - NULL, timeout, &argv[1], 1, NULL); - } else { - RedisModule_Assert(fsl->length); - RedisModule_ReplyWithLongLong(ctx, fsl->list[--fsl->length]); - /* I'm lazy so i'll replicate a potentially blocking command, it shouldn't block in this flow. */ - RedisModule_ReplicateVerbatim(ctx); - } - - return REDISMODULE_OK; -} - -int bpopgt_reply_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - RedisModuleString *keyname = RedisModule_GetBlockedClientReadyKey(ctx); - long long *pgt = RedisModule_GetBlockedClientPrivateData(ctx); - - fsl_t *fsl; - if (!get_fsl(ctx, keyname, REDISMODULE_WRITE, 0, &fsl, 0) || !fsl) - return RedisModule_ReplyWithError(ctx,"UNBLOCKED key no longer exists"); - - if (fsl->list[fsl->length-1] <= *pgt) - return REDISMODULE_ERR; - - RedisModule_Assert(fsl->length); - RedisModule_ReplyWithLongLong(ctx, fsl->list[--fsl->length]); - /* I'm lazy so i'll replicate a potentially blocking command, it shouldn't block in this flow. */ - RedisModule_ReplicateVerbatim(ctx); - return REDISMODULE_OK; -} - -int bpopgt_timeout_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - return RedisModule_ReplyWithSimpleString(ctx, "Request timedout"); -} - -void bpopgt_free_privdata(RedisModuleCtx *ctx, void *privdata) { - REDISMODULE_NOT_USED(ctx); - RedisModule_Free(privdata); -} - -/* FSL.BPOPGT <key> <gt> <timeout> - Block clients until list has an element greater than <gt>. - * When that happens, unblock client and pop the last element (from the right). */ -int fsl_bpopgt(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 4) - return RedisModule_WrongArity(ctx); - - long long gt; - if (RedisModule_StringToLongLong(argv[2],>) != REDISMODULE_OK) - return RedisModule_ReplyWithError(ctx,"ERR invalid integer"); - - long long timeout; - if (RedisModule_StringToLongLong(argv[3],&timeout) != REDISMODULE_OK || timeout < 0) - return RedisModule_ReplyWithError(ctx,"ERR invalid timeout"); - - fsl_t *fsl; - if (!get_fsl(ctx, argv[1], REDISMODULE_WRITE, 0, &fsl, 1)) - return REDISMODULE_OK; - - if (!fsl) - return RedisModule_ReplyWithError(ctx,"ERR key must exist"); - - if (fsl->list[fsl->length-1] <= gt) { - /* We use malloc so the tests in blockedonkeys.tcl can check for memory leaks */ - long long *pgt = RedisModule_Alloc(sizeof(long long)); - *pgt = gt; - RedisModule_BlockClientOnKeysWithFlags( - ctx, bpopgt_reply_callback, bpopgt_timeout_callback, - bpopgt_free_privdata, timeout, &argv[1], 1, pgt, - REDISMODULE_BLOCK_UNBLOCK_DELETED); - } else { - RedisModule_Assert(fsl->length); - RedisModule_ReplyWithLongLong(ctx, fsl->list[--fsl->length]); - /* I'm lazy so i'll replicate a potentially blocking command, it shouldn't block in this flow. */ - RedisModule_ReplicateVerbatim(ctx); - } - - return REDISMODULE_OK; -} - -int bpoppush_reply_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - RedisModuleString *src_keyname = RedisModule_GetBlockedClientReadyKey(ctx); - RedisModuleString *dst_keyname = RedisModule_GetBlockedClientPrivateData(ctx); - - fsl_t *src; - if (!get_fsl(ctx, src_keyname, REDISMODULE_WRITE, 0, &src, 0) || !src) - return REDISMODULE_ERR; - - fsl_t *dst; - if (!get_fsl(ctx, dst_keyname, REDISMODULE_WRITE, 1, &dst, 0) || !dst) - return REDISMODULE_ERR; - - RedisModule_Assert(src->length); - long long ele = src->list[--src->length]; - dst->list[dst->length++] = ele; - RedisModule_SignalKeyAsReady(ctx, dst_keyname); - /* I'm lazy so i'll replicate a potentially blocking command, it shouldn't block in this flow. */ - RedisModule_ReplicateVerbatim(ctx); - return RedisModule_ReplyWithLongLong(ctx, ele); -} - -int bpoppush_timeout_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - return RedisModule_ReplyWithSimpleString(ctx, "Request timedout"); -} - -void bpoppush_free_privdata(RedisModuleCtx *ctx, void *privdata) { - RedisModule_FreeString(ctx, privdata); -} - -/* FSL.BPOPPUSH <src> <dst> <timeout> - Block clients until <src> has an element. - * When that happens, unblock client, pop the last element from <src> and push it to <dst> - * (from the right). */ -int fsl_bpoppush(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 4) - return RedisModule_WrongArity(ctx); - - long long timeout; - if (RedisModule_StringToLongLong(argv[3],&timeout) != REDISMODULE_OK || timeout < 0) - return RedisModule_ReplyWithError(ctx,"ERR invalid timeout"); - - fsl_t *src; - if (!get_fsl(ctx, argv[1], REDISMODULE_WRITE, 0, &src, 1)) - return REDISMODULE_OK; - - if (!src) { - /* Retain string for reply callback */ - RedisModule_RetainString(ctx, argv[2]); - /* Key is empty, we must block */ - RedisModule_BlockClientOnKeys(ctx, bpoppush_reply_callback, bpoppush_timeout_callback, - bpoppush_free_privdata, timeout, &argv[1], 1, argv[2]); - } else { - fsl_t *dst; - if (!get_fsl(ctx, argv[2], REDISMODULE_WRITE, 1, &dst, 1)) - return REDISMODULE_OK; - - RedisModule_Assert(src->length); - long long ele = src->list[--src->length]; - dst->list[dst->length++] = ele; - RedisModule_SignalKeyAsReady(ctx, argv[2]); - RedisModule_ReplyWithLongLong(ctx, ele); - /* I'm lazy so i'll replicate a potentially blocking command, it shouldn't block in this flow. */ - RedisModule_ReplicateVerbatim(ctx); - } - - return REDISMODULE_OK; -} - -/* FSL.GETALL <key> - Reply with an array containing all elements. */ -int fsl_getall(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) - return RedisModule_WrongArity(ctx); - - fsl_t *fsl; - if (!get_fsl(ctx, argv[1], REDISMODULE_READ, 0, &fsl, 1)) - return REDISMODULE_OK; - - if (!fsl) - return RedisModule_ReplyWithArray(ctx, 0); - - RedisModule_ReplyWithArray(ctx, fsl->length); - for (int i = 0; i < fsl->length; i++) - RedisModule_ReplyWithLongLong(ctx, fsl->list[i]); - return REDISMODULE_OK; -} - -/* Callback for blockonkeys_popall */ -int blockonkeys_popall_reply_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argc); - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE); - if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_LIST) { - RedisModuleString *elem; - long len = 0; - RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN); - while ((elem = RedisModule_ListPop(key, REDISMODULE_LIST_HEAD)) != NULL) { - len++; - RedisModule_ReplyWithString(ctx, elem); - RedisModule_FreeString(ctx, elem); - } - /* I'm lazy so i'll replicate a potentially blocking command, it shouldn't block in this flow. */ - RedisModule_ReplicateVerbatim(ctx); - RedisModule_ReplySetArrayLength(ctx, len); - } else { - RedisModule_ReplyWithError(ctx, "ERR Not a list"); - } - RedisModule_CloseKey(key); - return REDISMODULE_OK; -} - -int blockonkeys_popall_timeout_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - return RedisModule_ReplyWithError(ctx, "ERR Timeout"); -} - -/* BLOCKONKEYS.POPALL key - * - * Blocks on an empty key for up to 3 seconds. When unblocked by a list - * operation like LPUSH, all the elements are popped and returned. Fails with an - * error on timeout. */ -int blockonkeys_popall(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) - return RedisModule_WrongArity(ctx); - - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ); - if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_EMPTY) { - RedisModule_BlockClientOnKeys(ctx, blockonkeys_popall_reply_callback, - blockonkeys_popall_timeout_callback, - NULL, 3000, &argv[1], 1, NULL); - } else { - RedisModule_ReplyWithError(ctx, "ERR Key not empty"); - } - RedisModule_CloseKey(key); - return REDISMODULE_OK; -} - -/* BLOCKONKEYS.LPUSH key val [val ..] - * BLOCKONKEYS.LPUSH_UNBLOCK key val [val ..] - * - * A module equivalent of LPUSH. If the name LPUSH_UNBLOCK is used, - * RM_SignalKeyAsReady() is also called. */ -int blockonkeys_lpush(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc < 3) - return RedisModule_WrongArity(ctx); - - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE); - if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_EMPTY && - RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_LIST) { - RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); - } else { - for (int i = 2; i < argc; i++) { - if (RedisModule_ListPush(key, REDISMODULE_LIST_HEAD, - argv[i]) != REDISMODULE_OK) { - RedisModule_CloseKey(key); - return RedisModule_ReplyWithError(ctx, "ERR Push failed"); - } - } - } - RedisModule_CloseKey(key); - - /* signal key as ready if the command is lpush_unblock */ - size_t len; - const char *str = RedisModule_StringPtrLen(argv[0], &len); - if (!strncasecmp(str, "blockonkeys.lpush_unblock", len)) { - RedisModule_SignalKeyAsReady(ctx, argv[1]); - } - RedisModule_ReplicateVerbatim(ctx); - return RedisModule_ReplyWithSimpleString(ctx, "OK"); -} - -/* Callback for the BLOCKONKEYS.BLPOPN command */ -int blockonkeys_blpopn_reply_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argc); - long long n; - RedisModule_StringToLongLong(argv[2], &n); - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE); - int result; - if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_LIST && - RedisModule_ValueLength(key) >= (size_t)n) { - RedisModule_ReplyWithArray(ctx, n); - for (long i = 0; i < n; i++) { - RedisModuleString *elem = RedisModule_ListPop(key, REDISMODULE_LIST_HEAD); - RedisModule_ReplyWithString(ctx, elem); - RedisModule_FreeString(ctx, elem); - } - /* I'm lazy so i'll replicate a potentially blocking command, it shouldn't block in this flow. */ - RedisModule_ReplicateVerbatim(ctx); - result = REDISMODULE_OK; - } else if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_LIST || - RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_EMPTY) { - const char *module_cmd = RedisModule_StringPtrLen(argv[0], NULL); - if (!strcasecmp(module_cmd, "blockonkeys.blpopn_or_unblock")) - RedisModule_UnblockClient(RedisModule_GetBlockedClientHandle(ctx), NULL); - - /* continue blocking */ - result = REDISMODULE_ERR; - } else { - result = RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); - } - RedisModule_CloseKey(key); - return result; -} - -int blockonkeys_blpopn_timeout_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - return RedisModule_ReplyWithError(ctx, "ERR Timeout"); -} - -int blockonkeys_blpopn_abort_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - return RedisModule_ReplyWithSimpleString(ctx, "Action aborted"); -} - -/* BLOCKONKEYS.BLPOPN key N - * - * Blocks until key has N elements and then pops them or fails after 3 seconds. - */ -int blockonkeys_blpopn(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc < 3) return RedisModule_WrongArity(ctx); - - long long n, timeout = 3000LL; - if (RedisModule_StringToLongLong(argv[2], &n) != REDISMODULE_OK) { - return RedisModule_ReplyWithError(ctx, "ERR Invalid N"); - } - - if (argc > 3 ) { - if (RedisModule_StringToLongLong(argv[3], &timeout) != REDISMODULE_OK) { - return RedisModule_ReplyWithError(ctx, "ERR Invalid timeout value"); - } - } - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE); - int keytype = RedisModule_KeyType(key); - if (keytype != REDISMODULE_KEYTYPE_EMPTY && - keytype != REDISMODULE_KEYTYPE_LIST) { - RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); - } else if (keytype == REDISMODULE_KEYTYPE_LIST && - RedisModule_ValueLength(key) >= (size_t)n) { - RedisModule_ReplyWithArray(ctx, n); - for (long i = 0; i < n; i++) { - RedisModuleString *elem = RedisModule_ListPop(key, REDISMODULE_LIST_HEAD); - RedisModule_ReplyWithString(ctx, elem); - RedisModule_FreeString(ctx, elem); - } - /* I'm lazy so i'll replicate a potentially blocking command, it shouldn't block in this flow. */ - RedisModule_ReplicateVerbatim(ctx); - } else { - RedisModule_BlockClientOnKeys(ctx, blockonkeys_blpopn_reply_callback, - timeout ? blockonkeys_blpopn_timeout_callback : blockonkeys_blpopn_abort_callback, - NULL, timeout, &argv[1], 1, NULL); - } - RedisModule_CloseKey(key); - return REDISMODULE_OK; -} - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - if (RedisModule_Init(ctx, "blockonkeys", 1, REDISMODULE_APIVER_1)== REDISMODULE_ERR) - return REDISMODULE_ERR; - - RedisModuleTypeMethods tm = { - .version = REDISMODULE_TYPE_METHOD_VERSION, - .rdb_load = fsl_rdb_load, - .rdb_save = fsl_rdb_save, - .aof_rewrite = fsl_aofrw, - .mem_usage = NULL, - .free = fsl_free, - .digest = NULL, - }; - - fsltype = RedisModule_CreateDataType(ctx, "fsltype_t", 0, &tm); - if (fsltype == NULL) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"fsl.push",fsl_push,"write",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"fsl.pushtimer",fsl_pushtimer,"write",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"fsl.bpop",fsl_bpop,"write",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"fsl.bpopgt",fsl_bpopgt,"write",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"fsl.bpoppush",fsl_bpoppush,"write",1,2,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"fsl.getall",fsl_getall,"",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "blockonkeys.popall", blockonkeys_popall, - "write", 1, 1, 1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "blockonkeys.lpush", blockonkeys_lpush, - "write", 1, 1, 1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "blockonkeys.lpush_unblock", blockonkeys_lpush, - "write", 1, 1, 1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "blockonkeys.blpopn", blockonkeys_blpopn, - "write", 1, 1, 1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "blockonkeys.blpopn_or_unblock", blockonkeys_blpopn, - "write", 1, 1, 1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/cmdintrospection.c b/examples/redis-unstable/tests/modules/cmdintrospection.c deleted file mode 100644 index 48208ff..0000000 --- a/examples/redis-unstable/tests/modules/cmdintrospection.c +++ /dev/null @@ -1,226 +0,0 @@ -#include "redismodule.h" - -#define UNUSED(V) ((void) V) - -int cmd_xadd(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - UNUSED(argv); - UNUSED(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, "cmdintrospection", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"cmdintrospection.xadd",cmd_xadd,"write deny-oom random fast",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - RedisModuleCommand *xadd = RedisModule_GetCommand(ctx,"cmdintrospection.xadd"); - - RedisModuleCommandInfo info = { - .version = REDISMODULE_COMMAND_INFO_VERSION, - .arity = -5, - .summary = "Appends a new message to a stream. Creates the key if it doesn't exist.", - .since = "5.0.0", - .complexity = "O(1) when adding a new entry, O(N) when trimming where N being the number of entries evicted.", - .tips = "nondeterministic_output", - .history = (RedisModuleCommandHistoryEntry[]){ - /* NOTE: All versions specified should be the module's versions, not - * Redis'! We use Redis versions in this example for the purpose of - * testing (comparing the output with the output of the vanilla - * XADD). */ - {"6.2.0", "Added the `NOMKSTREAM` option, `MINID` trimming strategy and the `LIMIT` option."}, - {"7.0.0", "Added support for the `<ms>-*` explicit ID form."}, - {"8.2.0", "Added the `KEEPREF`, `DELREF` and `ACKED` options."}, - {0} - }, - .key_specs = (RedisModuleCommandKeySpec[]){ - { - .notes = "UPDATE instead of INSERT because of the optional trimming feature", - .flags = REDISMODULE_CMD_KEY_RW | REDISMODULE_CMD_KEY_UPDATE, - .begin_search_type = REDISMODULE_KSPEC_BS_INDEX, - .bs.index.pos = 1, - .find_keys_type = REDISMODULE_KSPEC_FK_RANGE, - .fk.range = {0,1,0} - }, - {0} - }, - .args = (RedisModuleCommandArg[]){ - { - .name = "key", - .type = REDISMODULE_ARG_TYPE_KEY, - .key_spec_index = 0 - }, - { - .name = "nomkstream", - .type = REDISMODULE_ARG_TYPE_PURE_TOKEN, - .token = "NOMKSTREAM", - .since = "6.2.0", - .flags = REDISMODULE_CMD_ARG_OPTIONAL - }, - { - .name = "condition", - .type = REDISMODULE_ARG_TYPE_ONEOF, - .flags = REDISMODULE_CMD_ARG_OPTIONAL, - .subargs = (RedisModuleCommandArg[]){ - { - .name = "keepref", - .type = REDISMODULE_ARG_TYPE_PURE_TOKEN, - .token = "KEEPREF" - }, - { - .name = "delref", - .type = REDISMODULE_ARG_TYPE_PURE_TOKEN, - .token = "DELREF" - }, - { - .name = "acked", - .type = REDISMODULE_ARG_TYPE_PURE_TOKEN, - .token = "ACKED" - }, - {0} - } - }, - { - .name = "idmp", - .type = REDISMODULE_ARG_TYPE_ONEOF, - .flags = REDISMODULE_CMD_ARG_OPTIONAL, - .subargs = (RedisModuleCommandArg[]){ - { - .name = "idmpauto-with-pid", - .type = REDISMODULE_ARG_TYPE_BLOCK, - .subargs = (RedisModuleCommandArg[]){ - { - .name = "idmpauto-token", - .type = REDISMODULE_ARG_TYPE_PURE_TOKEN, - .token = "IDMPAUTO" - }, - { - .name = "pid", - .type = REDISMODULE_ARG_TYPE_STRING, - }, - {0} - } - }, - { - .name = "idmp-with-pid-iid", - .type = REDISMODULE_ARG_TYPE_BLOCK, - .subargs = (RedisModuleCommandArg[]){ - { - .name = "idmp-token", - .type = REDISMODULE_ARG_TYPE_PURE_TOKEN, - .token = "IDMP" - }, - { - .name = "pid", - .type = REDISMODULE_ARG_TYPE_STRING, - }, - { - .name = "iid", - .type = REDISMODULE_ARG_TYPE_STRING, - }, - {0} - } - }, - {0} - } - }, - { - .name = "trim", - .type = REDISMODULE_ARG_TYPE_BLOCK, - .flags = REDISMODULE_CMD_ARG_OPTIONAL, - .subargs = (RedisModuleCommandArg[]){ - { - .name = "strategy", - .type = REDISMODULE_ARG_TYPE_ONEOF, - .subargs = (RedisModuleCommandArg[]){ - { - .name = "maxlen", - .type = REDISMODULE_ARG_TYPE_PURE_TOKEN, - .token = "MAXLEN", - }, - { - .name = "minid", - .type = REDISMODULE_ARG_TYPE_PURE_TOKEN, - .token = "MINID", - .since = "6.2.0", - }, - {0} - } - }, - { - .name = "operator", - .type = REDISMODULE_ARG_TYPE_ONEOF, - .flags = REDISMODULE_CMD_ARG_OPTIONAL, - .subargs = (RedisModuleCommandArg[]){ - { - .name = "equal", - .type = REDISMODULE_ARG_TYPE_PURE_TOKEN, - .token = "=" - }, - { - .name = "approximately", - .type = REDISMODULE_ARG_TYPE_PURE_TOKEN, - .token = "~" - }, - {0} - } - }, - { - .name = "threshold", - .type = REDISMODULE_ARG_TYPE_STRING, - .display_text = "threshold" /* Just for coverage, doesn't have a visible effect */ - }, - { - .name = "count", - .type = REDISMODULE_ARG_TYPE_INTEGER, - .token = "LIMIT", - .since = "6.2.0", - .flags = REDISMODULE_CMD_ARG_OPTIONAL - }, - {0} - } - }, - { - .name = "id-selector", - .type = REDISMODULE_ARG_TYPE_ONEOF, - .subargs = (RedisModuleCommandArg[]){ - { - .name = "auto-id", - .type = REDISMODULE_ARG_TYPE_PURE_TOKEN, - .token = "*" - }, - { - .name = "id", - .type = REDISMODULE_ARG_TYPE_STRING, - }, - {0} - } - }, - { - .name = "data", - .type = REDISMODULE_ARG_TYPE_BLOCK, - .flags = REDISMODULE_CMD_ARG_MULTIPLE, - .subargs = (RedisModuleCommandArg[]){ - { - .name = "field", - .type = REDISMODULE_ARG_TYPE_STRING, - }, - { - .name = "value", - .type = REDISMODULE_ARG_TYPE_STRING, - }, - {0} - } - }, - {0} - } - }; - if (RedisModule_SetCommandInfo(xadd, &info) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/commandfilter.c b/examples/redis-unstable/tests/modules/commandfilter.c deleted file mode 100644 index 333b263..0000000 --- a/examples/redis-unstable/tests/modules/commandfilter.c +++ /dev/null @@ -1,251 +0,0 @@ -#include "redismodule.h" - -#include <string.h> -#include <strings.h> - -static RedisModuleString *log_key_name; - -static const char log_command_name[] = "commandfilter.log"; -static const char ping_command_name[] = "commandfilter.ping"; -static const char retained_command_name[] = "commandfilter.retained"; -static const char unregister_command_name[] = "commandfilter.unregister"; -static const char unfiltered_clientid_name[] = "unfilter_clientid"; -static int in_log_command = 0; - -unsigned long long unfiltered_clientid = 0; - -static RedisModuleCommandFilter *filter, *filter1; -static RedisModuleString *retained; - -int CommandFilter_UnregisterCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - (void) argc; - (void) argv; - - RedisModule_ReplyWithLongLong(ctx, - RedisModule_UnregisterCommandFilter(ctx, filter)); - - return REDISMODULE_OK; -} - -int CommandFilter_PingCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - (void) argc; - (void) argv; - - RedisModuleCallReply *reply = RedisModule_Call(ctx, "ping", "c", "@log"); - if (reply) { - RedisModule_ReplyWithCallReply(ctx, reply); - RedisModule_FreeCallReply(reply); - } else { - RedisModule_ReplyWithSimpleString(ctx, "Unknown command or invalid arguments"); - } - - return REDISMODULE_OK; -} - -int CommandFilter_Retained(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - (void) argc; - (void) argv; - - if (retained) { - RedisModule_ReplyWithString(ctx, retained); - } else { - RedisModule_ReplyWithNull(ctx); - } - - return REDISMODULE_OK; -} - -int CommandFilter_LogCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - RedisModuleString *s = RedisModule_CreateString(ctx, "", 0); - - int i; - for (i = 1; i < argc; i++) { - size_t arglen; - const char *arg = RedisModule_StringPtrLen(argv[i], &arglen); - - if (i > 1) RedisModule_StringAppendBuffer(ctx, s, " ", 1); - RedisModule_StringAppendBuffer(ctx, s, arg, arglen); - } - - RedisModuleKey *log = RedisModule_OpenKey(ctx, log_key_name, REDISMODULE_WRITE|REDISMODULE_READ); - RedisModule_ListPush(log, REDISMODULE_LIST_HEAD, s); - RedisModule_CloseKey(log); - RedisModule_FreeString(ctx, s); - - in_log_command = 1; - - size_t cmdlen; - const char *cmdname = RedisModule_StringPtrLen(argv[1], &cmdlen); - RedisModuleCallReply *reply = RedisModule_Call(ctx, cmdname, "v", &argv[2], (size_t)argc - 2); - if (reply) { - RedisModule_ReplyWithCallReply(ctx, reply); - RedisModule_FreeCallReply(reply); - } else { - RedisModule_ReplyWithSimpleString(ctx, "Unknown command or invalid arguments"); - } - - in_log_command = 0; - - return REDISMODULE_OK; -} - -int CommandFilter_UnfilteredClientId(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - if (argc < 2) - return RedisModule_WrongArity(ctx); - - long long id; - if (RedisModule_StringToLongLong(argv[1], &id) != REDISMODULE_OK) { - RedisModule_ReplyWithError(ctx, "invalid client id"); - return REDISMODULE_OK; - } - if (id < 0) { - RedisModule_ReplyWithError(ctx, "invalid client id"); - return REDISMODULE_OK; - } - - unfiltered_clientid = id; - RedisModule_ReplyWithSimpleString(ctx, "OK"); - return REDISMODULE_OK; -} - -/* Filter to protect against Bug #11894 reappearing - * - * ensures that the filter is only run the first time through, and not on reprocessing - */ -void CommandFilter_BlmoveSwap(RedisModuleCommandFilterCtx *filter) -{ - if (RedisModule_CommandFilterArgsCount(filter) != 6) - return; - - RedisModuleString *arg = RedisModule_CommandFilterArgGet(filter, 0); - size_t arg_len; - const char *arg_str = RedisModule_StringPtrLen(arg, &arg_len); - - if (arg_len != 6 || strncmp(arg_str, "blmove", 6)) - return; - - /* - * Swapping directional args (right/left) from source and destination. - * need to hold here, can't push into the ArgReplace func, as it will cause other to freed -> use after free - */ - RedisModuleString *dir1 = RedisModule_HoldString(NULL, RedisModule_CommandFilterArgGet(filter, 3)); - RedisModuleString *dir2 = RedisModule_HoldString(NULL, RedisModule_CommandFilterArgGet(filter, 4)); - RedisModule_CommandFilterArgReplace(filter, 3, dir2); - RedisModule_CommandFilterArgReplace(filter, 4, dir1); -} - -void CommandFilter_CommandFilter(RedisModuleCommandFilterCtx *filter) -{ - unsigned long long id = RedisModule_CommandFilterGetClientId(filter); - if (id == unfiltered_clientid) return; - - if (in_log_command) return; /* don't process our own RM_Call() from CommandFilter_LogCommand() */ - - /* Fun manipulations: - * - Remove @delme - * - Replace @replaceme - * - Append @insertbefore or @insertafter - * - Prefix with Log command if @log encountered - */ - int log = 0; - int pos = 0; - while (pos < RedisModule_CommandFilterArgsCount(filter)) { - const RedisModuleString *arg = RedisModule_CommandFilterArgGet(filter, pos); - size_t arg_len; - const char *arg_str = RedisModule_StringPtrLen(arg, &arg_len); - - if (arg_len == 6 && !memcmp(arg_str, "@delme", 6)) { - RedisModule_CommandFilterArgDelete(filter, pos); - continue; - } - if (arg_len == 10 && !memcmp(arg_str, "@replaceme", 10)) { - RedisModule_CommandFilterArgReplace(filter, pos, - RedisModule_CreateString(NULL, "--replaced--", 12)); - } else if (arg_len == 13 && !memcmp(arg_str, "@insertbefore", 13)) { - RedisModule_CommandFilterArgInsert(filter, pos, - RedisModule_CreateString(NULL, "--inserted-before--", 19)); - pos++; - } else if (arg_len == 12 && !memcmp(arg_str, "@insertafter", 12)) { - RedisModule_CommandFilterArgInsert(filter, pos + 1, - RedisModule_CreateString(NULL, "--inserted-after--", 18)); - pos++; - } else if (arg_len == 7 && !memcmp(arg_str, "@retain", 7)) { - if (retained) RedisModule_FreeString(NULL, retained); - retained = RedisModule_CommandFilterArgGet(filter, pos + 1); - RedisModule_RetainString(NULL, retained); - pos++; - } else if (arg_len == 4 && !memcmp(arg_str, "@log", 4)) { - log = 1; - } - pos++; - } - - if (log) RedisModule_CommandFilterArgInsert(filter, 0, - RedisModule_CreateString(NULL, log_command_name, sizeof(log_command_name)-1)); -} - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (RedisModule_Init(ctx,"commandfilter",1,REDISMODULE_APIVER_1) - == REDISMODULE_ERR) return REDISMODULE_ERR; - - if (argc != 2 && argc != 3) { - RedisModule_Log(ctx, "warning", "Log key name not specified"); - return REDISMODULE_ERR; - } - - long long noself = 0; - log_key_name = RedisModule_CreateStringFromString(ctx, argv[0]); - RedisModule_StringToLongLong(argv[1], &noself); - retained = NULL; - - if (RedisModule_CreateCommand(ctx,log_command_name, - CommandFilter_LogCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,ping_command_name, - CommandFilter_PingCommand,"deny-oom",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,retained_command_name, - CommandFilter_Retained,"readonly",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,unregister_command_name, - CommandFilter_UnregisterCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, unfiltered_clientid_name, - CommandFilter_UnfilteredClientId, "admin", 1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if ((filter = RedisModule_RegisterCommandFilter(ctx, CommandFilter_CommandFilter, - noself ? REDISMODULE_CMDFILTER_NOSELF : 0)) - == NULL) return REDISMODULE_ERR; - - if ((filter1 = RedisModule_RegisterCommandFilter(ctx, CommandFilter_BlmoveSwap, 0)) == NULL) - return REDISMODULE_ERR; - - if (argc == 3) { - const char *ptr = RedisModule_StringPtrLen(argv[2], NULL); - if (!strcasecmp(ptr, "noload")) { - /* This is a hint that we return ERR at the last moment of OnLoad. */ - RedisModule_FreeString(ctx, log_key_name); - if (retained) RedisModule_FreeString(NULL, retained); - return REDISMODULE_ERR; - } - } - - return REDISMODULE_OK; -} - -int RedisModule_OnUnload(RedisModuleCtx *ctx) { - RedisModule_FreeString(ctx, log_key_name); - if (retained) RedisModule_FreeString(NULL, retained); - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/configaccess.c b/examples/redis-unstable/tests/modules/configaccess.c deleted file mode 100644 index 7bc8a48..0000000 --- a/examples/redis-unstable/tests/modules/configaccess.c +++ /dev/null @@ -1,353 +0,0 @@ -#include "redismodule.h" -#include <assert.h> -#include <string.h> - -/* See moduleconfigs.c for registering module configs. We need to register some - * module configs with our module in order to test the interaction between - * module configs and the RM_Get/Set*Config APIs. */ -int configaccess_bool; - -int getBoolConfigCommand(const char *name, void *privdata) { - REDISMODULE_NOT_USED(name); - return (*(int *)privdata); -} - -int setBoolConfigCommand(const char *name, int new, void *privdata, RedisModuleString **err) { - REDISMODULE_NOT_USED(name); - REDISMODULE_NOT_USED(err); - *(int *)privdata = new; - return REDISMODULE_OK; -} - -/* Test command for RM_GetConfigType */ -int TestGetConfigType_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) { - return RedisModule_WrongArity(ctx); - } - - size_t len; - const char *config_name = RedisModule_StringPtrLen(argv[1], &len); - - RedisModuleConfigType type; - int res = RedisModule_ConfigGetType(config_name, &type); - if (res == REDISMODULE_ERR) { - RedisModule_ReplyWithError(ctx, "ERR Config does not exist"); - return REDISMODULE_ERR; - } - - const char *type_str; - switch (type) { - case REDISMODULE_CONFIG_TYPE_BOOL: - type_str = "bool"; - break; - case REDISMODULE_CONFIG_TYPE_NUMERIC: - type_str = "numeric"; - break; - case REDISMODULE_CONFIG_TYPE_STRING: - type_str = "string"; - break; - case REDISMODULE_CONFIG_TYPE_ENUM: - type_str = "enum"; - break; - default: - assert(0); - break; - } - - RedisModule_ReplyWithSimpleString(ctx, type_str); - return REDISMODULE_OK; -} - -/* Test command for config iteration */ -int TestConfigIteration_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - - if (argc > 2) { - return RedisModule_WrongArity(ctx); - } - - const char *pattern = NULL; - if (argc == 2) { - pattern = RedisModule_StringPtrLen(argv[1], NULL); - } - - RedisModuleConfigIterator *iter = RedisModule_ConfigIteratorCreate(ctx, pattern); - if (!iter) { - RedisModule_ReplyWithError(ctx, "ERR Failed to get config iterator"); - return REDISMODULE_ERR; - } - - /* Start array reply for the configs */ - RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN); - - /* Iterate through the dictionary */ - const char *config_name = NULL; - long count = 0; - while ((config_name = RedisModule_ConfigIteratorNext(iter)) != NULL) { - RedisModuleString *value = NULL; - RedisModule_ConfigGet(ctx, config_name, &value); - - RedisModule_ReplyWithArray(ctx, 2); - RedisModule_ReplyWithStringBuffer(ctx, config_name, strlen(config_name)); - RedisModule_ReplyWithString(ctx, value); - - RedisModule_FreeString(ctx, value); - ++count; - } - RedisModule_ReplySetArrayLength(ctx, count); - - /* Free the iterator */ - RedisModule_ConfigIteratorRelease(ctx, iter); - - return REDISMODULE_OK; -} - -/* Test command for RM_GetBoolConfig */ -int TestGetBoolConfig_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) { - return RedisModule_WrongArity(ctx); - } - - size_t len; - const char *config_name = RedisModule_StringPtrLen(argv[1], &len); - - int value; - if (RedisModule_ConfigGetBool(ctx, config_name, &value) == REDISMODULE_ERR) { - RedisModule_ReplyWithError(ctx, "ERR Failed to get bool config"); - return REDISMODULE_ERR; - } - - RedisModule_ReplyWithLongLong(ctx, value); - return REDISMODULE_OK; -} - -/* Test command for RM_GetNumericConfig */ -int TestGetNumericConfig_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) { - return RedisModule_WrongArity(ctx); - } - - size_t len; - const char *config_name = RedisModule_StringPtrLen(argv[1], &len); - - long long value; - if (RedisModule_ConfigGetNumeric(ctx, config_name, &value) == REDISMODULE_ERR) { - RedisModule_ReplyWithError(ctx, "ERR Failed to get numeric config"); - return REDISMODULE_ERR; - } - - RedisModule_ReplyWithLongLong(ctx, value); - return REDISMODULE_OK; -} - -/* Test command for RM_GetConfig */ -int TestGetConfig_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) { - return RedisModule_WrongArity(ctx); - } - - size_t len; - const char *config_name = RedisModule_StringPtrLen(argv[1], &len); - - RedisModuleString *value; - if (RedisModule_ConfigGet(ctx, config_name, &value) == REDISMODULE_ERR) { - RedisModule_ReplyWithError(ctx, "ERR Failed to get string config"); - return REDISMODULE_ERR; - } - - RedisModule_ReplyWithString(ctx, value); - RedisModule_FreeString(ctx,value); - return REDISMODULE_OK; -} - -/* Test command for RM_GetEnumConfig */ -int TestGetEnumConfig_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) { - return RedisModule_WrongArity(ctx); - } - - size_t len; - const char *config_name = RedisModule_StringPtrLen(argv[1], &len); - - RedisModuleString *value; - if (RedisModule_ConfigGetEnum(ctx, config_name, &value) == REDISMODULE_ERR) { - RedisModule_ReplyWithError(ctx, "ERR Failed to get enum name config"); - return REDISMODULE_ERR; - } - - RedisModule_ReplyWithString(ctx, value); - RedisModule_Free(value); - return REDISMODULE_OK; -} - -/* Test command for RM_SetBoolConfig */ -int TestSetBoolConfig_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 3) { - return RedisModule_WrongArity(ctx); - } - - size_t name_len, value_len; - const char *config_name = RedisModule_StringPtrLen(argv[1], &name_len); - const char *config_value = RedisModule_StringPtrLen(argv[2], &value_len); - - int bool_value; - if (!strcasecmp(config_value, "yes")) { - bool_value = 1; - } else if (!strcasecmp(config_value, "no")) { - bool_value = 0; - } else { - bool_value = -1; - } - - RedisModuleString *error = NULL; - int result = RedisModule_ConfigSetBool(ctx, config_name, bool_value, &error); - if (result == REDISMODULE_ERR) { - RedisModule_ReplyWithErrorFormat(ctx, "ERR Failed to set bool config %s: %s", config_name, RedisModule_StringPtrLen(error, NULL)); - RedisModule_FreeString(ctx, error); - return REDISMODULE_ERR; - } - - RedisModule_ReplyWithSimpleString(ctx, "OK"); - return REDISMODULE_OK; -} - -/* Test command for RM_SetNumericConfig */ -int TestSetNumericConfig_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 3) { - return RedisModule_WrongArity(ctx); - } - - size_t name_len; - const char *config_name = RedisModule_StringPtrLen(argv[1], &name_len); - - long long value; - const char *value_str= RedisModule_StringPtrLen(argv[2], NULL); - if (value_str[0] == '-') { - if (RedisModule_StringToLongLong(argv[2], &value) != REDISMODULE_OK) { - RedisModule_ReplyWithError(ctx, "ERR Invalid numeric value"); - return REDISMODULE_ERR; - } - } else { - if (RedisModule_StringToULongLong(argv[2], (unsigned long long*)&value) != REDISMODULE_OK) { - RedisModule_ReplyWithError(ctx, "ERR Invalid numeric value"); - return REDISMODULE_ERR; - } - } - - RedisModuleString *error = NULL; - int result = RedisModule_ConfigSetNumeric(ctx, config_name, value, &error); - if (result == REDISMODULE_OK) { - RedisModule_ReplyWithSimpleString(ctx, "OK"); - } else { - RedisModule_ReplyWithErrorFormat(ctx, "ERR Failed to set numeric config %s: %s", config_name, RedisModule_StringPtrLen(error, NULL)); - RedisModule_FreeString(ctx, error); - return REDISMODULE_ERR; - } - - return REDISMODULE_OK; -} - -/* Test command for RM_SetConfig */ -int TestSetConfig_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 3) { - return RedisModule_WrongArity(ctx); - } - - size_t name_len; - const char *config_name = RedisModule_StringPtrLen(argv[1], &name_len); - - RedisModuleString *error = NULL; - int result = RedisModule_ConfigSet(ctx, config_name, argv[2], &error); - if (result == REDISMODULE_OK) { - RedisModule_ReplyWithSimpleString(ctx, "OK"); - } else { - RedisModule_ReplyWithErrorFormat(ctx, "ERR Failed to set string config %s: %s", config_name, RedisModule_StringPtrLen(error, NULL)); - RedisModule_FreeString(ctx, error); - return REDISMODULE_ERR; - } - - return REDISMODULE_OK; -} - -/* Test command for RM_SetEnumConfig with name */ -int TestSetEnumConfig_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc < 3) { - return RedisModule_WrongArity(ctx); - } - - const char *config_name = RedisModule_StringPtrLen(argv[1], NULL); - - RedisModuleString *error = NULL; - int result = RedisModule_ConfigSetEnum(ctx, config_name, argv[2], &error); - - if (result == REDISMODULE_OK) { - RedisModule_ReplyWithSimpleString(ctx, "OK"); - } else { - RedisModule_ReplyWithErrorFormat(ctx, "ERR Failed to set enum config %s: %s", config_name, RedisModule_StringPtrLen(error, NULL)); - RedisModule_FreeString(ctx, error); - 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, "configaccess", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "configaccess.getconfigs", - TestConfigIteration_RedisCommand, "readonly", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "configaccess.getbool", - TestGetBoolConfig_RedisCommand, "readonly", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "configaccess.getnumeric", - TestGetNumericConfig_RedisCommand, "readonly", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "configaccess.get", - TestGetConfig_RedisCommand, "readonly", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "configaccess.getenum", - TestGetEnumConfig_RedisCommand, "readonly", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "configaccess.setbool", - TestSetBoolConfig_RedisCommand, "write", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "configaccess.setnumeric", - TestSetNumericConfig_RedisCommand, "write", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "configaccess.set", - TestSetConfig_RedisCommand, "write", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "configaccess.setenum", - TestSetEnumConfig_RedisCommand, "write", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "configaccess.getconfigtype", TestGetConfigType_RedisCommand, "readonly", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_RegisterBoolConfig(ctx, "bool", 1, REDISMODULE_CONFIG_DEFAULT, - getBoolConfigCommand, setBoolConfigCommand, NULL, &configaccess_bool) == REDISMODULE_ERR) { - RedisModule_Log(ctx, "warning", "Failed to register configaccess_bool"); - return REDISMODULE_ERR; - } - - RedisModule_Log(ctx, "debug", "Loading configaccess module configuration"); - if (RedisModule_LoadConfigs(ctx) == REDISMODULE_ERR) { - RedisModule_Log(ctx, "warning", "Failed to load configaccess module configuration"); - return REDISMODULE_ERR; - } - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/crash.c b/examples/redis-unstable/tests/modules/crash.c deleted file mode 100644 index c5063d0..0000000 --- a/examples/redis-unstable/tests/modules/crash.c +++ /dev/null @@ -1,311 +0,0 @@ -#include "redismodule.h" - -#include <strings.h> -#include <sys/mman.h> - -#define UNUSED(V) ((void) V) - -void assertCrash(RedisModuleInfoCtx *ctx, int for_crash_report) { - UNUSED(ctx); - UNUSED(for_crash_report); - RedisModule_Assert(0); -} - -void segfaultCrash(RedisModuleInfoCtx *ctx, int for_crash_report) { - UNUSED(ctx); - UNUSED(for_crash_report); - /* Compiler gives warnings about writing to a random address - * e.g "*((char*)-1) = 'x';". As a workaround, we map a read-only area - * and try to write there to trigger segmentation fault. */ - char *p = mmap(NULL, 4096, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - *p = 'x'; -} - -int cmd_crash(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - UNUSED(ctx); - UNUSED(argv); - UNUSED(argc); - - RedisModule_Assert(0); - return REDISMODULE_OK; -} - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - if (RedisModule_Init(ctx,"modulecrash",1,REDISMODULE_APIVER_1) - == REDISMODULE_ERR) return REDISMODULE_ERR; - - if (argc >= 1) { - if (!strcasecmp(RedisModule_StringPtrLen(argv[0], NULL), "segfault")) { - if (RedisModule_RegisterInfoFunc(ctx, segfaultCrash) == REDISMODULE_ERR) return REDISMODULE_ERR; - } else if (!strcasecmp(RedisModule_StringPtrLen(argv[0], NULL),"assert")) { - if (RedisModule_RegisterInfoFunc(ctx, assertCrash) == REDISMODULE_ERR) return REDISMODULE_ERR; - } - } - - /* Create modulecrash.xadd command which is similar to xadd command. - * It will crash in the command handler to verify we print command tokens - * when hide-user-data-from-log config is enabled */ - RedisModuleCommandInfo info = { - .version = REDISMODULE_COMMAND_INFO_VERSION, - .arity = -5, - .key_specs = (RedisModuleCommandKeySpec[]){ - { - .notes = "UPDATE instead of INSERT because of the optional trimming feature", - .flags = REDISMODULE_CMD_KEY_RW | REDISMODULE_CMD_KEY_UPDATE, - .begin_search_type = REDISMODULE_KSPEC_BS_INDEX, - .bs.index.pos = 1, - .find_keys_type = REDISMODULE_KSPEC_FK_RANGE, - .fk.range = {0,1,0} - }, - {0} - }, - .args = (RedisModuleCommandArg[]){ - { - .name = "key", - .type = REDISMODULE_ARG_TYPE_KEY, - .key_spec_index = 0 - }, - { - .name = "nomkstream", - .type = REDISMODULE_ARG_TYPE_PURE_TOKEN, - .token = "NOMKSTREAM", - .since = "6.2.0", - .flags = REDISMODULE_CMD_ARG_OPTIONAL - }, - { - .name = "trim", - .type = REDISMODULE_ARG_TYPE_BLOCK, - .flags = REDISMODULE_CMD_ARG_OPTIONAL, - .subargs = (RedisModuleCommandArg[]){ - { - .name = "strategy", - .type = REDISMODULE_ARG_TYPE_ONEOF, - .subargs = (RedisModuleCommandArg[]){ - { - .name = "maxlen", - .type = REDISMODULE_ARG_TYPE_PURE_TOKEN, - .token = "MAXLEN", - }, - { - .name = "minid", - .type = REDISMODULE_ARG_TYPE_PURE_TOKEN, - .token = "MINID", - .since = "6.2.0", - }, - {0} - } - }, - { - .name = "operator", - .type = REDISMODULE_ARG_TYPE_ONEOF, - .flags = REDISMODULE_CMD_ARG_OPTIONAL, - .subargs = (RedisModuleCommandArg[]){ - { - .name = "equal", - .type = REDISMODULE_ARG_TYPE_PURE_TOKEN, - .token = "=" - }, - { - .name = "approximately", - .type = REDISMODULE_ARG_TYPE_PURE_TOKEN, - .token = "~" - }, - {0} - } - }, - { - .name = "threshold", - .type = REDISMODULE_ARG_TYPE_STRING, - .display_text = "threshold" /* Just for coverage, doesn't have a visible effect */ - }, - { - .name = "count", - .type = REDISMODULE_ARG_TYPE_INTEGER, - .token = "LIMIT", - .since = "6.2.0", - .flags = REDISMODULE_CMD_ARG_OPTIONAL - }, - {0} - } - }, - { - .name = "id-selector", - .type = REDISMODULE_ARG_TYPE_ONEOF, - .subargs = (RedisModuleCommandArg[]){ - { - .name = "auto-id", - .type = REDISMODULE_ARG_TYPE_PURE_TOKEN, - .token = "*" - }, - { - .name = "id", - .type = REDISMODULE_ARG_TYPE_STRING, - }, - {0} - } - }, - { - .name = "data", - .type = REDISMODULE_ARG_TYPE_BLOCK, - .flags = REDISMODULE_CMD_ARG_MULTIPLE, - .subargs = (RedisModuleCommandArg[]){ - { - .name = "field", - .type = REDISMODULE_ARG_TYPE_STRING, - }, - { - .name = "value", - .type = REDISMODULE_ARG_TYPE_STRING, - }, - {0} - } - }, - {0} - } - }; - - RedisModuleCommand *cmd; - - if (RedisModule_CreateCommand(ctx,"modulecrash.xadd", cmd_crash,"write deny-oom random fast",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - cmd = RedisModule_GetCommand(ctx,"modulecrash.xadd"); - if (RedisModule_SetCommandInfo(cmd, &info) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - /* Create a subcommand: modulecrash.parent sub - * It will crash in the command handler to verify we print subcommand name - * when hide-user-data-from-log config is enabled */ - RedisModuleCommandInfo subcommand_info = { - .version = REDISMODULE_COMMAND_INFO_VERSION, - .arity = -5, - .key_specs = (RedisModuleCommandKeySpec[]){ - { - .flags = REDISMODULE_CMD_KEY_RW | REDISMODULE_CMD_KEY_UPDATE, - .begin_search_type = REDISMODULE_KSPEC_BS_INDEX, - .bs.index.pos = 1, - .find_keys_type = REDISMODULE_KSPEC_FK_RANGE, - .fk.range = {0,1,0} - }, - {0} - }, - .args = (RedisModuleCommandArg[]){ - { - .name = "key", - .type = REDISMODULE_ARG_TYPE_KEY, - .key_spec_index = 0 - }, - { - .name = "token", - .type = REDISMODULE_ARG_TYPE_PURE_TOKEN, - .token = "TOKEN", - .flags = REDISMODULE_CMD_ARG_OPTIONAL - }, - { - .name = "data", - .type = REDISMODULE_ARG_TYPE_BLOCK, - .subargs = (RedisModuleCommandArg[]){ - { - .name = "field", - .type = REDISMODULE_ARG_TYPE_STRING, - }, - { - .name = "value", - .type = REDISMODULE_ARG_TYPE_STRING, - }, - {0} - } - }, - {0} - } - }; - - if (RedisModule_CreateCommand(ctx,"modulecrash.parent",NULL,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - RedisModuleCommand *parent = RedisModule_GetCommand(ctx,"modulecrash.parent"); - - if (RedisModule_CreateSubcommand(parent,"subcmd",cmd_crash,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - cmd = RedisModule_GetCommand(ctx,"modulecrash.parent|subcmd"); - if (RedisModule_SetCommandInfo(cmd, &subcommand_info) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - /* Create modulecrash.zunion command which is similar to zunion command. - * It will crash in the command handler to verify we print command tokens - * when hide-user-data-from-log config is enabled */ - RedisModuleCommandInfo zunioninfo = { - .version = REDISMODULE_COMMAND_INFO_VERSION, - .arity = -5, - .key_specs = (RedisModuleCommandKeySpec[]){ - { - .flags = REDISMODULE_CMD_KEY_RO, - .begin_search_type = REDISMODULE_KSPEC_BS_INDEX, - .bs.index.pos = 1, - .find_keys_type = REDISMODULE_KSPEC_FK_KEYNUM, - .fk.keynum = {0,1,1} - }, - {0} - }, - .args = (RedisModuleCommandArg[]){ - { - .name = "numkeys", - .type = REDISMODULE_ARG_TYPE_INTEGER, - }, - { - .name = "key", - .type = REDISMODULE_ARG_TYPE_KEY, - .key_spec_index = 0, - .flags = REDISMODULE_CMD_ARG_MULTIPLE - }, - { - .name = "weights", - .type = REDISMODULE_ARG_TYPE_INTEGER, - .token = "WEIGHTS", - .flags = REDISMODULE_CMD_ARG_OPTIONAL | REDISMODULE_CMD_ARG_MULTIPLE - }, - { - .name = "aggregate", - .type = REDISMODULE_ARG_TYPE_ONEOF, - .token = "AGGREGATE", - .flags = REDISMODULE_CMD_ARG_OPTIONAL, - .subargs = (RedisModuleCommandArg[]){ - { - .name = "sum", - .type = REDISMODULE_ARG_TYPE_PURE_TOKEN, - .token = "sum" - }, - { - .name = "min", - .type = REDISMODULE_ARG_TYPE_PURE_TOKEN, - .token = "min" - }, - { - .name = "max", - .type = REDISMODULE_ARG_TYPE_PURE_TOKEN, - .token = "max" - }, - {0} - } - }, - { - .name = "withscores", - .type = REDISMODULE_ARG_TYPE_PURE_TOKEN, - .token = "WITHSCORES", - .flags = REDISMODULE_CMD_ARG_OPTIONAL - }, - {0} - } - }; - - if (RedisModule_CreateCommand(ctx,"modulecrash.zunion", cmd_crash,"readonly",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - cmd = RedisModule_GetCommand(ctx,"modulecrash.zunion"); - if (RedisModule_SetCommandInfo(cmd, &zunioninfo) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/datatype.c b/examples/redis-unstable/tests/modules/datatype.c deleted file mode 100644 index 05cf233..0000000 --- a/examples/redis-unstable/tests/modules/datatype.c +++ /dev/null @@ -1,323 +0,0 @@ -/* This module current tests a small subset but should be extended in the future - * for general ModuleDataType coverage. - */ - -/* define macros for having usleep */ -#define _BSD_SOURCE -#define _DEFAULT_SOURCE -#include <unistd.h> - -#include "redismodule.h" - -static RedisModuleType *datatype = NULL; -static int load_encver = 0; - -/* used to test processing events during slow loading */ -static volatile int slow_loading = 0; -static volatile int is_in_slow_loading = 0; - -#define DATATYPE_ENC_VER 1 - -typedef struct { - long long intval; - RedisModuleString *strval; -} DataType; - -static void *datatype_load(RedisModuleIO *io, int encver) { - load_encver = encver; - int intval = RedisModule_LoadSigned(io); - if (RedisModule_IsIOError(io)) return NULL; - - RedisModuleString *strval = RedisModule_LoadString(io); - if (RedisModule_IsIOError(io)) return NULL; - - DataType *dt = (DataType *) RedisModule_Alloc(sizeof(DataType)); - dt->intval = intval; - dt->strval = strval; - - if (slow_loading) { - RedisModuleCtx *ctx = RedisModule_GetContextFromIO(io); - is_in_slow_loading = 1; - while (slow_loading) { - RedisModule_Yield(ctx, REDISMODULE_YIELD_FLAG_CLIENTS, "Slow module operation"); - usleep(1000); - } - is_in_slow_loading = 0; - } - - return dt; -} - -static void datatype_save(RedisModuleIO *io, void *value) { - DataType *dt = (DataType *) value; - RedisModule_SaveSigned(io, dt->intval); - RedisModule_SaveString(io, dt->strval); -} - -static void datatype_free(void *value) { - if (value) { - DataType *dt = (DataType *) value; - - if (dt->strval) RedisModule_FreeString(NULL, dt->strval); - RedisModule_Free(dt); - } -} - -static void *datatype_copy(RedisModuleString *fromkey, RedisModuleString *tokey, const void *value) { - const DataType *old = value; - - /* Answers to ultimate questions cannot be copied! */ - if (old->intval == 42) - return NULL; - - DataType *new = (DataType *) RedisModule_Alloc(sizeof(DataType)); - - new->intval = old->intval; - new->strval = RedisModule_CreateStringFromString(NULL, old->strval); - - /* Breaking the rules here! We return a copy that also includes traces - * of fromkey/tokey to confirm we get what we expect. - */ - size_t len; - const char *str = RedisModule_StringPtrLen(fromkey, &len); - RedisModule_StringAppendBuffer(NULL, new->strval, "/", 1); - RedisModule_StringAppendBuffer(NULL, new->strval, str, len); - RedisModule_StringAppendBuffer(NULL, new->strval, "/", 1); - str = RedisModule_StringPtrLen(tokey, &len); - RedisModule_StringAppendBuffer(NULL, new->strval, str, len); - - return new; -} - -static int datatype_set(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 4) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - long long intval; - - if (RedisModule_StringToLongLong(argv[2], &intval) != REDISMODULE_OK) { - RedisModule_ReplyWithError(ctx, "Invalid integer value"); - return REDISMODULE_OK; - } - - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE); - DataType *dt = RedisModule_Calloc(sizeof(DataType), 1); - dt->intval = intval; - dt->strval = argv[3]; - RedisModule_RetainString(ctx, dt->strval); - - RedisModule_ModuleTypeSetValue(key, datatype, dt); - RedisModule_CloseKey(key); - RedisModule_ReplyWithSimpleString(ctx, "OK"); - - return REDISMODULE_OK; -} - -static int datatype_restore(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 4) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - long long encver; - if (RedisModule_StringToLongLong(argv[3], &encver) != REDISMODULE_OK) { - RedisModule_ReplyWithError(ctx, "Invalid integer value"); - return REDISMODULE_OK; - } - - DataType *dt = RedisModule_LoadDataTypeFromStringEncver(argv[2], datatype, encver); - if (!dt) { - RedisModule_ReplyWithError(ctx, "Invalid data"); - return REDISMODULE_OK; - } - - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE); - RedisModule_ModuleTypeSetValue(key, datatype, dt); - RedisModule_CloseKey(key); - RedisModule_ReplyWithLongLong(ctx, load_encver); - - return REDISMODULE_OK; -} - -static int datatype_get(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ); - DataType *dt = RedisModule_ModuleTypeGetValue(key); - RedisModule_CloseKey(key); - - if (!dt) { - RedisModule_ReplyWithNullArray(ctx); - } else { - RedisModule_ReplyWithArray(ctx, 2); - RedisModule_ReplyWithLongLong(ctx, dt->intval); - RedisModule_ReplyWithString(ctx, dt->strval); - } - return REDISMODULE_OK; -} - -static int datatype_dump(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ); - DataType *dt = RedisModule_ModuleTypeGetValue(key); - RedisModule_CloseKey(key); - - RedisModuleString *reply = RedisModule_SaveDataTypeToString(ctx, dt, datatype); - if (!reply) { - RedisModule_ReplyWithError(ctx, "Failed to save"); - return REDISMODULE_OK; - } - - RedisModule_ReplyWithString(ctx, reply); - RedisModule_FreeString(ctx, reply); - return REDISMODULE_OK; -} - -static int datatype_swap(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 3) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - RedisModuleKey *a = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE); - RedisModuleKey *b = RedisModule_OpenKey(ctx, argv[2], REDISMODULE_WRITE); - void *val = RedisModule_ModuleTypeGetValue(a); - - int error = (RedisModule_ModuleTypeReplaceValue(b, datatype, val, &val) == REDISMODULE_ERR || - RedisModule_ModuleTypeReplaceValue(a, datatype, val, NULL) == REDISMODULE_ERR); - if (!error) - RedisModule_ReplyWithSimpleString(ctx, "OK"); - else - RedisModule_ReplyWithError(ctx, "ERR failed"); - - RedisModule_CloseKey(a); - RedisModule_CloseKey(b); - - return REDISMODULE_OK; -} - -/* used to enable or disable slow loading */ -static int datatype_slow_loading(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - long long ll; - if (RedisModule_StringToLongLong(argv[1], &ll) != REDISMODULE_OK) { - RedisModule_ReplyWithError(ctx, "Invalid integer value"); - return REDISMODULE_OK; - } - slow_loading = ll; - RedisModule_ReplyWithSimpleString(ctx, "OK"); - return REDISMODULE_OK; -} - -/* used to test if we reached the slow loading code */ -static int datatype_is_in_slow_loading(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - if (argc != 1) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - RedisModule_ReplyWithLongLong(ctx, is_in_slow_loading); - return REDISMODULE_OK; -} - -int createDataTypeBlockCheck(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - static RedisModuleType *datatype_outside_onload = NULL; - - RedisModuleTypeMethods datatype_methods = { - .version = REDISMODULE_TYPE_METHOD_VERSION, - .rdb_load = datatype_load, - .rdb_save = datatype_save, - .free = datatype_free, - .copy = datatype_copy - }; - - datatype_outside_onload = RedisModule_CreateDataType(ctx, "test_dt_outside_onload", 1, &datatype_methods); - - /* This validates that it's not possible to create datatype outside OnLoad, - * thus returns an error if it succeeds. */ - if (datatype_outside_onload == NULL) { - RedisModule_ReplyWithSimpleString(ctx, "OK"); - } else { - RedisModule_ReplyWithError(ctx, "UNEXPECTEDOK"); - } - return REDISMODULE_OK; -} - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - if (RedisModule_Init(ctx,"datatype",DATATYPE_ENC_VER,REDISMODULE_APIVER_1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - /* Creates a command which creates a datatype outside OnLoad() function. */ - if (RedisModule_CreateCommand(ctx,"block.create.datatype.outside.onload", createDataTypeBlockCheck, "write", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - RedisModule_SetModuleOptions(ctx, REDISMODULE_OPTIONS_HANDLE_IO_ERRORS); - - RedisModuleTypeMethods datatype_methods = { - .version = REDISMODULE_TYPE_METHOD_VERSION, - .rdb_load = datatype_load, - .rdb_save = datatype_save, - .free = datatype_free, - .copy = datatype_copy - }; - - datatype = RedisModule_CreateDataType(ctx, "test___dt", 1, &datatype_methods); - if (datatype == NULL) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"datatype.set", datatype_set, - "write deny-oom", 1, 1, 1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"datatype.get", datatype_get,"",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"datatype.restore", datatype_restore, - "write deny-oom", 1, 1, 1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"datatype.dump", datatype_dump,"",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "datatype.swap", datatype_swap, - "write", 1, 1, 1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "datatype.slow_loading", datatype_slow_loading, - "allow-loading", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "datatype.is_in_slow_loading", datatype_is_in_slow_loading, - "allow-loading", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - return REDISMODULE_OK; -} - -int RedisModule_OnUnload(RedisModuleCtx *ctx) { - REDISMODULE_NOT_USED(ctx); - if (datatype) { - RedisModule_Free(datatype); - datatype = NULL; - } - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/datatype2.c b/examples/redis-unstable/tests/modules/datatype2.c deleted file mode 100644 index bc0dc3d..0000000 --- a/examples/redis-unstable/tests/modules/datatype2.c +++ /dev/null @@ -1,739 +0,0 @@ -/* This module is used to test a use case of a module that stores information - * about keys in global memory, and relies on the enhanced data type callbacks to - * get key name and dbid on various operations. - * - * it simulates a simple memory allocator. The smallest allocation unit of - * the allocator is a mem block with a size of 4KB. Multiple mem blocks are combined - * using a linked list. These linked lists are placed in a global dict named 'mem_pool'. - * Each db has a 'mem_pool'. You can use the 'mem.alloc' command to allocate a specified - * number of mem blocks, and use 'mem.free' to release the memory. Use 'mem.write', 'mem.read' - * to write and read the specified mem block (note that each mem block can only be written once). - * Use 'mem.usage' to get the memory usage under different dbs, and it will return the size - * mem blocks and used mem blocks under the db. - * The specific structure diagram is as follows: - * - * - * Global variables of the module: - * - * mem blocks link - * ┌─────┬─────┐ - * │ │ │ ┌───┐ ┌───┐ ┌───┐ - * │ k1 │ ───┼───►│4KB├───►│4KB├───►│4KB│ - * │ │ │ └───┘ └───┘ └───┘ - * ├─────┼─────┤ - * ┌───────┐ ┌────► │ │ │ ┌───┐ ┌───┐ - * │ │ │ │ k2 │ ───┼───►│4KB├───►│4KB│ - * │ db0 ├──────┘ │ │ │ └───┘ └───┘ - * │ │ ├─────┼─────┤ - * ├───────┤ │ │ │ ┌───┐ ┌───┐ ┌───┐ - * │ │ │ k3 │ ───┼───►│4KB├───►│4KB├───►│4KB│ - * │ db1 ├──►null │ │ │ └───┘ └───┘ └───┘ - * │ │ └─────┴─────┘ - * ├───────┤ dict - * │ │ - * │ db2 ├─────────┐ - * │ │ │ - * ├───────┤ │ ┌─────┬─────┐ - * │ │ │ │ │ │ ┌───┐ ┌───┐ ┌───┐ - * │ db3 ├──►null │ │ k1 │ ───┼───►│4KB├───►│4KB├───►│4KB│ - * │ │ │ │ │ │ └───┘ └───┘ └───┘ - * └───────┘ │ ├─────┼─────┤ - * mem_pool[MAX_DB] │ │ │ │ ┌───┐ ┌───┐ - * └──►│ k2 │ ───┼───►│4KB├───►│4KB│ - * │ │ │ └───┘ └───┘ - * └─────┴─────┘ - * dict - * - * - * Keys in redis database: - * - * ┌───────┐ - * │ size │ - * ┌───────────►│ used │ - * │ │ mask │ - * ┌─────┬─────┐ │ └───────┘ ┌───────┐ - * │ │ │ │ MemAllocObject │ size │ - * │ k1 │ ───┼─┘ ┌───────────►│ used │ - * │ │ │ │ │ mask │ - * ├─────┼─────┤ ┌───────┐ ┌─────┬─────┐ │ └───────┘ - * │ │ │ │ size │ │ │ │ │ MemAllocObject - * │ k2 │ ───┼─────────────►│ used │ │ k1 │ ───┼─┘ - * │ │ │ │ mask │ │ │ │ - * ├─────┼─────┤ └───────┘ ├─────┼─────┤ - * │ │ │ MemAllocObject │ │ │ - * │ k3 │ ───┼─┐ │ k2 │ ───┼─┐ - * │ │ │ │ │ │ │ │ - * └─────┴─────┘ │ ┌───────┐ └─────┴─────┘ │ ┌───────┐ - * redis db[0] │ │ size │ redis db[1] │ │ size │ - * └───────────►│ used │ └───────────►│ used │ - * │ mask │ │ mask │ - * └───────┘ └───────┘ - * MemAllocObject MemAllocObject - * - **/ - -#include "redismodule.h" -#include <stdio.h> -#include <stdlib.h> -#include <ctype.h> -#include <string.h> -#include <stdint.h> - -static RedisModuleType *MemAllocType; - -#define MAX_DB 16 -RedisModuleDict *mem_pool[MAX_DB]; -typedef struct MemAllocObject { - long long size; - long long used; - uint64_t mask; -} MemAllocObject; - -MemAllocObject *createMemAllocObject(void) { - MemAllocObject *o = RedisModule_Calloc(1, sizeof(*o)); - return o; -} - -/*---------------------------- mem block apis ------------------------------------*/ -#define BLOCK_SIZE 4096 -struct MemBlock { - char block[BLOCK_SIZE]; - struct MemBlock *next; -}; - -void MemBlockFree(struct MemBlock *head) { - if (head) { - struct MemBlock *block = head->next, *next; - RedisModule_Free(head); - while (block) { - next = block->next; - RedisModule_Free(block); - block = next; - } - } -} -struct MemBlock *MemBlockCreate(long long num) { - if (num <= 0) { - return NULL; - } - - struct MemBlock *head = RedisModule_Calloc(1, sizeof(struct MemBlock)); - struct MemBlock *block = head; - while (--num) { - block->next = RedisModule_Calloc(1, sizeof(struct MemBlock)); - block = block->next; - } - - return head; -} - -long long MemBlockNum(const struct MemBlock *head) { - long long num = 0; - const struct MemBlock *block = head; - while (block) { - num++; - block = block->next; - } - - return num; -} - -size_t MemBlockWrite(struct MemBlock *head, long long block_index, const char *data, size_t size) { - size_t w_size = 0; - struct MemBlock *block = head; - while (block_index-- && block) { - block = block->next; - } - - if (block) { - size = size > BLOCK_SIZE ? BLOCK_SIZE:size; - memcpy(block->block, data, size); - w_size += size; - } - - return w_size; -} - -int MemBlockRead(struct MemBlock *head, long long block_index, char *data, size_t size) { - size_t r_size = 0; - struct MemBlock *block = head; - while (block_index-- && block) { - block = block->next; - } - - if (block) { - size = size > BLOCK_SIZE ? BLOCK_SIZE:size; - memcpy(data, block->block, size); - r_size += size; - } - - return r_size; -} - -void MemPoolFreeDb(RedisModuleCtx *ctx, int dbid) { - RedisModuleString *key; - void *tdata; - RedisModuleDictIter *iter = RedisModule_DictIteratorStartC(mem_pool[dbid], "^", NULL, 0); - while((key = RedisModule_DictNext(ctx, iter, &tdata)) != NULL) { - MemBlockFree((struct MemBlock *)tdata); - } - RedisModule_DictIteratorStop(iter); - RedisModule_FreeDict(NULL, mem_pool[dbid]); - mem_pool[dbid] = RedisModule_CreateDict(NULL); -} - -struct MemBlock *MemBlockClone(const struct MemBlock *head) { - struct MemBlock *newhead = NULL; - if (head) { - newhead = RedisModule_Calloc(1, sizeof(struct MemBlock)); - memcpy(newhead->block, head->block, BLOCK_SIZE); - struct MemBlock *newblock = newhead; - const struct MemBlock *oldblock = head->next; - while (oldblock) { - newblock->next = RedisModule_Calloc(1, sizeof(struct MemBlock)); - newblock = newblock->next; - memcpy(newblock->block, oldblock->block, BLOCK_SIZE); - oldblock = oldblock->next; - } - } - - return newhead; -} - -/*---------------------------- event handler ------------------------------------*/ -void swapDbCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data) { - REDISMODULE_NOT_USED(ctx); - REDISMODULE_NOT_USED(e); - REDISMODULE_NOT_USED(sub); - - RedisModuleSwapDbInfo *ei = data; - - // swap - RedisModuleDict *tmp = mem_pool[ei->dbnum_first]; - mem_pool[ei->dbnum_first] = mem_pool[ei->dbnum_second]; - mem_pool[ei->dbnum_second] = tmp; -} - -void flushdbCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data) { - REDISMODULE_NOT_USED(ctx); - REDISMODULE_NOT_USED(e); - int i; - RedisModuleFlushInfo *fi = data; - - RedisModule_AutoMemory(ctx); - - if (sub == REDISMODULE_SUBEVENT_FLUSHDB_START) { - if (fi->dbnum != -1) { - MemPoolFreeDb(ctx, fi->dbnum); - } else { - for (i = 0; i < MAX_DB; i++) { - MemPoolFreeDb(ctx, i); - } - } - } -} - -/*---------------------------- command implementation ------------------------------------*/ - -/* MEM.ALLOC key block_num */ -int MemAlloc_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - RedisModule_AutoMemory(ctx); - - if (argc != 3) { - return RedisModule_WrongArity(ctx); - } - - long long block_num; - if ((RedisModule_StringToLongLong(argv[2], &block_num) != REDISMODULE_OK) || block_num <= 0) { - return RedisModule_ReplyWithError(ctx, "ERR invalid block_num: must be a value greater than 0"); - } - - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ | REDISMODULE_WRITE); - int type = RedisModule_KeyType(key); - if (type != REDISMODULE_KEYTYPE_EMPTY && RedisModule_ModuleTypeGetType(key) != MemAllocType) { - return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); - } - - MemAllocObject *o; - if (type == REDISMODULE_KEYTYPE_EMPTY) { - o = createMemAllocObject(); - RedisModule_ModuleTypeSetValue(key, MemAllocType, o); - } else { - o = RedisModule_ModuleTypeGetValue(key); - } - - struct MemBlock *mem = MemBlockCreate(block_num); - RedisModule_Assert(mem != NULL); - RedisModule_DictSet(mem_pool[RedisModule_GetSelectedDb(ctx)], argv[1], mem); - o->size = block_num; - o->used = 0; - o->mask = 0; - - RedisModule_ReplyWithLongLong(ctx, block_num); - RedisModule_ReplicateVerbatim(ctx); - return REDISMODULE_OK; -} - -/* MEM.FREE key */ -int MemFree_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - RedisModule_AutoMemory(ctx); - - if (argc != 2) { - return RedisModule_WrongArity(ctx); - } - - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ); - int type = RedisModule_KeyType(key); - if (type != REDISMODULE_KEYTYPE_EMPTY && RedisModule_ModuleTypeGetType(key) != MemAllocType) { - return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); - } - - int ret = 0; - MemAllocObject *o; - if (type == REDISMODULE_KEYTYPE_EMPTY) { - RedisModule_ReplyWithLongLong(ctx, ret); - return REDISMODULE_OK; - } else { - o = RedisModule_ModuleTypeGetValue(key); - } - - int nokey; - struct MemBlock *mem = (struct MemBlock *)RedisModule_DictGet(mem_pool[RedisModule_GetSelectedDb(ctx)], argv[1], &nokey); - if (!nokey && mem) { - RedisModule_DictDel(mem_pool[RedisModule_GetSelectedDb(ctx)], argv[1], NULL); - MemBlockFree(mem); - o->used = 0; - o->size = 0; - o->mask = 0; - ret = 1; - } - - RedisModule_ReplyWithLongLong(ctx, ret); - RedisModule_ReplicateVerbatim(ctx); - return REDISMODULE_OK; -} - -/* MEM.WRITE key block_index data */ -int MemWrite_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - RedisModule_AutoMemory(ctx); - - if (argc != 4) { - return RedisModule_WrongArity(ctx); - } - - long long block_index; - if ((RedisModule_StringToLongLong(argv[2], &block_index) != REDISMODULE_OK) || block_index < 0) { - return RedisModule_ReplyWithError(ctx, "ERR invalid block_index: must be a value greater than 0"); - } - - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ | REDISMODULE_WRITE); - int type = RedisModule_KeyType(key); - if (type != REDISMODULE_KEYTYPE_EMPTY && RedisModule_ModuleTypeGetType(key) != MemAllocType) { - return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); - } - - MemAllocObject *o; - if (type == REDISMODULE_KEYTYPE_EMPTY) { - return RedisModule_ReplyWithError(ctx, "ERR Memory has not been allocated"); - } else { - o = RedisModule_ModuleTypeGetValue(key); - } - - if (o->mask & (1UL << block_index)) { - return RedisModule_ReplyWithError(ctx, "ERR block is busy"); - } - - int ret = 0; - int nokey; - struct MemBlock *mem = (struct MemBlock *)RedisModule_DictGet(mem_pool[RedisModule_GetSelectedDb(ctx)], argv[1], &nokey); - if (!nokey && mem) { - size_t len; - const char *buf = RedisModule_StringPtrLen(argv[3], &len); - ret = MemBlockWrite(mem, block_index, buf, len); - o->mask |= (1UL << block_index); - o->used++; - } - - RedisModule_ReplyWithLongLong(ctx, ret); - RedisModule_ReplicateVerbatim(ctx); - return REDISMODULE_OK; -} - -/* MEM.READ key block_index */ -int MemRead_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - RedisModule_AutoMemory(ctx); - - if (argc != 3) { - return RedisModule_WrongArity(ctx); - } - - long long block_index; - if ((RedisModule_StringToLongLong(argv[2], &block_index) != REDISMODULE_OK) || block_index < 0) { - return RedisModule_ReplyWithError(ctx, "ERR invalid block_index: must be a value greater than 0"); - } - - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ); - int type = RedisModule_KeyType(key); - if (type != REDISMODULE_KEYTYPE_EMPTY && RedisModule_ModuleTypeGetType(key) != MemAllocType) { - return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); - } - - MemAllocObject *o; - if (type == REDISMODULE_KEYTYPE_EMPTY) { - return RedisModule_ReplyWithError(ctx, "ERR Memory has not been allocated"); - } else { - o = RedisModule_ModuleTypeGetValue(key); - } - - if (!(o->mask & (1UL << block_index))) { - return RedisModule_ReplyWithNull(ctx); - } - - int nokey; - struct MemBlock *mem = (struct MemBlock *)RedisModule_DictGet(mem_pool[RedisModule_GetSelectedDb(ctx)], argv[1], &nokey); - RedisModule_Assert(nokey == 0 && mem != NULL); - - char buf[BLOCK_SIZE]; - MemBlockRead(mem, block_index, buf, sizeof(buf)); - - /* Assuming that the contents are all c-style strings */ - RedisModule_ReplyWithStringBuffer(ctx, buf, strlen(buf)); - return REDISMODULE_OK; -} - -/* MEM.USAGE dbid */ -int MemUsage_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - RedisModule_AutoMemory(ctx); - - if (argc != 2) { - return RedisModule_WrongArity(ctx); - } - - long long dbid; - if ((RedisModule_StringToLongLong(argv[1], (long long *)&dbid) != REDISMODULE_OK)) { - return RedisModule_ReplyWithError(ctx, "ERR invalid value: must be a integer"); - } - - if (dbid < 0 || dbid >= MAX_DB) { - return RedisModule_ReplyWithError(ctx, "ERR dbid out of range"); - } - - - long long size = 0, used = 0; - - void *data; - RedisModuleString *key; - RedisModuleDictIter *iter = RedisModule_DictIteratorStartC(mem_pool[dbid], "^", NULL, 0); - while((key = RedisModule_DictNext(ctx, iter, &data)) != NULL) { - int dbbackup = RedisModule_GetSelectedDb(ctx); - RedisModule_SelectDb(ctx, dbid); - RedisModuleKey *openkey = RedisModule_OpenKey(ctx, key, REDISMODULE_READ); - int type = RedisModule_KeyType(openkey); - RedisModule_Assert(type != REDISMODULE_KEYTYPE_EMPTY && RedisModule_ModuleTypeGetType(openkey) == MemAllocType); - MemAllocObject *o = RedisModule_ModuleTypeGetValue(openkey); - used += o->used; - size += o->size; - RedisModule_CloseKey(openkey); - RedisModule_SelectDb(ctx, dbbackup); - } - RedisModule_DictIteratorStop(iter); - - RedisModule_ReplyWithArray(ctx, 4); - RedisModule_ReplyWithSimpleString(ctx, "total"); - RedisModule_ReplyWithLongLong(ctx, size); - RedisModule_ReplyWithSimpleString(ctx, "used"); - RedisModule_ReplyWithLongLong(ctx, used); - return REDISMODULE_OK; -} - -/* MEM.ALLOCANDWRITE key block_num block_index data block_index data ... */ -int MemAllocAndWrite_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - RedisModule_AutoMemory(ctx); - - if (argc < 3) { - return RedisModule_WrongArity(ctx); - } - - long long block_num; - if ((RedisModule_StringToLongLong(argv[2], &block_num) != REDISMODULE_OK) || block_num <= 0) { - return RedisModule_ReplyWithError(ctx, "ERR invalid block_num: must be a value greater than 0"); - } - - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ | REDISMODULE_WRITE); - int type = RedisModule_KeyType(key); - if (type != REDISMODULE_KEYTYPE_EMPTY && RedisModule_ModuleTypeGetType(key) != MemAllocType) { - return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); - } - - MemAllocObject *o; - if (type == REDISMODULE_KEYTYPE_EMPTY) { - o = createMemAllocObject(); - RedisModule_ModuleTypeSetValue(key, MemAllocType, o); - } else { - o = RedisModule_ModuleTypeGetValue(key); - } - - struct MemBlock *mem = MemBlockCreate(block_num); - RedisModule_Assert(mem != NULL); - RedisModule_DictSet(mem_pool[RedisModule_GetSelectedDb(ctx)], argv[1], mem); - o->used = 0; - o->mask = 0; - o->size = block_num; - - int i = 3; - long long block_index; - for (; i < argc; i++) { - /* Security is guaranteed internally, so no security check. */ - RedisModule_StringToLongLong(argv[i], &block_index); - size_t len; - const char * buf = RedisModule_StringPtrLen(argv[i + 1], &len); - MemBlockWrite(mem, block_index, buf, len); - o->used++; - o->mask |= (1UL << block_index); - } - - RedisModule_ReplyWithSimpleString(ctx, "OK"); - RedisModule_ReplicateVerbatim(ctx); - return REDISMODULE_OK; -} - -/*---------------------------- type callbacks ------------------------------------*/ - -void *MemAllocRdbLoad(RedisModuleIO *rdb, int encver) { - if (encver != 0) { - return NULL; - } - - MemAllocObject *o = createMemAllocObject(); - o->size = RedisModule_LoadSigned(rdb); - o->used = RedisModule_LoadSigned(rdb); - o->mask = RedisModule_LoadUnsigned(rdb); - - const RedisModuleString *key = RedisModule_GetKeyNameFromIO(rdb); - int dbid = RedisModule_GetDbIdFromIO(rdb); - - if (o->size) { - size_t size; - char *tmpbuf; - long long num = o->size; - struct MemBlock *head = RedisModule_Calloc(1, sizeof(struct MemBlock)); - tmpbuf = RedisModule_LoadStringBuffer(rdb, &size); - memcpy(head->block, tmpbuf, size > BLOCK_SIZE ? BLOCK_SIZE:size); - RedisModule_Free(tmpbuf); - struct MemBlock *block = head; - while (--num) { - block->next = RedisModule_Calloc(1, sizeof(struct MemBlock)); - block = block->next; - - tmpbuf = RedisModule_LoadStringBuffer(rdb, &size); - memcpy(block->block, tmpbuf, size > BLOCK_SIZE ? BLOCK_SIZE:size); - RedisModule_Free(tmpbuf); - } - - RedisModule_DictSet(mem_pool[dbid], (RedisModuleString *)key, head); - } - - return o; -} - -void MemAllocRdbSave(RedisModuleIO *rdb, void *value) { - MemAllocObject *o = value; - RedisModule_SaveSigned(rdb, o->size); - RedisModule_SaveSigned(rdb, o->used); - RedisModule_SaveUnsigned(rdb, o->mask); - - const RedisModuleString *key = RedisModule_GetKeyNameFromIO(rdb); - int dbid = RedisModule_GetDbIdFromIO(rdb); - - if (o->size) { - int nokey; - struct MemBlock *mem = (struct MemBlock *)RedisModule_DictGet(mem_pool[dbid], (RedisModuleString *)key, &nokey); - RedisModule_Assert(nokey == 0 && mem != NULL); - - struct MemBlock *block = mem; - while (block) { - RedisModule_SaveStringBuffer(rdb, block->block, BLOCK_SIZE); - block = block->next; - } - } -} - -void MemAllocAofRewrite(RedisModuleIO *aof, RedisModuleString *key, void *value) { - MemAllocObject *o = (MemAllocObject *)value; - if (o->size) { - int dbid = RedisModule_GetDbIdFromIO(aof); - int nokey; - size_t i = 0, j = 0; - struct MemBlock *mem = (struct MemBlock *)RedisModule_DictGet(mem_pool[dbid], (RedisModuleString *)key, &nokey); - RedisModule_Assert(nokey == 0 && mem != NULL); - size_t array_size = o->size * 2; - RedisModuleString ** string_array = RedisModule_Calloc(array_size, sizeof(RedisModuleString *)); - while (mem) { - string_array[i] = RedisModule_CreateStringFromLongLong(NULL, j); - string_array[i + 1] = RedisModule_CreateString(NULL, mem->block, BLOCK_SIZE); - mem = mem->next; - i += 2; - j++; - } - RedisModule_EmitAOF(aof, "mem.allocandwrite", "slv", key, o->size, string_array, array_size); - for (i = 0; i < array_size; i++) { - RedisModule_FreeString(NULL, string_array[i]); - } - RedisModule_Free(string_array); - } else { - RedisModule_EmitAOF(aof, "mem.allocandwrite", "sl", key, o->size); - } -} - -void MemAllocFree(void *value) { - RedisModule_Free(value); -} - -void MemAllocUnlink(RedisModuleString *key, const void *value) { - REDISMODULE_NOT_USED(key); - REDISMODULE_NOT_USED(value); - - /* When unlink and unlink2 exist at the same time, we will only call unlink2. */ - RedisModule_Assert(0); -} - -void MemAllocUnlink2(RedisModuleKeyOptCtx *ctx, const void *value) { - MemAllocObject *o = (MemAllocObject *)value; - - const RedisModuleString *key = RedisModule_GetKeyNameFromOptCtx(ctx); - int dbid = RedisModule_GetDbIdFromOptCtx(ctx); - - if (o->size) { - void *oldval; - RedisModule_DictDel(mem_pool[dbid], (RedisModuleString *)key, &oldval); - RedisModule_Assert(oldval != NULL); - MemBlockFree((struct MemBlock *)oldval); - } -} - -void MemAllocDigest(RedisModuleDigest *md, void *value) { - MemAllocObject *o = (MemAllocObject *)value; - RedisModule_DigestAddLongLong(md, o->size); - RedisModule_DigestAddLongLong(md, o->used); - RedisModule_DigestAddLongLong(md, o->mask); - - int dbid = RedisModule_GetDbIdFromDigest(md); - const RedisModuleString *key = RedisModule_GetKeyNameFromDigest(md); - - if (o->size) { - int nokey; - struct MemBlock *mem = (struct MemBlock *)RedisModule_DictGet(mem_pool[dbid], (RedisModuleString *)key, &nokey); - RedisModule_Assert(nokey == 0 && mem != NULL); - - struct MemBlock *block = mem; - while (block) { - RedisModule_DigestAddStringBuffer(md, (const char *)block->block, BLOCK_SIZE); - block = block->next; - } - } -} - -void *MemAllocCopy2(RedisModuleKeyOptCtx *ctx, const void *value) { - const MemAllocObject *old = value; - MemAllocObject *new = createMemAllocObject(); - new->size = old->size; - new->used = old->used; - new->mask = old->mask; - - int from_dbid = RedisModule_GetDbIdFromOptCtx(ctx); - int to_dbid = RedisModule_GetToDbIdFromOptCtx(ctx); - const RedisModuleString *fromkey = RedisModule_GetKeyNameFromOptCtx(ctx); - const RedisModuleString *tokey = RedisModule_GetToKeyNameFromOptCtx(ctx); - - if (old->size) { - int nokey; - struct MemBlock *oldmem = (struct MemBlock *)RedisModule_DictGet(mem_pool[from_dbid], (RedisModuleString *)fromkey, &nokey); - RedisModule_Assert(nokey == 0 && oldmem != NULL); - struct MemBlock *newmem = MemBlockClone(oldmem); - RedisModule_Assert(newmem != NULL); - RedisModule_DictSet(mem_pool[to_dbid], (RedisModuleString *)tokey, newmem); - } - - return new; -} - -size_t MemAllocMemUsage2(RedisModuleKeyOptCtx *ctx, const void *value, size_t sample_size) { - REDISMODULE_NOT_USED(ctx); - REDISMODULE_NOT_USED(sample_size); - uint64_t size = 0; - MemAllocObject *o = (MemAllocObject *)value; - - size += sizeof(*o); - size += o->size * sizeof(struct MemBlock); - - return size; -} - -size_t MemAllocMemFreeEffort2(RedisModuleKeyOptCtx *ctx, const void *value) { - REDISMODULE_NOT_USED(ctx); - MemAllocObject *o = (MemAllocObject *)value; - return o->size; -} - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - if (RedisModule_Init(ctx, "datatype2", 1,REDISMODULE_APIVER_1) == REDISMODULE_ERR) { - return REDISMODULE_ERR; - } - - RedisModuleTypeMethods tm = { - .version = REDISMODULE_TYPE_METHOD_VERSION, - .rdb_load = MemAllocRdbLoad, - .rdb_save = MemAllocRdbSave, - .aof_rewrite = MemAllocAofRewrite, - .free = MemAllocFree, - .digest = MemAllocDigest, - .unlink = MemAllocUnlink, - // .defrag = MemAllocDefrag, // Tested in defragtest.c - .unlink2 = MemAllocUnlink2, - .copy2 = MemAllocCopy2, - .mem_usage2 = MemAllocMemUsage2, - .free_effort2 = MemAllocMemFreeEffort2, - }; - - MemAllocType = RedisModule_CreateDataType(ctx, "mem_alloc", 0, &tm); - if (MemAllocType == NULL) { - return REDISMODULE_ERR; - } - - if (RedisModule_CreateCommand(ctx, "mem.alloc", MemAlloc_RedisCommand, "write deny-oom", 1, 1, 1) == REDISMODULE_ERR) { - return REDISMODULE_ERR; - } - - if (RedisModule_CreateCommand(ctx, "mem.free", MemFree_RedisCommand, "write deny-oom", 1, 1, 1) == REDISMODULE_ERR) { - return REDISMODULE_ERR; - } - - if (RedisModule_CreateCommand(ctx, "mem.write", MemWrite_RedisCommand, "write deny-oom", 1, 1, 1) == REDISMODULE_ERR) { - return REDISMODULE_ERR; - } - - if (RedisModule_CreateCommand(ctx, "mem.read", MemRead_RedisCommand, "readonly", 1, 1, 1) == REDISMODULE_ERR) { - return REDISMODULE_ERR; - } - - if (RedisModule_CreateCommand(ctx, "mem.usage", MemUsage_RedisCommand, "readonly", 1, 1, 1) == REDISMODULE_ERR) { - return REDISMODULE_ERR; - } - - /* used for internal aof rewrite */ - if (RedisModule_CreateCommand(ctx, "mem.allocandwrite", MemAllocAndWrite_RedisCommand, "write deny-oom", 1, 1, 1) == REDISMODULE_ERR) { - return REDISMODULE_ERR; - } - - for(int i = 0; i < MAX_DB; i++){ - mem_pool[i] = RedisModule_CreateDict(NULL); - } - - RedisModule_SubscribeToServerEvent(ctx, RedisModuleEvent_FlushDB, flushdbCallback); - RedisModule_SubscribeToServerEvent(ctx, RedisModuleEvent_SwapDB, swapDbCallback); - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/defragtest.c b/examples/redis-unstable/tests/modules/defragtest.c deleted file mode 100644 index 8b9e182..0000000 --- a/examples/redis-unstable/tests/modules/defragtest.c +++ /dev/null @@ -1,475 +0,0 @@ -/* A module that implements defrag callback mechanisms. - */ - -#include "redismodule.h" -#include <stdlib.h> -#include <string.h> - -#define UNUSED(V) ((void) V) - -static RedisModuleType *FragType; - -struct FragObject { - unsigned long len; - void **values; - int maxstep; -}; - -/* Make sure we get the expected cursor */ -unsigned long int last_set_cursor = 0; - -unsigned long int datatype_attempts = 0; -unsigned long int datatype_defragged = 0; -unsigned long int datatype_raw_defragged = 0; -unsigned long int datatype_resumes = 0; -unsigned long int datatype_wrong_cursor = 0; -unsigned long int defrag_started = 0; -unsigned long int defrag_ended = 0; -unsigned long int global_strings_attempts = 0; -unsigned long int global_strings_defragged = 0; -unsigned long int global_dicts_resumes = 0; /* Number of dict defragmentation resumed from a previous break */ -unsigned long int global_subdicts_resumes = 0; /* Number of subdict defragmentation resumed from a previous break */ -unsigned long int global_dicts_attempts = 0; /* Number of attempts to defragment dictionary */ -unsigned long int global_dicts_defragged = 0; /* Number of dictionaries successfully defragmented */ -unsigned long int global_dicts_items_defragged = 0; /* Number of dictionaries items successfully defragmented */ - -unsigned long global_strings_len = 0; -RedisModuleString **global_strings = NULL; - -unsigned long global_dicts_len = 0; -RedisModuleDict **global_dicts = NULL; - -static void createGlobalStrings(RedisModuleCtx *ctx, unsigned long count) -{ - global_strings_len = count; - global_strings = RedisModule_Alloc(sizeof(RedisModuleString *) * count); - - for (unsigned long i = 0; i < count; i++) { - global_strings[i] = RedisModule_CreateStringFromLongLong(ctx, i); - } -} - -static int defragGlobalStrings(RedisModuleDefragCtx *ctx) -{ - unsigned long cursor = 0; - RedisModule_DefragCursorGet(ctx, &cursor); - - if (!global_strings_len) return 0; /* strings is empty. */ - RedisModule_Assert(cursor < global_strings_len); - for (; cursor < global_strings_len; cursor++) { - RedisModuleString *str = global_strings[cursor]; - if (!str) continue; - RedisModuleString *new = RedisModule_DefragRedisModuleString(ctx, str); - global_strings_attempts++; - if (new != NULL) { - global_strings[cursor] = new; - global_strings_defragged++; - } - - if (RedisModule_DefragShouldStop(ctx)) { - RedisModule_DefragCursorSet(ctx, cursor); - return 1; - } - } - return 0; -} - -static void createFragGlobalStrings(RedisModuleCtx *ctx) { - for (unsigned long i = 0; i < global_strings_len; i++) { - if (i % 2 == 1) { - RedisModule_FreeString(ctx, global_strings[i]); - global_strings[i] = NULL; - } - } -} - -static void createGlobalDicts(RedisModuleCtx *ctx, unsigned long count) { - global_dicts_len = count; - global_dicts = RedisModule_Alloc(sizeof(RedisModuleDict *) * count); - - /* Create some nested dictionaries: - * - Each main dict contains some subdicts. - * - Each sub-dict contains some strings. */ - for (unsigned long i = 0; i < count; i++) { - RedisModuleDict *dict = RedisModule_CreateDict(ctx); - for (unsigned long j = 0; j < 10; j++) { - /* Create sub dict. */ - RedisModuleDict *subdict = RedisModule_CreateDict(ctx); - for (unsigned long k = 0; k < 10; k++) { - RedisModuleString *str = RedisModule_CreateStringFromULongLong(ctx, k); - RedisModule_DictSet(subdict, str, str); - } - - RedisModuleString *key = RedisModule_CreateStringFromULongLong(ctx, j); - RedisModule_DictSet(dict, key, subdict); - RedisModule_FreeString(ctx, key); - } - global_dicts[i] = dict; - } -} - -static void freeFragGlobalSubDict(RedisModuleCtx *ctx, RedisModuleDict *subdict) { - char *key; - size_t keylen; - RedisModuleString *str; - RedisModuleDictIter *iter = RedisModule_DictIteratorStartC(subdict, "^", NULL, 0); - while ((key = RedisModule_DictNextC(iter, &keylen, (void**)&str))) { - RedisModule_FreeString(ctx, str); - } - RedisModule_FreeDict(ctx, subdict); - RedisModule_DictIteratorStop(iter); -} - -static void createFragGlobalDicts(RedisModuleCtx *ctx) { - char *key; - size_t keylen; - RedisModuleDict *subdict; - - for (unsigned long i = 0; i < global_dicts_len; i++) { - RedisModuleDict *dict = global_dicts[i]; - if (!dict) continue; - - /* Handle dictionaries differently based on their index in global_dicts array: - * 1. For odd indices (i % 2 == 1): Remove the entire dictionary. - * 2. For even indices: Keep the dictionary but remove half of its items. */ - if (i % 2 == 1) { - RedisModuleDictIter *iter = RedisModule_DictIteratorStartC(dict, "^", NULL, 0); - while ((key = RedisModule_DictNextC(iter, &keylen, (void**)&subdict))) { - freeFragGlobalSubDict(ctx, subdict); - } - RedisModule_FreeDict(ctx, dict); - global_dicts[i] = NULL; - RedisModule_DictIteratorStop(iter); - } else { - int key_index = 0; - RedisModuleDictIter *iter = RedisModule_DictIteratorStartC(dict, "^", NULL, 0); - while ((key = RedisModule_DictNextC(iter, &keylen, (void**)&subdict))) { - if (key_index++ % 2 == 1) { - freeFragGlobalSubDict(ctx, subdict); - RedisModule_DictReplaceC(dict, key, keylen, NULL); - } - } - RedisModule_DictIteratorStop(iter); - } - } -} - -static int defragGlobalSubDictValueCB(RedisModuleDefragCtx *ctx, void *data, unsigned char *key, size_t keylen, void **newptr) { - REDISMODULE_NOT_USED(key); - REDISMODULE_NOT_USED(keylen); - if (!data) return 0; - *newptr = RedisModule_DefragAlloc(ctx, data); - return 0; -} - -static int defragGlobalDictValueCB(RedisModuleDefragCtx *ctx, void *data, unsigned char *key, size_t keylen, void **newptr) { - REDISMODULE_NOT_USED(key); - REDISMODULE_NOT_USED(keylen); - static RedisModuleString *seekTo = NULL; - RedisModuleDict *subdict = data; - if (!subdict) return 0; - if (seekTo != NULL) global_subdicts_resumes++; - - *newptr = RedisModule_DefragRedisModuleDict(ctx, subdict, defragGlobalSubDictValueCB, &seekTo); - if (*newptr) global_dicts_items_defragged++; - /* Return 1 if seekTo is not NULL, indicating this node needs more defrag work. */ - return seekTo != NULL; -} - -static int defragGlobalDicts(RedisModuleDefragCtx *ctx) { - static RedisModuleString *seekTo = NULL; - static unsigned long dict_index = 0; - unsigned long cursor = 0; - - RedisModule_DefragCursorGet(ctx, &cursor); - if (cursor == 0) { /* Start a new defrag. */ - if (seekTo) { - RedisModule_FreeString(NULL, seekTo); - seekTo = NULL; - } - dict_index = 0; - } else { - global_dicts_resumes++; - } - - if (!global_dicts_len) return 0; /* dicts is empty. */ - RedisModule_Assert(dict_index < global_dicts_len); - for (; dict_index < global_dicts_len; dict_index++) { - RedisModuleDict *dict = global_dicts[dict_index]; - if (!dict) continue; - RedisModuleDict *new = RedisModule_DefragRedisModuleDict(ctx, dict, defragGlobalDictValueCB, &seekTo); - global_dicts_attempts++; - if (new != NULL) { - global_dicts[dict_index] = new; - global_dicts_defragged++; - } - - if (seekTo != NULL) { - /* Set cursor to 1 to indicate defragmentation is not finished. */ - RedisModule_DefragCursorSet(ctx, 1); - return 1; - } - } - - /* Set cursor to 0 to indicate completion. */ - dict_index = 0; - RedisModule_DefragCursorSet(ctx, 0); - return 0; -} - -typedef enum { DEFRAG_NOT_START, DEFRAG_STRING, DEFRAG_DICT } defrag_module_stage; -static int defragGlobal(RedisModuleDefragCtx *ctx) { - static defrag_module_stage stage = DEFRAG_NOT_START; - if (stage == DEFRAG_NOT_START) { - stage = DEFRAG_STRING; /* Start a new global defrag. */ - } - - if (stage == DEFRAG_STRING) { - if (defragGlobalStrings(ctx) != 0) return 1; - stage = DEFRAG_DICT; - } - if (stage == DEFRAG_DICT) { - if (defragGlobalDicts(ctx) != 0) return 1; - stage = DEFRAG_NOT_START; - } - return 0; -} - -static void defragStart(RedisModuleDefragCtx *ctx) { - REDISMODULE_NOT_USED(ctx); - defrag_started++; -} - -static void defragEnd(RedisModuleDefragCtx *ctx) { - REDISMODULE_NOT_USED(ctx); - defrag_ended++; -} - -static void FragInfo(RedisModuleInfoCtx *ctx, int for_crash_report) { - REDISMODULE_NOT_USED(for_crash_report); - - RedisModule_InfoAddSection(ctx, "stats"); - RedisModule_InfoAddFieldLongLong(ctx, "datatype_attempts", datatype_attempts); - RedisModule_InfoAddFieldLongLong(ctx, "datatype_defragged", datatype_defragged); - RedisModule_InfoAddFieldLongLong(ctx, "datatype_raw_defragged", datatype_raw_defragged); - RedisModule_InfoAddFieldLongLong(ctx, "datatype_resumes", datatype_resumes); - RedisModule_InfoAddFieldLongLong(ctx, "datatype_wrong_cursor", datatype_wrong_cursor); - RedisModule_InfoAddFieldLongLong(ctx, "global_strings_attempts", global_strings_attempts); - RedisModule_InfoAddFieldLongLong(ctx, "global_strings_defragged", global_strings_defragged); - RedisModule_InfoAddFieldLongLong(ctx, "global_dicts_resumes", global_dicts_resumes); - RedisModule_InfoAddFieldLongLong(ctx, "global_subdicts_resumes", global_subdicts_resumes); - RedisModule_InfoAddFieldLongLong(ctx, "global_dicts_attempts", global_dicts_attempts); - RedisModule_InfoAddFieldLongLong(ctx, "global_dicts_defragged", global_dicts_defragged); - RedisModule_InfoAddFieldLongLong(ctx, "global_dicts_items_defragged", global_dicts_items_defragged); - RedisModule_InfoAddFieldLongLong(ctx, "defrag_started", defrag_started); - RedisModule_InfoAddFieldLongLong(ctx, "defrag_ended", defrag_ended); -} - -struct FragObject *createFragObject(unsigned long len, unsigned long size, int maxstep) { - struct FragObject *o = RedisModule_Alloc(sizeof(*o)); - o->len = len; - o->values = RedisModule_Alloc(sizeof(RedisModuleString*) * len); - o->maxstep = maxstep; - - for (unsigned long i = 0; i < len; i++) { - o->values[i] = RedisModule_Calloc(1, size); - } - - return o; -} - -/* FRAG.RESETSTATS */ -static int fragResetStatsCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - datatype_attempts = 0; - datatype_defragged = 0; - datatype_raw_defragged = 0; - datatype_resumes = 0; - datatype_wrong_cursor = 0; - global_strings_attempts = 0; - global_strings_defragged = 0; - global_dicts_resumes = 0; - global_subdicts_resumes = 0; - global_dicts_attempts = 0; - global_dicts_defragged = 0; - global_dicts_items_defragged = 0; - defrag_started = 0; - defrag_ended = 0; - - RedisModule_ReplyWithSimpleString(ctx, "OK"); - return REDISMODULE_OK; -} - -/* FRAG.CREATE key len size maxstep */ -static int fragCreateCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 5) - return RedisModule_WrongArity(ctx); - - RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], - REDISMODULE_READ|REDISMODULE_WRITE); - int type = RedisModule_KeyType(key); - if (type != REDISMODULE_KEYTYPE_EMPTY) - { - return RedisModule_ReplyWithError(ctx, "ERR key exists"); - } - - long long len; - if ((RedisModule_StringToLongLong(argv[2], &len) != REDISMODULE_OK)) { - return RedisModule_ReplyWithError(ctx, "ERR invalid len"); - } - - long long size; - if ((RedisModule_StringToLongLong(argv[3], &size) != REDISMODULE_OK)) { - return RedisModule_ReplyWithError(ctx, "ERR invalid size"); - } - - long long maxstep; - if ((RedisModule_StringToLongLong(argv[4], &maxstep) != REDISMODULE_OK)) { - return RedisModule_ReplyWithError(ctx, "ERR invalid maxstep"); - } - - struct FragObject *o = createFragObject(len, size, maxstep); - RedisModule_ModuleTypeSetValue(key, FragType, o); - RedisModule_ReplyWithSimpleString(ctx, "OK"); - RedisModule_CloseKey(key); - - return REDISMODULE_OK; -} - -/* FRAG.create_frag_global len */ -static int fragCreateGlobalCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - UNUSED(argv); - if (argc != 2) - return RedisModule_WrongArity(ctx); - - long long glen; - if ((RedisModule_StringToLongLong(argv[1], &glen) != REDISMODULE_OK)) { - return RedisModule_ReplyWithError(ctx, "ERR invalid len"); - } - - createGlobalStrings(ctx, glen); - createGlobalDicts(ctx, glen); - createFragGlobalStrings(ctx); - createFragGlobalDicts(ctx); - RedisModule_ReplyWithSimpleString(ctx, "OK"); - return REDISMODULE_OK; -} - -void FragFree(void *value) { - struct FragObject *o = value; - - for (unsigned long i = 0; i < o->len; i++) - RedisModule_Free(o->values[i]); - RedisModule_Free(o->values); - RedisModule_Free(o); -} - -size_t FragFreeEffort(RedisModuleString *key, const void *value) { - REDISMODULE_NOT_USED(key); - - const struct FragObject *o = value; - return o->len; -} - -int FragDefrag(RedisModuleDefragCtx *ctx, RedisModuleString *key, void **value) { - unsigned long i = 0; - int steps = 0; - - int dbid = RedisModule_GetDbIdFromDefragCtx(ctx); - RedisModule_Assert(dbid != -1); - - RedisModule_Log(NULL, "notice", "Defrag key: %s", RedisModule_StringPtrLen(key, NULL)); - - /* Attempt to get cursor, validate it's what we're exepcting */ - if (RedisModule_DefragCursorGet(ctx, &i) == REDISMODULE_OK) { - if (i > 0) datatype_resumes++; - - /* Validate we're expecting this cursor */ - if (i != last_set_cursor) datatype_wrong_cursor++; - } else { - if (last_set_cursor != 0) datatype_wrong_cursor++; - } - - /* Attempt to defrag the object itself */ - datatype_attempts++; - struct FragObject *o = RedisModule_DefragAlloc(ctx, *value); - if (o == NULL) { - /* Not defragged */ - o = *value; - } else { - /* Defragged */ - *value = o; - datatype_defragged++; - } - - /* Deep defrag now */ - for (; i < o->len; i++) { - datatype_attempts++; - void *new = RedisModule_DefragAlloc(ctx, o->values[i]); - if (new) { - o->values[i] = new; - datatype_defragged++; - } - - if ((o->maxstep && ++steps > o->maxstep) || - ((i % 64 == 0) && RedisModule_DefragShouldStop(ctx))) - { - RedisModule_DefragCursorSet(ctx, i); - last_set_cursor = i; - return 1; - } - } - - /* Defrag the values array itself using RedisModule_DefragAllocRaw - * and RedisModule_DefragFreeRaw for testing purposes. */ - void *new_values = RedisModule_DefragAllocRaw(ctx, o->len * sizeof(void*)); - memcpy(new_values, o->values, o->len * sizeof(void*)); - RedisModule_DefragFreeRaw(ctx, o->values); - o->values = new_values; - datatype_raw_defragged++; - - last_set_cursor = 0; - return 0; -} - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - if (RedisModule_Init(ctx, "defragtest", 1, REDISMODULE_APIVER_1) - == REDISMODULE_ERR) return REDISMODULE_ERR; - - if (RedisModule_GetTypeMethodVersion() < REDISMODULE_TYPE_METHOD_VERSION) { - return REDISMODULE_ERR; - } - - RedisModuleTypeMethods tm = { - .version = REDISMODULE_TYPE_METHOD_VERSION, - .free = FragFree, - .free_effort = FragFreeEffort, - .defrag = FragDefrag - }; - - FragType = RedisModule_CreateDataType(ctx, "frag_type", 0, &tm); - if (FragType == NULL) return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "frag.create", - fragCreateCommand, "write deny-oom", 1, 1, 1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "frag.create_frag_global", - fragCreateGlobalCommand, "write deny-oom", 1, 1, 1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "frag.resetstats", - fragResetStatsCommand, "write deny-oom", 1, 1, 1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - RedisModule_RegisterInfoFunc(ctx, FragInfo); - RedisModule_RegisterDefragFunc2(ctx, defragGlobal); - RedisModule_RegisterDefragCallbacks(ctx, defragStart, defragEnd); - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/eventloop.c b/examples/redis-unstable/tests/modules/eventloop.c deleted file mode 100644 index c0cfdf0..0000000 --- a/examples/redis-unstable/tests/modules/eventloop.c +++ /dev/null @@ -1,276 +0,0 @@ -/* This module contains four tests : - * 1- test.sanity : Basic tests for argument validation mostly. - * 2- test.sendbytes : Creates a pipe and registers its fds to the event loop, - * one end of the pipe for read events and the other end for - * the write events. On writable event, data is written. On - * readable event data is read. Repeated until all data is - * received. - * 3- test.iteration : A test for BEFORE_SLEEP and AFTER_SLEEP callbacks. - * Counters are incremented each time these events are - * fired. They should be equal and increment monotonically. - * 4- test.oneshot : Test for oneshot API - */ - -#include "redismodule.h" -#include <stdlib.h> -#include <unistd.h> -#include <fcntl.h> -#include <memory.h> -#include <errno.h> - -int fds[2]; -long long buf_size; -char *src; -long long src_offset; -char *dst; -long long dst_offset; - -RedisModuleBlockedClient *bc; -RedisModuleCtx *reply_ctx; - -void onReadable(int fd, void *user_data, int mask) { - REDISMODULE_NOT_USED(mask); - - RedisModule_Assert(strcmp(user_data, "userdataread") == 0); - - while (1) { - int rd = read(fd, dst + dst_offset, buf_size - dst_offset); - if (rd <= 0) - return; - dst_offset += rd; - - /* Received all bytes */ - if (dst_offset == buf_size) { - if (memcmp(src, dst, buf_size) == 0) - RedisModule_ReplyWithSimpleString(reply_ctx, "OK"); - else - RedisModule_ReplyWithError(reply_ctx, "ERR bytes mismatch"); - - RedisModule_EventLoopDel(fds[0], REDISMODULE_EVENTLOOP_READABLE); - RedisModule_EventLoopDel(fds[1], REDISMODULE_EVENTLOOP_WRITABLE); - RedisModule_Free(src); - RedisModule_Free(dst); - close(fds[0]); - close(fds[1]); - - RedisModule_FreeThreadSafeContext(reply_ctx); - RedisModule_UnblockClient(bc, NULL); - return; - } - }; -} - -void onWritable(int fd, void *user_data, int mask) { - REDISMODULE_NOT_USED(user_data); - REDISMODULE_NOT_USED(mask); - - RedisModule_Assert(strcmp(user_data, "userdatawrite") == 0); - - while (1) { - /* Check if we sent all data */ - if (src_offset >= buf_size) - return; - int written = write(fd, src + src_offset, buf_size - src_offset); - if (written <= 0) { - return; - } - - src_offset += written; - }; -} - -/* Create a pipe(), register pipe fds to the event loop and send/receive data - * using them. */ -int sendbytes(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - if (RedisModule_StringToLongLong(argv[1], &buf_size) != REDISMODULE_OK || - buf_size == 0) { - RedisModule_ReplyWithError(ctx, "Invalid integer value"); - return REDISMODULE_OK; - } - - bc = RedisModule_BlockClient(ctx, NULL, NULL, NULL, 0); - reply_ctx = RedisModule_GetThreadSafeContext(bc); - - /* Allocate source buffer and write some random data */ - src = RedisModule_Calloc(1,buf_size); - src_offset = 0; - memset(src, rand() % 0xFF, buf_size); - memcpy(src, "randomtestdata", strlen("randomtestdata")); - - dst = RedisModule_Calloc(1,buf_size); - dst_offset = 0; - - /* Create a pipe and register it to the event loop. */ - if (pipe(fds) < 0) return REDISMODULE_ERR; - if (fcntl(fds[0], F_SETFL, O_NONBLOCK) < 0) return REDISMODULE_ERR; - if (fcntl(fds[1], F_SETFL, O_NONBLOCK) < 0) return REDISMODULE_ERR; - - if (RedisModule_EventLoopAdd(fds[0], REDISMODULE_EVENTLOOP_READABLE, - onReadable, "userdataread") != REDISMODULE_OK) return REDISMODULE_ERR; - if (RedisModule_EventLoopAdd(fds[1], REDISMODULE_EVENTLOOP_WRITABLE, - onWritable, "userdatawrite") != REDISMODULE_OK) return REDISMODULE_ERR; - return REDISMODULE_OK; -} - -int sanity(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - if (pipe(fds) < 0) return REDISMODULE_ERR; - - if (RedisModule_EventLoopAdd(fds[0], 9999999, onReadable, NULL) - == REDISMODULE_OK || errno != EINVAL) { - RedisModule_ReplyWithError(ctx, "ERR non-existing event type should fail"); - goto out; - } - if (RedisModule_EventLoopAdd(-1, REDISMODULE_EVENTLOOP_READABLE, onReadable, NULL) - == REDISMODULE_OK || errno != ERANGE) { - RedisModule_ReplyWithError(ctx, "ERR out of range fd should fail"); - goto out; - } - if (RedisModule_EventLoopAdd(99999999, REDISMODULE_EVENTLOOP_READABLE, onReadable, NULL) - == REDISMODULE_OK || errno != ERANGE) { - RedisModule_ReplyWithError(ctx, "ERR out of range fd should fail"); - goto out; - } - if (RedisModule_EventLoopAdd(fds[0], REDISMODULE_EVENTLOOP_READABLE, NULL, NULL) - == REDISMODULE_OK || errno != EINVAL) { - RedisModule_ReplyWithError(ctx, "ERR null callback should fail"); - goto out; - } - if (RedisModule_EventLoopAdd(fds[0], 9999999, onReadable, NULL) - == REDISMODULE_OK || errno != EINVAL) { - RedisModule_ReplyWithError(ctx, "ERR non-existing event type should fail"); - goto out; - } - if (RedisModule_EventLoopDel(fds[0], REDISMODULE_EVENTLOOP_READABLE) - != REDISMODULE_OK || errno != 0) { - RedisModule_ReplyWithError(ctx, "ERR del on non-registered fd should not fail"); - goto out; - } - if (RedisModule_EventLoopDel(fds[0], 9999999) == REDISMODULE_OK || - errno != EINVAL) { - RedisModule_ReplyWithError(ctx, "ERR non-existing event type should fail"); - goto out; - } - if (RedisModule_EventLoopDel(-1, REDISMODULE_EVENTLOOP_READABLE) - == REDISMODULE_OK || errno != ERANGE) { - RedisModule_ReplyWithError(ctx, "ERR out of range fd should fail"); - goto out; - } - if (RedisModule_EventLoopDel(99999999, REDISMODULE_EVENTLOOP_READABLE) - == REDISMODULE_OK || errno != ERANGE) { - RedisModule_ReplyWithError(ctx, "ERR out of range fd should fail"); - goto out; - } - if (RedisModule_EventLoopAdd(fds[0], REDISMODULE_EVENTLOOP_READABLE, onReadable, NULL) - != REDISMODULE_OK || errno != 0) { - RedisModule_ReplyWithError(ctx, "ERR Add failed"); - goto out; - } - if (RedisModule_EventLoopAdd(fds[0], REDISMODULE_EVENTLOOP_READABLE, onReadable, NULL) - != REDISMODULE_OK || errno != 0) { - RedisModule_ReplyWithError(ctx, "ERR Adding same fd twice failed"); - goto out; - } - if (RedisModule_EventLoopDel(fds[0], REDISMODULE_EVENTLOOP_READABLE) - != REDISMODULE_OK || errno != 0) { - RedisModule_ReplyWithError(ctx, "ERR Del failed"); - goto out; - } - if (RedisModule_EventLoopAddOneShot(NULL, NULL) == REDISMODULE_OK || errno != EINVAL) { - RedisModule_ReplyWithError(ctx, "ERR null callback should fail"); - goto out; - } - - RedisModule_ReplyWithSimpleString(ctx, "OK"); -out: - close(fds[0]); - close(fds[1]); - return REDISMODULE_OK; -} - -static long long beforeSleepCount; -static long long afterSleepCount; - -int iteration(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - /* On each event loop iteration, eventloopCallback() is called. We increment - * beforeSleepCount and afterSleepCount, so these two should be equal. - * We reply with iteration count, caller can test if iteration count - * increments monotonically */ - RedisModule_Assert(beforeSleepCount == afterSleepCount); - RedisModule_ReplyWithLongLong(ctx, beforeSleepCount); - return REDISMODULE_OK; -} - -void oneshotCallback(void* arg) -{ - RedisModule_Assert(strcmp(arg, "userdata") == 0); - RedisModule_ReplyWithSimpleString(reply_ctx, "OK"); - RedisModule_FreeThreadSafeContext(reply_ctx); - RedisModule_UnblockClient(bc, NULL); -} - -int oneshot(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - bc = RedisModule_BlockClient(ctx, NULL, NULL, NULL, 0); - reply_ctx = RedisModule_GetThreadSafeContext(bc); - - if (RedisModule_EventLoopAddOneShot(oneshotCallback, "userdata") != REDISMODULE_OK) { - RedisModule_ReplyWithError(ctx, "ERR oneshot failed"); - RedisModule_FreeThreadSafeContext(reply_ctx); - RedisModule_UnblockClient(bc, NULL); - } - return REDISMODULE_OK; -} - -void eventloopCallback(struct RedisModuleCtx *ctx, RedisModuleEvent eid, uint64_t subevent, void *data) { - REDISMODULE_NOT_USED(ctx); - REDISMODULE_NOT_USED(eid); - REDISMODULE_NOT_USED(subevent); - REDISMODULE_NOT_USED(data); - - RedisModule_Assert(eid.id == REDISMODULE_EVENT_EVENTLOOP); - if (subevent == REDISMODULE_SUBEVENT_EVENTLOOP_BEFORE_SLEEP) - beforeSleepCount++; - else if (subevent == REDISMODULE_SUBEVENT_EVENTLOOP_AFTER_SLEEP) - afterSleepCount++; -} - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - if (RedisModule_Init(ctx,"eventloop",1,REDISMODULE_APIVER_1) - == REDISMODULE_ERR) return REDISMODULE_ERR; - - /* Test basics. */ - if (RedisModule_CreateCommand(ctx, "test.sanity", sanity, "", 0, 0, 0) - == REDISMODULE_ERR) return REDISMODULE_ERR; - - /* Register a command to create a pipe() and send data through it by using - * event loop API. */ - if (RedisModule_CreateCommand(ctx, "test.sendbytes", sendbytes, "", 0, 0, 0) - == REDISMODULE_ERR) return REDISMODULE_ERR; - - /* Register a command to return event loop iteration count. */ - if (RedisModule_CreateCommand(ctx, "test.iteration", iteration, "", 0, 0, 0) - == REDISMODULE_ERR) return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "test.oneshot", oneshot, "", 0, 0, 0) - == REDISMODULE_ERR) return REDISMODULE_ERR; - - if (RedisModule_SubscribeToServerEvent(ctx, RedisModuleEvent_EventLoop, - eventloopCallback) != REDISMODULE_OK) return REDISMODULE_ERR; - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/fork.c b/examples/redis-unstable/tests/modules/fork.c deleted file mode 100644 index d7a0d15..0000000 --- a/examples/redis-unstable/tests/modules/fork.c +++ /dev/null @@ -1,96 +0,0 @@ - -/* define macros for having usleep */ -#define _BSD_SOURCE -#define _DEFAULT_SOURCE - -#include "redismodule.h" -#include <string.h> -#include <assert.h> -#include <unistd.h> - -#define UNUSED(V) ((void) V) - -int child_pid = -1; -int exitted_with_code = -1; - -void done_handler(int exitcode, int bysignal, void *user_data) { - child_pid = -1; - exitted_with_code = exitcode; - assert(user_data==(void*)0xdeadbeef); - UNUSED(bysignal); -} - -int fork_create(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - long long code_to_exit_with; - long long usleep_us; - if (argc != 3) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - if(!RMAPI_FUNC_SUPPORTED(RedisModule_Fork)){ - RedisModule_ReplyWithError(ctx, "Fork api is not supported in the current redis version"); - return REDISMODULE_OK; - } - - RedisModule_StringToLongLong(argv[1], &code_to_exit_with); - RedisModule_StringToLongLong(argv[2], &usleep_us); - exitted_with_code = -1; - int fork_child_pid = RedisModule_Fork(done_handler, (void*)0xdeadbeef); - if (fork_child_pid < 0) { - RedisModule_ReplyWithError(ctx, "Fork failed"); - return REDISMODULE_OK; - } else if (fork_child_pid > 0) { - /* parent */ - child_pid = fork_child_pid; - RedisModule_ReplyWithLongLong(ctx, child_pid); - return REDISMODULE_OK; - } - - /* child */ - RedisModule_Log(ctx, "notice", "fork child started"); - usleep(usleep_us); - RedisModule_Log(ctx, "notice", "fork child exiting"); - RedisModule_ExitFromChild(code_to_exit_with); - /* unreachable */ - return 0; -} - -int fork_exitcode(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - UNUSED(argv); - UNUSED(argc); - RedisModule_ReplyWithLongLong(ctx, exitted_with_code); - return REDISMODULE_OK; -} - -int fork_kill(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - UNUSED(argv); - UNUSED(argc); - if (RedisModule_KillForkChild(child_pid) != REDISMODULE_OK) - RedisModule_ReplyWithError(ctx, "KillForkChild failed"); - else - RedisModule_ReplyWithLongLong(ctx, 1); - child_pid = -1; - return REDISMODULE_OK; -} - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - UNUSED(argv); - UNUSED(argc); - if (RedisModule_Init(ctx,"fork",1,REDISMODULE_APIVER_1)== REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"fork.create", fork_create,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"fork.exitcode", fork_exitcode,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"fork.kill", fork_kill,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/getchannels.c b/examples/redis-unstable/tests/modules/getchannels.c deleted file mode 100644 index 330531d..0000000 --- a/examples/redis-unstable/tests/modules/getchannels.c +++ /dev/null @@ -1,69 +0,0 @@ -#include "redismodule.h" -#include <strings.h> -#include <assert.h> -#include <unistd.h> -#include <errno.h> - -/* A sample with declarable channels, that are used to validate against ACLs */ -int getChannels_subscribe(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if ((argc - 1) % 3 != 0) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - char *err = NULL; - - /* getchannels.command [[subscribe|unsubscribe|publish] [pattern|literal] <channel> ...] - * This command marks the given channel is accessed based on the - * provided modifiers. */ - for (int i = 1; i < argc; i += 3) { - const char *operation = RedisModule_StringPtrLen(argv[i], NULL); - const char *type = RedisModule_StringPtrLen(argv[i+1], NULL); - int flags = 0; - - if (!strcasecmp(operation, "subscribe")) { - flags |= REDISMODULE_CMD_CHANNEL_SUBSCRIBE; - } else if (!strcasecmp(operation, "unsubscribe")) { - flags |= REDISMODULE_CMD_CHANNEL_UNSUBSCRIBE; - } else if (!strcasecmp(operation, "publish")) { - flags |= REDISMODULE_CMD_CHANNEL_PUBLISH; - } else { - err = "Invalid channel operation"; - break; - } - - if (!strcasecmp(type, "literal")) { - /* No op */ - } else if (!strcasecmp(type, "pattern")) { - flags |= REDISMODULE_CMD_CHANNEL_PATTERN; - } else { - err = "Invalid channel type"; - break; - } - if (RedisModule_IsChannelsPositionRequest(ctx)) { - RedisModule_ChannelAtPosWithFlags(ctx, i+2, flags); - } - } - - if (!RedisModule_IsChannelsPositionRequest(ctx)) { - if (err) { - RedisModule_ReplyWithError(ctx, err); - } else { - /* Normal implementation would go here, but for tests just return okay */ - 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, "getchannels", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "getchannels.command", getChannels_subscribe, "getchannels-api", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/getkeys.c b/examples/redis-unstable/tests/modules/getkeys.c deleted file mode 100644 index cee3b3e..0000000 --- a/examples/redis-unstable/tests/modules/getkeys.c +++ /dev/null @@ -1,178 +0,0 @@ - -#include "redismodule.h" -#include <strings.h> -#include <assert.h> -#include <unistd.h> -#include <errno.h> - -#define UNUSED(V) ((void) V) - -/* A sample movable keys command that returns a list of all - * arguments that follow a KEY argument, i.e. - */ -int getkeys_command(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - int i; - int count = 0; - - /* Handle getkeys-api introspection */ - if (RedisModule_IsKeysPositionRequest(ctx)) { - for (i = 0; i < argc; i++) { - size_t len; - const char *str = RedisModule_StringPtrLen(argv[i], &len); - - if (len == 3 && !strncasecmp(str, "key", 3) && i + 1 < argc) - RedisModule_KeyAtPos(ctx, i + 1); - } - - return REDISMODULE_OK; - } - - /* Handle real command invocation */ - RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_LEN); - for (i = 0; i < argc; i++) { - size_t len; - const char *str = RedisModule_StringPtrLen(argv[i], &len); - - if (len == 3 && !strncasecmp(str, "key", 3) && i + 1 < argc) { - RedisModule_ReplyWithString(ctx, argv[i+1]); - count++; - } - } - RedisModule_ReplySetArrayLength(ctx, count); - - return REDISMODULE_OK; -} - -int getkeys_command_with_flags(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - int i; - int count = 0; - - /* Handle getkeys-api introspection */ - if (RedisModule_IsKeysPositionRequest(ctx)) { - for (i = 0; i < argc; i++) { - size_t len; - const char *str = RedisModule_StringPtrLen(argv[i], &len); - - if (len == 3 && !strncasecmp(str, "key", 3) && i + 1 < argc) - RedisModule_KeyAtPosWithFlags(ctx, i + 1, REDISMODULE_CMD_KEY_RO | REDISMODULE_CMD_KEY_ACCESS); - } - - return REDISMODULE_OK; - } - - /* Handle real command invocation */ - RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_LEN); - for (i = 0; i < argc; i++) { - size_t len; - const char *str = RedisModule_StringPtrLen(argv[i], &len); - - if (len == 3 && !strncasecmp(str, "key", 3) && i + 1 < argc) { - RedisModule_ReplyWithString(ctx, argv[i+1]); - count++; - } - } - RedisModule_ReplySetArrayLength(ctx, count); - - return REDISMODULE_OK; -} - -int getkeys_fixed(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - int i; - - RedisModule_ReplyWithArray(ctx, argc - 1); - for (i = 1; i < argc; i++) { - RedisModule_ReplyWithString(ctx, argv[i]); - } - return REDISMODULE_OK; -} - -/* Introspect a command using RM_GetCommandKeys() and returns the list - * of keys. Essentially this is COMMAND GETKEYS implemented in a module. - * INTROSPECT <with-flags> <cmd> <args> - */ -int getkeys_introspect(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - long long with_flags = 0; - - if (argc < 4) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - if (RedisModule_StringToLongLong(argv[1],&with_flags) != REDISMODULE_OK) - return RedisModule_ReplyWithError(ctx,"ERR invalid integer"); - - int num_keys, *keyflags = NULL; - int *keyidx = RedisModule_GetCommandKeysWithFlags(ctx, &argv[2], argc - 2, &num_keys, with_flags ? &keyflags : NULL); - - if (!keyidx) { - if (!errno) - RedisModule_ReplyWithEmptyArray(ctx); - else { - char err[100]; - switch (errno) { - case ENOENT: - RedisModule_ReplyWithError(ctx, "ERR ENOENT"); - break; - case EINVAL: - RedisModule_ReplyWithError(ctx, "ERR EINVAL"); - break; - default: - snprintf(err, sizeof(err) - 1, "ERR errno=%d", errno); - RedisModule_ReplyWithError(ctx, err); - break; - } - } - } else { - int i; - - RedisModule_ReplyWithArray(ctx, num_keys); - for (i = 0; i < num_keys; i++) { - if (!with_flags) { - RedisModule_ReplyWithString(ctx, argv[2 + keyidx[i]]); - continue; - } - RedisModule_ReplyWithArray(ctx, 2); - RedisModule_ReplyWithString(ctx, argv[2 + keyidx[i]]); - char* sflags = ""; - if (keyflags[i] & REDISMODULE_CMD_KEY_RO) - sflags = "RO"; - else if (keyflags[i] & REDISMODULE_CMD_KEY_RW) - sflags = "RW"; - else if (keyflags[i] & REDISMODULE_CMD_KEY_OW) - sflags = "OW"; - else if (keyflags[i] & REDISMODULE_CMD_KEY_RM) - sflags = "RM"; - RedisModule_ReplyWithCString(ctx, sflags); - } - - RedisModule_Free(keyidx); - RedisModule_Free(keyflags); - } - - return REDISMODULE_OK; -} - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - UNUSED(argv); - UNUSED(argc); - if (RedisModule_Init(ctx,"getkeys",1,REDISMODULE_APIVER_1)== REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"getkeys.command", getkeys_command,"getkeys-api",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"getkeys.command_with_flags", getkeys_command_with_flags,"getkeys-api",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"getkeys.fixed", getkeys_fixed,"",2,4,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"getkeys.introspect", getkeys_introspect,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/hash.c b/examples/redis-unstable/tests/modules/hash.c deleted file mode 100644 index 462c21e..0000000 --- a/examples/redis-unstable/tests/modules/hash.c +++ /dev/null @@ -1,244 +0,0 @@ -#include "redismodule.h" -#include <strings.h> -#include <errno.h> -#include <stdlib.h> - -#define UNUSED(x) (void)(x) - -/* If a string is ":deleted:", the special value for deleted hash fields is - * returned; otherwise the input string is returned. */ -static RedisModuleString *value_or_delete(RedisModuleString *s) { - if (!strcasecmp(RedisModule_StringPtrLen(s, NULL), ":delete:")) - return REDISMODULE_HASH_DELETE; - else - return s; -} - -/* HASH.SET key flags field1 value1 [field2 value2 ..] - * - * Sets 1-4 fields. Returns the same as RedisModule_HashSet(). - * Flags is a string of "nxa" where n = NX, x = XX, a = COUNT_ALL. - * To delete a field, use the value ":delete:". - */ -int hash_set(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc < 5 || argc % 2 == 0 || argc > 11) - return RedisModule_WrongArity(ctx); - - RedisModule_AutoMemory(ctx); - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE); - - size_t flags_len; - const char *flags_str = RedisModule_StringPtrLen(argv[2], &flags_len); - int flags = REDISMODULE_HASH_NONE; - for (size_t i = 0; i < flags_len; i++) { - switch (flags_str[i]) { - case 'n': flags |= REDISMODULE_HASH_NX; break; - case 'x': flags |= REDISMODULE_HASH_XX; break; - case 'a': flags |= REDISMODULE_HASH_COUNT_ALL; break; - } - } - - /* Test some varargs. (In real-world, use a loop and set one at a time.) */ - int result; - errno = 0; - if (argc == 5) { - result = RedisModule_HashSet(key, flags, - argv[3], value_or_delete(argv[4]), - NULL); - } else if (argc == 7) { - result = RedisModule_HashSet(key, flags, - argv[3], value_or_delete(argv[4]), - argv[5], value_or_delete(argv[6]), - NULL); - } else if (argc == 9) { - result = RedisModule_HashSet(key, flags, - argv[3], value_or_delete(argv[4]), - argv[5], value_or_delete(argv[6]), - argv[7], value_or_delete(argv[8]), - NULL); - } else if (argc == 11) { - result = RedisModule_HashSet(key, flags, - argv[3], value_or_delete(argv[4]), - argv[5], value_or_delete(argv[6]), - argv[7], value_or_delete(argv[8]), - argv[9], value_or_delete(argv[10]), - NULL); - } else { - return RedisModule_ReplyWithError(ctx, "ERR too many fields"); - } - - /* Check errno */ - if (result == 0) { - if (errno == ENOTSUP) - return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); - else - RedisModule_Assert(errno == ENOENT); - } - - return RedisModule_ReplyWithLongLong(ctx, result); -} - -RedisModuleKey* openKeyWithMode(RedisModuleCtx *ctx, RedisModuleString *keyName, int mode) { - int supportedMode = RedisModule_GetOpenKeyModesAll(); - if (!(supportedMode & REDISMODULE_READ) || ((supportedMode & mode)!=mode)) { - RedisModule_ReplyWithError(ctx, "OpenKey mode is not supported"); - return NULL; - } - - RedisModuleKey *key = RedisModule_OpenKey(ctx, keyName, REDISMODULE_READ | mode); - if (!key) { - RedisModule_ReplyWithError(ctx, "key not found"); - return NULL; - } - - return key; -} - -int test_open_key_subexpired_hget(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc<3) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - RedisModuleKey *key = openKeyWithMode(ctx, argv[1], REDISMODULE_OPEN_KEY_ACCESS_EXPIRED); - if (!key) return REDISMODULE_OK; - - RedisModuleString *value; - RedisModule_HashGet(key,REDISMODULE_HASH_NONE,argv[2],&value,NULL); - - /* return the value */ - if (value) { - RedisModule_ReplyWithString(ctx, value); - RedisModule_FreeString(ctx, value); - } else { - RedisModule_ReplyWithNull(ctx); - } - RedisModule_CloseKey(key); - return REDISMODULE_OK; -} - -int test_open_key_hget_expire(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc<3) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - RedisModuleKey *key = openKeyWithMode(ctx, argv[1], REDISMODULE_OPEN_KEY_ACCESS_EXPIRED); - if (!key) return REDISMODULE_OK; - - mstime_t expireAt; - - /* Let's test here that we get error if using invalid flags combination */ - RedisModule_Assert( - RedisModule_HashGet(key, - REDISMODULE_HASH_EXISTS | - REDISMODULE_HASH_EXPIRE_TIME, - argv[2], &expireAt, NULL) == REDISMODULE_ERR); - - /* Now let's get the expire time */ - RedisModule_HashGet(key, REDISMODULE_HASH_EXPIRE_TIME,argv[2],&expireAt,NULL); - RedisModule_ReplyWithLongLong(ctx, expireAt); - RedisModule_CloseKey(key); - return REDISMODULE_OK; -} - -/* Test variadic function to get two expiration times */ -int test_open_key_hget_two_expire(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc<3) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - RedisModuleKey *key = openKeyWithMode(ctx, argv[1], REDISMODULE_OPEN_KEY_ACCESS_EXPIRED); - if (!key) return REDISMODULE_OK; - - mstime_t expireAt1, expireAt2; - RedisModule_HashGet(key,REDISMODULE_HASH_EXPIRE_TIME,argv[2],&expireAt1,argv[3],&expireAt2,NULL); - - /* return the two expire time */ - RedisModule_ReplyWithArray(ctx, 2); - RedisModule_ReplyWithLongLong(ctx, expireAt1); - RedisModule_ReplyWithLongLong(ctx, expireAt2); - RedisModule_CloseKey(key); - return REDISMODULE_OK; -} - -int test_open_key_hget_min_expire(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc!=2) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - RedisModuleKey *key = openKeyWithMode(ctx, argv[1], REDISMODULE_READ); - if (!key) return REDISMODULE_OK; - - volatile mstime_t minExpire = RedisModule_HashFieldMinExpire(key); - RedisModule_ReplyWithLongLong(ctx, minExpire); - RedisModule_CloseKey(key); - return REDISMODULE_OK; -} - -int numReplies; -void ScanCallback(RedisModuleKey *key, RedisModuleString *field, RedisModuleString *value, void *privdata) { - UNUSED(key); - RedisModuleCtx *ctx = (RedisModuleCtx *)privdata; - - /* Reply with the field and value (or NULL for sets) */ - RedisModule_ReplyWithString(ctx, field); - if (value) { - RedisModule_ReplyWithString(ctx, value); - } else { - RedisModule_ReplyWithCString(ctx, "(null)"); - } - numReplies+=2; -} - -int test_open_key_access_expired_hscan(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc < 2) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - RedisModuleKey *key = openKeyWithMode(ctx, argv[1], REDISMODULE_OPEN_KEY_ACCESS_EXPIRED); - - if (!key) - return RedisModule_ReplyWithError(ctx, "ERR key not exists"); - - /* Verify it is a hash */ - if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_HASH) { - RedisModule_CloseKey(key); - return RedisModule_ReplyWithError(ctx, "ERR key is not a hash"); - } - - /* Scan the hash and reply pairs of key-value */ - RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN); - numReplies = 0; - RedisModuleScanCursor *cursor = RedisModule_ScanCursorCreate(); - while (RedisModule_ScanKey(key, cursor, ScanCallback, ctx)); - RedisModule_ScanCursorDestroy(cursor); - RedisModule_CloseKey(key); - RedisModule_ReplySetArrayLength(ctx, numReplies); - return REDISMODULE_OK; -} - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - if (RedisModule_Init(ctx, "hash", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "hash.set", hash_set, "write", 1, 1, 1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx, "hash.hget_expired", test_open_key_subexpired_hget,"", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx, "hash.hscan_expired", test_open_key_access_expired_hscan,"", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx, "hash.hget_expire", test_open_key_hget_expire,"", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx, "hash.hget_two_expire", test_open_key_hget_two_expire,"", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx, "hash.hget_min_expire", test_open_key_hget_min_expire,"", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/hooks.c b/examples/redis-unstable/tests/modules/hooks.c deleted file mode 100644 index 59c8492..0000000 --- a/examples/redis-unstable/tests/modules/hooks.c +++ /dev/null @@ -1,496 +0,0 @@ -/* This module is used to test the server events hooks API. - * - * ----------------------------------------------------------------------------- - * - * Copyright (c) 2019-Present, Redis Ltd. - * All rights reserved. - * - * Licensed under your choice of (a) the Redis Source Available License 2.0 - * (RSALv2); or (b) the Server Side Public License v1 (SSPLv1); or (c) the - * GNU Affero General Public License v3 (AGPLv3). - */ - -#include "redismodule.h" -#include <stdio.h> -#include <string.h> -#include <strings.h> -#include <assert.h> - -/* We need to store events to be able to test and see what we got, and we can't - * store them in the key-space since that would mess up rdb loading (duplicates) - * and be lost of flushdb. */ -RedisModuleDict *event_log = NULL; -/* stores all the keys on which we got 'removed' event */ -RedisModuleDict *removed_event_log = NULL; -/* stores all the subevent on which we got 'removed' event */ -RedisModuleDict *removed_subevent_type = NULL; -/* stores all the keys on which we got 'removed' event with expiry information */ -RedisModuleDict *removed_expiry_log = NULL; - -typedef struct EventElement { - long count; - RedisModuleString *last_val_string; - long last_val_int; -} EventElement; - -void LogStringEvent(RedisModuleCtx *ctx, const char* keyname, const char* data) { - EventElement *event = RedisModule_DictGetC(event_log, (void*)keyname, strlen(keyname), NULL); - if (!event) { - event = RedisModule_Alloc(sizeof(EventElement)); - memset(event, 0, sizeof(EventElement)); - RedisModule_DictSetC(event_log, (void*)keyname, strlen(keyname), event); - } - if (event->last_val_string) RedisModule_FreeString(ctx, event->last_val_string); - event->last_val_string = RedisModule_CreateString(ctx, data, strlen(data)); - event->count++; -} - -void LogNumericEvent(RedisModuleCtx *ctx, const char* keyname, long data) { - REDISMODULE_NOT_USED(ctx); - EventElement *event = RedisModule_DictGetC(event_log, (void*)keyname, strlen(keyname), NULL); - if (!event) { - event = RedisModule_Alloc(sizeof(EventElement)); - memset(event, 0, sizeof(EventElement)); - RedisModule_DictSetC(event_log, (void*)keyname, strlen(keyname), event); - } - event->last_val_int = data; - event->count++; -} - -void FreeEvent(RedisModuleCtx *ctx, EventElement *event) { - if (event->last_val_string) - RedisModule_FreeString(ctx, event->last_val_string); - RedisModule_Free(event); -} - -int cmdEventCount(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - if (argc != 2){ - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - EventElement *event = RedisModule_DictGet(event_log, argv[1], NULL); - RedisModule_ReplyWithLongLong(ctx, event? event->count: 0); - return REDISMODULE_OK; -} - -int cmdEventLast(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - if (argc != 2){ - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - EventElement *event = RedisModule_DictGet(event_log, argv[1], NULL); - if (event && event->last_val_string) - RedisModule_ReplyWithString(ctx, event->last_val_string); - else if (event) - RedisModule_ReplyWithLongLong(ctx, event->last_val_int); - else - RedisModule_ReplyWithNull(ctx); - return REDISMODULE_OK; -} - -void clearEvents(RedisModuleCtx *ctx) -{ - RedisModuleString *key; - EventElement *event; - RedisModuleDictIter *iter = RedisModule_DictIteratorStart(event_log, "^", NULL); - while((key = RedisModule_DictNext(ctx, iter, (void**)&event)) != NULL) { - event->count = 0; - event->last_val_int = 0; - if (event->last_val_string) RedisModule_FreeString(ctx, event->last_val_string); - event->last_val_string = NULL; - RedisModule_DictDel(event_log, key, NULL); - RedisModule_Free(event); - } - RedisModule_DictIteratorStop(iter); -} - -int cmdEventsClear(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - REDISMODULE_NOT_USED(argc); - REDISMODULE_NOT_USED(argv); - clearEvents(ctx); - return REDISMODULE_OK; -} - -/* Client state change callback. */ -void clientChangeCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data) -{ - REDISMODULE_NOT_USED(e); - - RedisModuleClientInfo *ci = data; - char *keyname = (sub == REDISMODULE_SUBEVENT_CLIENT_CHANGE_CONNECTED) ? - "client-connected" : "client-disconnected"; - LogNumericEvent(ctx, keyname, ci->id); -} - -void flushdbCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data) -{ - REDISMODULE_NOT_USED(e); - - RedisModuleFlushInfo *fi = data; - char *keyname = (sub == REDISMODULE_SUBEVENT_FLUSHDB_START) ? - "flush-start" : "flush-end"; - LogNumericEvent(ctx, keyname, fi->dbnum); -} - -void roleChangeCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data) -{ - REDISMODULE_NOT_USED(e); - REDISMODULE_NOT_USED(data); - - RedisModuleReplicationInfo *ri = data; - char *keyname = (sub == REDISMODULE_EVENT_REPLROLECHANGED_NOW_MASTER) ? - "role-master" : "role-replica"; - LogStringEvent(ctx, keyname, ri->masterhost); -} - -void replicationChangeCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data) -{ - REDISMODULE_NOT_USED(e); - REDISMODULE_NOT_USED(data); - - char *keyname = (sub == REDISMODULE_SUBEVENT_REPLICA_CHANGE_ONLINE) ? - "replica-online" : "replica-offline"; - LogNumericEvent(ctx, keyname, 0); -} - -void rasterLinkChangeCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data) -{ - REDISMODULE_NOT_USED(e); - REDISMODULE_NOT_USED(data); - - char *keyname = (sub == REDISMODULE_SUBEVENT_MASTER_LINK_UP) ? - "masterlink-up" : "masterlink-down"; - LogNumericEvent(ctx, keyname, 0); -} - -void persistenceCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data) -{ - REDISMODULE_NOT_USED(e); - REDISMODULE_NOT_USED(data); - - char *keyname = NULL; - switch (sub) { - case REDISMODULE_SUBEVENT_PERSISTENCE_RDB_START: keyname = "persistence-rdb-start"; break; - case REDISMODULE_SUBEVENT_PERSISTENCE_AOF_START: keyname = "persistence-aof-start"; break; - case REDISMODULE_SUBEVENT_PERSISTENCE_SYNC_AOF_START: keyname = "persistence-syncaof-start"; break; - case REDISMODULE_SUBEVENT_PERSISTENCE_SYNC_RDB_START: keyname = "persistence-syncrdb-start"; break; - case REDISMODULE_SUBEVENT_PERSISTENCE_ENDED: keyname = "persistence-end"; break; - case REDISMODULE_SUBEVENT_PERSISTENCE_FAILED: keyname = "persistence-failed"; break; - } - /* modifying the keyspace from the fork child is not an option, using log instead */ - RedisModule_Log(ctx, "warning", "module-event-%s", keyname); - if (sub == REDISMODULE_SUBEVENT_PERSISTENCE_SYNC_RDB_START || - sub == REDISMODULE_SUBEVENT_PERSISTENCE_SYNC_AOF_START) - { - LogNumericEvent(ctx, keyname, 0); - } -} - -void loadingCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data) -{ - REDISMODULE_NOT_USED(e); - REDISMODULE_NOT_USED(data); - - char *keyname = NULL; - switch (sub) { - case REDISMODULE_SUBEVENT_LOADING_RDB_START: keyname = "loading-rdb-start"; break; - case REDISMODULE_SUBEVENT_LOADING_AOF_START: keyname = "loading-aof-start"; break; - case REDISMODULE_SUBEVENT_LOADING_REPL_START: keyname = "loading-repl-start"; break; - case REDISMODULE_SUBEVENT_LOADING_ENDED: keyname = "loading-end"; break; - case REDISMODULE_SUBEVENT_LOADING_FAILED: keyname = "loading-failed"; break; - } - LogNumericEvent(ctx, keyname, 0); -} - -void loadingProgressCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data) -{ - REDISMODULE_NOT_USED(e); - - RedisModuleLoadingProgress *ei = data; - char *keyname = (sub == REDISMODULE_SUBEVENT_LOADING_PROGRESS_RDB) ? - "loading-progress-rdb" : "loading-progress-aof"; - LogNumericEvent(ctx, keyname, ei->progress); -} - -void shutdownCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data) -{ - REDISMODULE_NOT_USED(e); - REDISMODULE_NOT_USED(data); - REDISMODULE_NOT_USED(sub); - - RedisModule_Log(ctx, "warning", "module-event-%s", "shutdown"); -} - -void cronLoopCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data) -{ - REDISMODULE_NOT_USED(e); - REDISMODULE_NOT_USED(sub); - - RedisModuleCronLoop *ei = data; - LogNumericEvent(ctx, "cron-loop", ei->hz); -} - -void moduleChangeCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data) -{ - REDISMODULE_NOT_USED(e); - - RedisModuleModuleChange *ei = data; - char *keyname = (sub == REDISMODULE_SUBEVENT_MODULE_LOADED) ? - "module-loaded" : "module-unloaded"; - LogStringEvent(ctx, keyname, ei->module_name); -} - -void swapDbCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data) -{ - REDISMODULE_NOT_USED(e); - REDISMODULE_NOT_USED(sub); - - RedisModuleSwapDbInfo *ei = data; - LogNumericEvent(ctx, "swapdb-first", ei->dbnum_first); - LogNumericEvent(ctx, "swapdb-second", ei->dbnum_second); -} - -void configChangeCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data) -{ - REDISMODULE_NOT_USED(e); - if (sub != REDISMODULE_SUBEVENT_CONFIG_CHANGE) { - return; - } - - RedisModuleConfigChangeV1 *ei = data; - LogNumericEvent(ctx, "config-change-count", ei->num_changes); - LogStringEvent(ctx, "config-change-first", ei->config_names[0]); -} - -void keyInfoCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data) -{ - REDISMODULE_NOT_USED(e); - - RedisModuleKeyInfoV1 *ei = data; - RedisModuleKey *kp = ei->key; - RedisModuleString *key = (RedisModuleString *) RedisModule_GetKeyNameFromModuleKey(kp); - const char *keyname = RedisModule_StringPtrLen(key, NULL); - RedisModuleString *event_keyname = RedisModule_CreateStringPrintf(ctx, "key-info-%s", keyname); - LogStringEvent(ctx, RedisModule_StringPtrLen(event_keyname, NULL), keyname); - RedisModule_FreeString(ctx, event_keyname); - - /* Despite getting a key object from the callback, we also try to re-open it - * to make sure the callback is called before it is actually removed from the keyspace. */ - RedisModuleKey *kp_open = RedisModule_OpenKey(ctx, key, REDISMODULE_READ); - assert(RedisModule_ValueLength(kp) == RedisModule_ValueLength(kp_open)); - RedisModule_CloseKey(kp_open); - - /* We also try to RM_Call a command that accesses that key, also to make sure it's still in the keyspace. */ - char *size_command = NULL; - int key_type = RedisModule_KeyType(kp); - if (key_type == REDISMODULE_KEYTYPE_STRING) { - size_command = "STRLEN"; - } else if (key_type == REDISMODULE_KEYTYPE_LIST) { - size_command = "LLEN"; - } else if (key_type == REDISMODULE_KEYTYPE_HASH) { - size_command = "HLEN"; - } else if (key_type == REDISMODULE_KEYTYPE_SET) { - size_command = "SCARD"; - } else if (key_type == REDISMODULE_KEYTYPE_ZSET) { - size_command = "ZCARD"; - } else if (key_type == REDISMODULE_KEYTYPE_STREAM) { - size_command = "XLEN"; - } - if (size_command != NULL) { - RedisModuleCallReply *reply = RedisModule_Call(ctx, size_command, "s", key); - assert(reply != NULL); - assert(RedisModule_ValueLength(kp) == (size_t) RedisModule_CallReplyInteger(reply)); - RedisModule_FreeCallReply(reply); - } - - /* Now use the key object we got from the callback for various validations. */ - RedisModuleString *prev = RedisModule_DictGetC(removed_event_log, (void*)keyname, strlen(keyname), NULL); - /* We keep object length */ - RedisModuleString *v = RedisModule_CreateStringPrintf(ctx, "%zd", RedisModule_ValueLength(kp)); - /* For string type, we keep value instead of length */ - if (RedisModule_KeyType(kp) == REDISMODULE_KEYTYPE_STRING) { - RedisModule_FreeString(ctx, v); - size_t len; - /* We need to access the string value with RedisModule_StringDMA. - * RedisModule_StringDMA may call dbUnshareStringValue to free the origin object, - * so we also can test it. */ - char *s = RedisModule_StringDMA(kp, &len, REDISMODULE_READ); - v = RedisModule_CreateString(ctx, s, len); - } - RedisModule_DictReplaceC(removed_event_log, (void*)keyname, strlen(keyname), v); - if (prev != NULL) { - RedisModule_FreeString(ctx, prev); - } - - const char *subevent = "deleted"; - if (sub == REDISMODULE_SUBEVENT_KEY_EXPIRED) { - subevent = "expired"; - } else if (sub == REDISMODULE_SUBEVENT_KEY_EVICTED) { - subevent = "evicted"; - } else if (sub == REDISMODULE_SUBEVENT_KEY_OVERWRITTEN) { - subevent = "overwritten"; - } - RedisModule_DictReplaceC(removed_subevent_type, (void*)keyname, strlen(keyname), (void *)subevent); - - RedisModuleString *prevexpire = RedisModule_DictGetC(removed_expiry_log, (void*)keyname, strlen(keyname), NULL); - RedisModuleString *expire = RedisModule_CreateStringPrintf(ctx, "%lld", RedisModule_GetAbsExpire(kp)); - RedisModule_DictReplaceC(removed_expiry_log, (void*)keyname, strlen(keyname), (void *)expire); - if (prevexpire != NULL) { - RedisModule_FreeString(ctx, prevexpire); - } -} - -static int cmdIsKeyRemoved(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){ - if(argc != 2){ - return RedisModule_WrongArity(ctx); - } - - const char *key = RedisModule_StringPtrLen(argv[1], NULL); - - RedisModuleString *value = RedisModule_DictGetC(removed_event_log, (void*)key, strlen(key), NULL); - - if (value == NULL) { - return RedisModule_ReplyWithError(ctx, "ERR Key was not removed"); - } - - const char *subevent = RedisModule_DictGetC(removed_subevent_type, (void*)key, strlen(key), NULL); - RedisModule_ReplyWithArray(ctx, 2); - RedisModule_ReplyWithString(ctx, value); - RedisModule_ReplyWithSimpleString(ctx, subevent); - - return REDISMODULE_OK; -} - -static int cmdKeyExpiry(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){ - if(argc != 2){ - return RedisModule_WrongArity(ctx); - } - - const char* key = RedisModule_StringPtrLen(argv[1], NULL); - RedisModuleString *expire = RedisModule_DictGetC(removed_expiry_log, (void*)key, strlen(key), NULL); - if (expire == NULL) { - return RedisModule_ReplyWithError(ctx, "ERR Key was not removed"); - } - RedisModule_ReplyWithString(ctx, expire); - return REDISMODULE_OK; -} - -/* This function must be present on each Redis module. It is used in order to - * register the commands into the Redis server. */ -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { -#define VerifySubEventSupported(e, s) \ - if (!RedisModule_IsSubEventSupported(e, s)) { \ - return REDISMODULE_ERR; \ - } - - if (RedisModule_Init(ctx,"testhook",1,REDISMODULE_APIVER_1) - == REDISMODULE_ERR) return REDISMODULE_ERR; - - /* Example on how to check if a server sub event is supported */ - if (!RedisModule_IsSubEventSupported(RedisModuleEvent_ReplicationRoleChanged, REDISMODULE_EVENT_REPLROLECHANGED_NOW_MASTER)) { - return REDISMODULE_ERR; - } - - /* replication related hooks */ - RedisModule_SubscribeToServerEvent(ctx, - RedisModuleEvent_ReplicationRoleChanged, roleChangeCallback); - RedisModule_SubscribeToServerEvent(ctx, - RedisModuleEvent_ReplicaChange, replicationChangeCallback); - RedisModule_SubscribeToServerEvent(ctx, - RedisModuleEvent_MasterLinkChange, rasterLinkChangeCallback); - - /* persistence related hooks */ - RedisModule_SubscribeToServerEvent(ctx, - RedisModuleEvent_Persistence, persistenceCallback); - RedisModule_SubscribeToServerEvent(ctx, - RedisModuleEvent_Loading, loadingCallback); - RedisModule_SubscribeToServerEvent(ctx, - RedisModuleEvent_LoadingProgress, loadingProgressCallback); - - /* other hooks */ - RedisModule_SubscribeToServerEvent(ctx, - RedisModuleEvent_ClientChange, clientChangeCallback); - RedisModule_SubscribeToServerEvent(ctx, - RedisModuleEvent_FlushDB, flushdbCallback); - RedisModule_SubscribeToServerEvent(ctx, - RedisModuleEvent_Shutdown, shutdownCallback); - RedisModule_SubscribeToServerEvent(ctx, - RedisModuleEvent_CronLoop, cronLoopCallback); - - RedisModule_SubscribeToServerEvent(ctx, - RedisModuleEvent_ModuleChange, moduleChangeCallback); - RedisModule_SubscribeToServerEvent(ctx, - RedisModuleEvent_SwapDB, swapDbCallback); - - RedisModule_SubscribeToServerEvent(ctx, - RedisModuleEvent_Config, configChangeCallback); - - RedisModule_SubscribeToServerEvent(ctx, - RedisModuleEvent_Key, keyInfoCallback); - - event_log = RedisModule_CreateDict(ctx); - removed_event_log = RedisModule_CreateDict(ctx); - removed_subevent_type = RedisModule_CreateDict(ctx); - removed_expiry_log = RedisModule_CreateDict(ctx); - - if (RedisModule_CreateCommand(ctx,"hooks.event_count", cmdEventCount,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx,"hooks.event_last", cmdEventLast,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx,"hooks.clear", cmdEventsClear,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx,"hooks.is_key_removed", cmdIsKeyRemoved,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx,"hooks.pexpireat", cmdKeyExpiry,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (argc == 1) { - const char *ptr = RedisModule_StringPtrLen(argv[0], NULL); - if (!strcasecmp(ptr, "noload")) { - /* This is a hint that we return ERR at the last moment of OnLoad. */ - RedisModule_FreeDict(ctx, event_log); - RedisModule_FreeDict(ctx, removed_event_log); - RedisModule_FreeDict(ctx, removed_subevent_type); - RedisModule_FreeDict(ctx, removed_expiry_log); - return REDISMODULE_ERR; - } - } - - return REDISMODULE_OK; -} - -int RedisModule_OnUnload(RedisModuleCtx *ctx) { - clearEvents(ctx); - RedisModule_FreeDict(ctx, event_log); - event_log = NULL; - - RedisModuleDictIter *iter = RedisModule_DictIteratorStartC(removed_event_log, "^", NULL, 0); - char* key; - size_t keyLen; - RedisModuleString* val; - while((key = RedisModule_DictNextC(iter, &keyLen, (void**)&val))){ - RedisModule_FreeString(ctx, val); - } - RedisModule_FreeDict(ctx, removed_event_log); - RedisModule_DictIteratorStop(iter); - removed_event_log = NULL; - - RedisModule_FreeDict(ctx, removed_subevent_type); - removed_subevent_type = NULL; - - iter = RedisModule_DictIteratorStartC(removed_expiry_log, "^", NULL, 0); - while((key = RedisModule_DictNextC(iter, &keyLen, (void**)&val))){ - RedisModule_FreeString(ctx, val); - } - RedisModule_FreeDict(ctx, removed_expiry_log); - RedisModule_DictIteratorStop(iter); - removed_expiry_log = NULL; - - return REDISMODULE_OK; -} - diff --git a/examples/redis-unstable/tests/modules/infotest.c b/examples/redis-unstable/tests/modules/infotest.c deleted file mode 100644 index b93a0c4..0000000 --- a/examples/redis-unstable/tests/modules/infotest.c +++ /dev/null @@ -1,119 +0,0 @@ -#include "redismodule.h" - -#include <string.h> - -void InfoFunc(RedisModuleInfoCtx *ctx, int for_crash_report) { - RedisModule_InfoAddSection(ctx, ""); - RedisModule_InfoAddFieldLongLong(ctx, "global", -2); - RedisModule_InfoAddFieldULongLong(ctx, "uglobal", (unsigned long long)-2); - - RedisModule_InfoAddSection(ctx, "Spanish"); - RedisModule_InfoAddFieldCString(ctx, "uno", "one"); - RedisModule_InfoAddFieldLongLong(ctx, "dos", 2); - - RedisModule_InfoAddSection(ctx, "Italian"); - RedisModule_InfoAddFieldLongLong(ctx, "due", 2); - RedisModule_InfoAddFieldDouble(ctx, "tre", 3.3); - - RedisModule_InfoAddSection(ctx, "keyspace"); - RedisModule_InfoBeginDictField(ctx, "db0"); - RedisModule_InfoAddFieldLongLong(ctx, "keys", 3); - RedisModule_InfoAddFieldLongLong(ctx, "expires", 1); - RedisModule_InfoEndDictField(ctx); - - RedisModule_InfoAddSection(ctx, "unsafe"); - RedisModule_InfoBeginDictField(ctx, "unsafe:field"); - RedisModule_InfoAddFieldLongLong(ctx, "value", 1); - RedisModule_InfoEndDictField(ctx); - - if (for_crash_report) { - RedisModule_InfoAddSection(ctx, "Klingon"); - RedisModule_InfoAddFieldCString(ctx, "one", "wa'"); - RedisModule_InfoAddFieldCString(ctx, "two", "cha'"); - RedisModule_InfoAddFieldCString(ctx, "three", "wej"); - } - -} - -int info_get(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, char field_type) -{ - if (argc != 3 && argc != 4) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - int err = REDISMODULE_OK; - const char *section, *field; - section = RedisModule_StringPtrLen(argv[1], NULL); - field = RedisModule_StringPtrLen(argv[2], NULL); - RedisModuleServerInfoData *info = RedisModule_GetServerInfo(ctx, section); - if (field_type=='i') { - long long ll = RedisModule_ServerInfoGetFieldSigned(info, field, &err); - if (err==REDISMODULE_OK) - RedisModule_ReplyWithLongLong(ctx, ll); - } else if (field_type=='u') { - unsigned long long ll = (unsigned long long)RedisModule_ServerInfoGetFieldUnsigned(info, field, &err); - if (err==REDISMODULE_OK) - RedisModule_ReplyWithLongLong(ctx, ll); - } else if (field_type=='d') { - double d = RedisModule_ServerInfoGetFieldDouble(info, field, &err); - if (err==REDISMODULE_OK) - RedisModule_ReplyWithDouble(ctx, d); - } else if (field_type=='c') { - const char *str = RedisModule_ServerInfoGetFieldC(info, field); - if (str) - RedisModule_ReplyWithCString(ctx, str); - } else { - RedisModuleString *str = RedisModule_ServerInfoGetField(ctx, info, field); - if (str) { - RedisModule_ReplyWithString(ctx, str); - RedisModule_FreeString(ctx, str); - } else - err=REDISMODULE_ERR; - } - if (err!=REDISMODULE_OK) - RedisModule_ReplyWithError(ctx, "not found"); - RedisModule_FreeServerInfo(ctx, info); - return REDISMODULE_OK; -} - -int info_gets(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - return info_get(ctx, argv, argc, 's'); -} - -int info_getc(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - return info_get(ctx, argv, argc, 'c'); -} - -int info_geti(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - return info_get(ctx, argv, argc, 'i'); -} - -int info_getu(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - return info_get(ctx, argv, argc, 'u'); -} - -int info_getd(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - return info_get(ctx, argv, argc, 'd'); -} - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - if (RedisModule_Init(ctx,"infotest",1,REDISMODULE_APIVER_1) - == REDISMODULE_ERR) return REDISMODULE_ERR; - - if (RedisModule_RegisterInfoFunc(ctx, InfoFunc) == REDISMODULE_ERR) return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"info.gets", info_gets,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx,"info.getc", info_getc,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx,"info.geti", info_geti,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx,"info.getu", info_getu,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx,"info.getd", info_getd,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/internalsecret.c b/examples/redis-unstable/tests/modules/internalsecret.c deleted file mode 100644 index 043089c..0000000 --- a/examples/redis-unstable/tests/modules/internalsecret.c +++ /dev/null @@ -1,154 +0,0 @@ -#include "redismodule.h" -#include <errno.h> - -int InternalAuth_GetInternalSecret(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - /* NOTE: The internal secret SHOULD NOT be exposed by any module. This is - done for testing purposes only. */ - size_t len; - const char *secret = RedisModule_GetInternalSecret(ctx, &len); - if(secret) { - RedisModule_ReplyWithStringBuffer(ctx, secret, len); - } else { - RedisModule_ReplyWithError(ctx, "ERR no internal secret available"); - } - return REDISMODULE_OK; -} - -int InternalAuth_InternalCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModule_ReplyWithSimpleString(ctx, "OK"); - return REDISMODULE_OK; -} - -typedef enum { - RM_CALL_REGULAR = 0, - RM_CALL_WITHUSER = 1, - RM_CALL_WITHDETACHEDCLIENT = 2, - RM_CALL_REPLICATED = 3 -} RMCallMode; - -int call_rm_call(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, RMCallMode mode) { - if(argc < 2){ - return RedisModule_WrongArity(ctx); - } - RedisModuleCallReply *rep = NULL; - RedisModuleCtx *detached_ctx = NULL; - const char* cmd = RedisModule_StringPtrLen(argv[1], NULL); - - switch (mode) { - case RM_CALL_REGULAR: - // Regular call, with the unrestricted user. - rep = RedisModule_Call(ctx, cmd, "vE", argv + 2, (size_t)argc - 2); - break; - case RM_CALL_WITHUSER: - // Simply call the command with the current client. - rep = RedisModule_Call(ctx, cmd, "vCE", argv + 2, (size_t)argc - 2); - break; - case RM_CALL_WITHDETACHEDCLIENT: - // Use a context created with the thread-safe-context API - detached_ctx = RedisModule_GetThreadSafeContext(NULL); - if(!detached_ctx){ - RedisModule_ReplyWithError(ctx, "ERR failed to create detached context"); - return REDISMODULE_ERR; - } - // Dispatch the command with the detached context - rep = RedisModule_Call(detached_ctx, cmd, "vCE", argv + 2, (size_t)argc - 2); - break; - case RM_CALL_REPLICATED: - rep = RedisModule_Call(ctx, cmd, "vE", argv + 2, (size_t)argc - 2); - } - - if(!rep) { - char err[100]; - switch (errno) { - case EACCES: - RedisModule_ReplyWithError(ctx, "ERR NOPERM"); - break; - case ENOENT: - RedisModule_ReplyWithError(ctx, "ERR unknown command"); - break; - default: - snprintf(err, sizeof(err) - 1, "ERR errno=%d", errno); - RedisModule_ReplyWithError(ctx, err); - break; - } - } else { - RedisModule_ReplyWithCallReply(ctx, rep); - RedisModule_FreeCallReply(rep); - if (mode == RM_CALL_REPLICATED) - RedisModule_ReplicateVerbatim(ctx); - } - - if (mode == RM_CALL_WITHDETACHEDCLIENT) { - RedisModule_FreeThreadSafeContext(detached_ctx); - } - - return REDISMODULE_OK; -} - -int internal_rmcall(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - return call_rm_call(ctx, argv, argc, RM_CALL_REGULAR); -} - -int noninternal_rmcall(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - return call_rm_call(ctx, argv, argc, RM_CALL_REGULAR); -} - -int noninternal_rmcall_withuser(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - return call_rm_call(ctx, argv, argc, RM_CALL_WITHUSER); -} - -int noninternal_rmcall_detachedcontext_withuser(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - return call_rm_call(ctx, argv, argc, RM_CALL_WITHDETACHEDCLIENT); -} - -int internal_rmcall_replicated(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - return call_rm_call(ctx, argv, argc, RM_CALL_REPLICATED); -} - -/* This function must be present on each Redis module. It is used in order to - * register the commands into the Redis server. */ -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - if (RedisModule_Init(ctx,"testinternalsecret",1,REDISMODULE_APIVER_1) - == REDISMODULE_ERR) return REDISMODULE_ERR; - - /* WARNING: A module should NEVER expose the internal secret - this is for - * testing purposes only. */ - if (RedisModule_CreateCommand(ctx,"internalauth.getinternalsecret", - InternalAuth_GetInternalSecret,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"internalauth.internalcommand", - InternalAuth_InternalCommand,"internal",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"internalauth.internal_rmcall", - internal_rmcall,"write internal",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"internalauth.noninternal_rmcall", - noninternal_rmcall,"write",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"internalauth.noninternal_rmcall_withuser", - noninternal_rmcall_withuser,"write",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"internalauth.noninternal_rmcall_detachedcontext_withuser", - noninternal_rmcall_detachedcontext_withuser,"write",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"internalauth.internal_rmcall_replicated", - internal_rmcall_replicated,"write internal",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/keyspace_events.c b/examples/redis-unstable/tests/modules/keyspace_events.c deleted file mode 100644 index 146261f..0000000 --- a/examples/redis-unstable/tests/modules/keyspace_events.c +++ /dev/null @@ -1,479 +0,0 @@ -/* This module is used to test the server keyspace events API. - * - * ----------------------------------------------------------------------------- - * - * Copyright (c) 2020-Present, Redis Ltd. - * All rights reserved. - * - * Licensed under your choice of (a) the Redis Source Available License 2.0 - * (RSALv2); or (b) the Server Side Public License v1 (SSPLv1); or (c) the - * GNU Affero General Public License v3 (AGPLv3). - */ - -#define _BSD_SOURCE -#define _DEFAULT_SOURCE /* For usleep */ - -#include "redismodule.h" -#include <stdio.h> -#include <string.h> -#include <strings.h> -#include <unistd.h> - -ustime_t cached_time = 0; - -/** stores all the keys on which we got 'loaded' keyspace notification **/ -RedisModuleDict *loaded_event_log = NULL; -/** stores all the keys on which we got 'module' keyspace notification **/ -RedisModuleDict *module_event_log = NULL; - -/** Counts how many deleted KSN we got on keys with a prefix of "count_dels_" **/ -static size_t dels = 0; - -static int KeySpace_NotificationLoaded(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key){ - REDISMODULE_NOT_USED(ctx); - REDISMODULE_NOT_USED(type); - - if(strcmp(event, "loaded") == 0){ - const char* keyName = RedisModule_StringPtrLen(key, NULL); - int nokey; - RedisModule_DictGetC(loaded_event_log, (void*)keyName, strlen(keyName), &nokey); - if(nokey){ - RedisModule_DictSetC(loaded_event_log, (void*)keyName, strlen(keyName), RedisModule_HoldString(ctx, key)); - } - } - - return REDISMODULE_OK; -} - -static long long callback_call_count = 0; -static int KeySpace_NotificationGeneric(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key) { - REDISMODULE_NOT_USED(type); - callback_call_count++; - const char *key_str = RedisModule_StringPtrLen(key, NULL); - if (strncmp(key_str, "count_dels_", 11) == 0 && strcmp(event, "del") == 0) { - if (RedisModule_GetContextFlags(ctx) & REDISMODULE_CTX_FLAGS_MASTER) { - dels++; - RedisModule_Replicate(ctx, "keyspace.incr_dels", ""); - } - return REDISMODULE_OK; - } - if (cached_time) { - RedisModule_Assert(cached_time == RedisModule_CachedMicroseconds()); - usleep(1); - RedisModule_Assert(cached_time != RedisModule_Microseconds()); - } - - if (strcmp(event, "del") == 0) { - RedisModuleString *copykey = RedisModule_CreateStringPrintf(ctx, "%s_copy", RedisModule_StringPtrLen(key, NULL)); - RedisModuleCallReply* rep = RedisModule_Call(ctx, "DEL", "s!", copykey); - RedisModule_FreeString(ctx, copykey); - RedisModule_FreeCallReply(rep); - - int ctx_flags = RedisModule_GetContextFlags(ctx); - if (ctx_flags & REDISMODULE_CTX_FLAGS_LUA) { - RedisModuleCallReply* rep = RedisModule_Call(ctx, "INCR", "c", "lua"); - RedisModule_FreeCallReply(rep); - } - if (ctx_flags & REDISMODULE_CTX_FLAGS_MULTI) { - RedisModuleCallReply* rep = RedisModule_Call(ctx, "INCR", "c", "multi"); - RedisModule_FreeCallReply(rep); - } - } - - return REDISMODULE_OK; -} - -static int KeySpace_NotificationExpired(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key) { - REDISMODULE_NOT_USED(type); - REDISMODULE_NOT_USED(event); - REDISMODULE_NOT_USED(key); - - RedisModuleCallReply* rep = RedisModule_Call(ctx, "INCR", "c!", "testkeyspace:expired"); - RedisModule_FreeCallReply(rep); - - return REDISMODULE_OK; -} - -/* This key miss notification handler is performing a write command inside the notification callback. - * Notice, it is discourage and currently wrong to perform a write command inside key miss event. - * It can cause read commands to be replicated to the replica/aof. This test is here temporary (for coverage and - * verification that it's not crashing). */ -static int KeySpace_NotificationModuleKeyMiss(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key) { - REDISMODULE_NOT_USED(type); - REDISMODULE_NOT_USED(event); - REDISMODULE_NOT_USED(key); - - int flags = RedisModule_GetContextFlags(ctx); - if (!(flags & REDISMODULE_CTX_FLAGS_MASTER)) { - return REDISMODULE_OK; // ignore the event on replica - } - - RedisModuleCallReply* rep = RedisModule_Call(ctx, "incr", "!c", "missed"); - RedisModule_FreeCallReply(rep); - - return REDISMODULE_OK; -} - -static int KeySpace_NotificationModuleString(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key) { - REDISMODULE_NOT_USED(type); - REDISMODULE_NOT_USED(event); - RedisModuleKey *redis_key = RedisModule_OpenKey(ctx, key, REDISMODULE_READ); - - size_t len = 0; - /* RedisModule_StringDMA could change the data format and cause the old robj to be freed. - * This code verifies that such format change will not cause any crashes.*/ - char *data = RedisModule_StringDMA(redis_key, &len, REDISMODULE_READ); - int res = strncmp(data, "dummy", 5); - REDISMODULE_NOT_USED(res); - - RedisModule_CloseKey(redis_key); - - return REDISMODULE_OK; -} - -static void KeySpace_PostNotificationStringFreePD(void *pd) { - RedisModule_FreeString(NULL, pd); -} - -static void KeySpace_PostNotificationString(RedisModuleCtx *ctx, void *pd) { - REDISMODULE_NOT_USED(ctx); - RedisModuleCallReply* rep = RedisModule_Call(ctx, "incr", "!s", pd); - RedisModule_FreeCallReply(rep); -} - -static int KeySpace_NotificationModuleStringPostNotificationJob(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key) { - REDISMODULE_NOT_USED(ctx); - REDISMODULE_NOT_USED(type); - REDISMODULE_NOT_USED(event); - - const char *key_str = RedisModule_StringPtrLen(key, NULL); - - if (strncmp(key_str, "string1_", 8) != 0) { - return REDISMODULE_OK; - } - - RedisModuleString *new_key = RedisModule_CreateStringPrintf(NULL, "string_changed{%s}", key_str); - RedisModule_AddPostNotificationJob(ctx, KeySpace_PostNotificationString, new_key, KeySpace_PostNotificationStringFreePD); - return REDISMODULE_OK; -} - -static int KeySpace_NotificationModule(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key) { - REDISMODULE_NOT_USED(ctx); - REDISMODULE_NOT_USED(type); - REDISMODULE_NOT_USED(event); - - const char* keyName = RedisModule_StringPtrLen(key, NULL); - int nokey; - RedisModule_DictGetC(module_event_log, (void*)keyName, strlen(keyName), &nokey); - if(nokey){ - RedisModule_DictSetC(module_event_log, (void*)keyName, strlen(keyName), RedisModule_HoldString(ctx, key)); - } - return REDISMODULE_OK; -} - -static int cmdNotify(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){ - if(argc != 2){ - return RedisModule_WrongArity(ctx); - } - - RedisModule_NotifyKeyspaceEvent(ctx, REDISMODULE_NOTIFY_MODULE, "notify", argv[1]); - RedisModule_ReplyWithNull(ctx); - return REDISMODULE_OK; -} - -static int cmdIsModuleKeyNotified(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){ - if(argc != 2){ - return RedisModule_WrongArity(ctx); - } - - const char* key = RedisModule_StringPtrLen(argv[1], NULL); - - int nokey; - RedisModuleString* keyStr = RedisModule_DictGetC(module_event_log, (void*)key, strlen(key), &nokey); - - RedisModule_ReplyWithArray(ctx, 2); - RedisModule_ReplyWithLongLong(ctx, !nokey); - if(nokey){ - RedisModule_ReplyWithNull(ctx); - }else{ - RedisModule_ReplyWithString(ctx, keyStr); - } - return REDISMODULE_OK; -} - -static int cmdIsKeyLoaded(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){ - if(argc != 2){ - return RedisModule_WrongArity(ctx); - } - - const char* key = RedisModule_StringPtrLen(argv[1], NULL); - - int nokey; - RedisModuleString* keyStr = RedisModule_DictGetC(loaded_event_log, (void*)key, strlen(key), &nokey); - - RedisModule_ReplyWithArray(ctx, 2); - RedisModule_ReplyWithLongLong(ctx, !nokey); - if(nokey){ - RedisModule_ReplyWithNull(ctx); - }else{ - RedisModule_ReplyWithString(ctx, keyStr); - } - return REDISMODULE_OK; -} - -static int cmdDelKeyCopy(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) - return RedisModule_WrongArity(ctx); - - cached_time = RedisModule_CachedMicroseconds(); - - RedisModuleCallReply* rep = RedisModule_Call(ctx, "DEL", "s!", argv[1]); - if (!rep) { - RedisModule_ReplyWithError(ctx, "NULL reply returned"); - } else { - RedisModule_ReplyWithCallReply(ctx, rep); - RedisModule_FreeCallReply(rep); - } - cached_time = 0; - return REDISMODULE_OK; -} - -/* Call INCR and propagate using RM_Call with `!`. */ -static int cmdIncrCase1(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) - return RedisModule_WrongArity(ctx); - - RedisModuleCallReply* rep = RedisModule_Call(ctx, "INCR", "s!", argv[1]); - if (!rep) { - RedisModule_ReplyWithError(ctx, "NULL reply returned"); - } else { - RedisModule_ReplyWithCallReply(ctx, rep); - RedisModule_FreeCallReply(rep); - } - return REDISMODULE_OK; -} - -/* Call INCR and propagate using RM_Replicate. */ -static int cmdIncrCase2(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) - return RedisModule_WrongArity(ctx); - - RedisModuleCallReply* rep = RedisModule_Call(ctx, "INCR", "s", argv[1]); - if (!rep) { - RedisModule_ReplyWithError(ctx, "NULL reply returned"); - } else { - RedisModule_ReplyWithCallReply(ctx, rep); - RedisModule_FreeCallReply(rep); - } - RedisModule_Replicate(ctx, "INCR", "s", argv[1]); - return REDISMODULE_OK; -} - -/* Call INCR and propagate using RM_ReplicateVerbatim. */ -static int cmdIncrCase3(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) - return RedisModule_WrongArity(ctx); - - RedisModuleCallReply* rep = RedisModule_Call(ctx, "INCR", "s", argv[1]); - if (!rep) { - RedisModule_ReplyWithError(ctx, "NULL reply returned"); - } else { - RedisModule_ReplyWithCallReply(ctx, rep); - RedisModule_FreeCallReply(rep); - } - RedisModule_ReplicateVerbatim(ctx); - return REDISMODULE_OK; -} - -static int cmdIncrDels(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - dels++; - return RedisModule_ReplyWithSimpleString(ctx, "OK"); -} - -static int cmdGetDels(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - return RedisModule_ReplyWithLongLong(ctx, dels); -} - -static RedisModuleNotificationFunc get_callback_for_event(int event_mask) { - switch(event_mask) { - case REDISMODULE_NOTIFY_LOADED: - return KeySpace_NotificationLoaded; - case REDISMODULE_NOTIFY_GENERIC: - return KeySpace_NotificationGeneric; - case REDISMODULE_NOTIFY_EXPIRED: - return KeySpace_NotificationExpired; - case REDISMODULE_NOTIFY_MODULE: - return KeySpace_NotificationModule; - case REDISMODULE_NOTIFY_KEY_MISS: - return KeySpace_NotificationModuleKeyMiss; - case REDISMODULE_NOTIFY_STRING: - // We have two callbacks for STRING events in your OnLoad, - // For simplicity, pick the first: - return KeySpace_NotificationModuleString; - default: - return NULL; - } -} - -int GetCallbackCountCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - RedisModule_ReplyWithLongLong(ctx, callback_call_count); - return REDISMODULE_OK; -} - -static int CmdUnsub(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) { - return RedisModule_WrongArity(ctx); - } - - long long event_mask; - if (RedisModule_StringToLongLong(argv[1], &event_mask) != REDISMODULE_OK) { - return RedisModule_ReplyWithError(ctx, "ERR invalid event mask"); - } - - RedisModuleNotificationFunc cb = get_callback_for_event((int)event_mask); - if (cb == NULL) { - return RedisModule_ReplyWithError(ctx, "ERR unknown event mask"); - } - - if (RedisModule_UnsubscribeFromKeyspaceEvents(ctx, (int)event_mask, cb) != REDISMODULE_OK) { - return RedisModule_ReplyWithError(ctx, "ERR unsubscribe failed"); - } - - return RedisModule_ReplyWithSimpleString(ctx, "OK"); -} -/* This function must be present on each Redis module. It is used in order to - * register the commands into the Redis server. */ -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (RedisModule_Init(ctx,"testkeyspace",1,REDISMODULE_APIVER_1) == REDISMODULE_ERR){ - return REDISMODULE_ERR; - } - - loaded_event_log = RedisModule_CreateDict(ctx); - module_event_log = RedisModule_CreateDict(ctx); - - int keySpaceAll = RedisModule_GetKeyspaceNotificationFlagsAll(); - - if (!(keySpaceAll & REDISMODULE_NOTIFY_LOADED)) { - // REDISMODULE_NOTIFY_LOADED event are not supported we can not start - return REDISMODULE_ERR; - } - - if(RedisModule_SubscribeToKeyspaceEvents(ctx, REDISMODULE_NOTIFY_LOADED, KeySpace_NotificationLoaded) != REDISMODULE_OK){ - return REDISMODULE_ERR; - } - - if(RedisModule_SubscribeToKeyspaceEvents(ctx, REDISMODULE_NOTIFY_GENERIC, KeySpace_NotificationGeneric) != REDISMODULE_OK){ - return REDISMODULE_ERR; - } - - if(RedisModule_SubscribeToKeyspaceEvents(ctx, REDISMODULE_NOTIFY_EXPIRED, KeySpace_NotificationExpired) != REDISMODULE_OK){ - return REDISMODULE_ERR; - } - - if(RedisModule_SubscribeToKeyspaceEvents(ctx, REDISMODULE_NOTIFY_MODULE, KeySpace_NotificationModule) != REDISMODULE_OK){ - return REDISMODULE_ERR; - } - - if(RedisModule_SubscribeToKeyspaceEvents(ctx, REDISMODULE_NOTIFY_KEY_MISS, KeySpace_NotificationModuleKeyMiss) != REDISMODULE_OK){ - return REDISMODULE_ERR; - } - - if(RedisModule_SubscribeToKeyspaceEvents(ctx, REDISMODULE_NOTIFY_STRING, KeySpace_NotificationModuleString) != REDISMODULE_OK){ - return REDISMODULE_ERR; - } - - if(RedisModule_SubscribeToKeyspaceEvents(ctx, REDISMODULE_NOTIFY_STRING, KeySpace_NotificationModuleStringPostNotificationJob) != REDISMODULE_OK){ - return REDISMODULE_ERR; - } - - if (RedisModule_CreateCommand(ctx,"keyspace.notify", cmdNotify,"",0,0,0) == REDISMODULE_ERR){ - return REDISMODULE_ERR; - } - - if (RedisModule_CreateCommand(ctx,"keyspace.is_module_key_notified", cmdIsModuleKeyNotified,"",0,0,0) == REDISMODULE_ERR){ - return REDISMODULE_ERR; - } - - if (RedisModule_CreateCommand(ctx,"keyspace.is_key_loaded", cmdIsKeyLoaded,"",0,0,0) == REDISMODULE_ERR){ - return REDISMODULE_ERR; - } - - if (RedisModule_CreateCommand(ctx, "keyspace.del_key_copy", cmdDelKeyCopy, - "write", 0, 0, 0) == REDISMODULE_ERR){ - return REDISMODULE_ERR; - } - - if (RedisModule_CreateCommand(ctx, "keyspace.incr_case1", cmdIncrCase1, - "write", 0, 0, 0) == REDISMODULE_ERR){ - return REDISMODULE_ERR; - } - - if (RedisModule_CreateCommand(ctx, "keyspace.incr_case2", cmdIncrCase2, - "write", 0, 0, 0) == REDISMODULE_ERR){ - return REDISMODULE_ERR; - } - - if (RedisModule_CreateCommand(ctx, "keyspace.incr_case3", cmdIncrCase3, - "write", 0, 0, 0) == REDISMODULE_ERR){ - return REDISMODULE_ERR; - } - - if (RedisModule_CreateCommand(ctx, "keyspace.incr_dels", cmdIncrDels, - "write", 0, 0, 0) == REDISMODULE_ERR){ - return REDISMODULE_ERR; - } - - if (RedisModule_CreateCommand(ctx, "keyspace.get_dels", cmdGetDels, - "readonly", 0, 0, 0) == REDISMODULE_ERR){ - return REDISMODULE_ERR; - } - - if (RedisModule_CreateCommand(ctx, "keyspace.unsubscribe", CmdUnsub, "write", 0, 0, 0) == REDISMODULE_ERR){ - return REDISMODULE_ERR; - } - - if (RedisModule_CreateCommand(ctx, "keyspace.callback_count", GetCallbackCountCommand, "", 0, 0, 0)== REDISMODULE_ERR){ - return REDISMODULE_ERR; - } - - if (argc == 1) { - const char *ptr = RedisModule_StringPtrLen(argv[0], NULL); - if (!strcasecmp(ptr, "noload")) { - /* This is a hint that we return ERR at the last moment of OnLoad. */ - RedisModule_FreeDict(ctx, loaded_event_log); - RedisModule_FreeDict(ctx, module_event_log); - return REDISMODULE_ERR; - } - } - - return REDISMODULE_OK; -} - -int RedisModule_OnUnload(RedisModuleCtx *ctx) { - RedisModuleDictIter *iter = RedisModule_DictIteratorStartC(loaded_event_log, "^", NULL, 0); - char* key; - size_t keyLen; - RedisModuleString* val; - while((key = RedisModule_DictNextC(iter, &keyLen, (void**)&val))){ - RedisModule_FreeString(ctx, val); - } - RedisModule_FreeDict(ctx, loaded_event_log); - RedisModule_DictIteratorStop(iter); - loaded_event_log = NULL; - - iter = RedisModule_DictIteratorStartC(module_event_log, "^", NULL, 0); - while((key = RedisModule_DictNextC(iter, &keyLen, (void**)&val))){ - RedisModule_FreeString(ctx, val); - } - RedisModule_FreeDict(ctx, module_event_log); - RedisModule_DictIteratorStop(iter); - module_event_log = NULL; - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/keyspecs.c b/examples/redis-unstable/tests/modules/keyspecs.c deleted file mode 100644 index 0a70de8..0000000 --- a/examples/redis-unstable/tests/modules/keyspecs.c +++ /dev/null @@ -1,236 +0,0 @@ -#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); - - /* Handle getkeys-api introspection (for "kspec.nonewithgetkeys") */ - if (RedisModule_IsKeysPositionRequest(ctx)) { - for (int i = 1; i < argc; i += 2) - RedisModule_KeyAtPosWithFlags(ctx, i, REDISMODULE_CMD_KEY_RO | REDISMODULE_CMD_KEY_ACCESS); - - return REDISMODULE_OK; - } - - 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; -} diff --git a/examples/redis-unstable/tests/modules/list.c b/examples/redis-unstable/tests/modules/list.c deleted file mode 100644 index 401b2d8..0000000 --- a/examples/redis-unstable/tests/modules/list.c +++ /dev/null @@ -1,252 +0,0 @@ -#include "redismodule.h" -#include <assert.h> -#include <errno.h> -#include <strings.h> - -/* LIST.GETALL key [REVERSE] */ -int list_getall(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc < 2 || argc > 3) return RedisModule_WrongArity(ctx); - int reverse = (argc == 3 && - !strcasecmp(RedisModule_StringPtrLen(argv[2], NULL), - "REVERSE")); - RedisModule_AutoMemory(ctx); - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ); - if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_LIST) { - return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); - } - long n = RedisModule_ValueLength(key); - RedisModule_ReplyWithArray(ctx, n); - if (!reverse) { - for (long i = 0; i < n; i++) { - RedisModuleString *elem = RedisModule_ListGet(key, i); - RedisModule_ReplyWithString(ctx, elem); - RedisModule_FreeString(ctx, elem); - } - } else { - for (long i = -1; i >= -n; i--) { - RedisModuleString *elem = RedisModule_ListGet(key, i); - RedisModule_ReplyWithString(ctx, elem); - RedisModule_FreeString(ctx, elem); - } - } - - /* Test error condition: index out of bounds */ - assert(RedisModule_ListGet(key, n) == NULL); - assert(errno == EDOM); /* no more elements in list */ - - /* RedisModule_CloseKey(key); //implicit, done by auto memory */ - return REDISMODULE_OK; -} - -/* LIST.EDIT key [REVERSE] cmdstr [value ..] - * - * cmdstr is a string of the following characters: - * - * k -- keep - * d -- delete - * i -- insert value from args - * r -- replace with value from args - * - * The number of occurrences of "i" and "r" in cmdstr) should correspond to the - * number of args after cmdstr. - * - * Reply with a RESP3 Map, containing the number of edits (inserts, replaces, deletes) - * performed, as well as the last index and the entry it points to. - */ -int list_edit(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc < 3) return RedisModule_WrongArity(ctx); - RedisModule_AutoMemory(ctx); - int argpos = 1; /* the next arg */ - - /* key */ - int keymode = REDISMODULE_READ | REDISMODULE_WRITE; - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[argpos++], keymode); - if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_LIST) { - return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); - } - - /* REVERSE */ - int reverse = 0; - if (argc >= 4 && - !strcasecmp(RedisModule_StringPtrLen(argv[argpos], NULL), "REVERSE")) { - reverse = 1; - argpos++; - } - - /* cmdstr */ - size_t cmdstr_len; - const char *cmdstr = RedisModule_StringPtrLen(argv[argpos++], &cmdstr_len); - - /* validate cmdstr vs. argc */ - long num_req_args = 0; - long min_list_length = 0; - for (size_t cmdpos = 0; cmdpos < cmdstr_len; cmdpos++) { - char c = cmdstr[cmdpos]; - if (c == 'i' || c == 'r') num_req_args++; - if (c == 'd' || c == 'r' || c == 'k') min_list_length++; - } - if (argc < argpos + num_req_args) { - return RedisModule_ReplyWithError(ctx, "ERR too few args"); - } - if ((long)RedisModule_ValueLength(key) < min_list_length) { - return RedisModule_ReplyWithError(ctx, "ERR list too short"); - } - - /* Iterate over the chars in cmdstr (edit instructions) */ - long long num_inserts = 0, num_deletes = 0, num_replaces = 0; - long index = reverse ? -1 : 0; - RedisModuleString *value; - - for (size_t cmdpos = 0; cmdpos < cmdstr_len; cmdpos++) { - switch (cmdstr[cmdpos]) { - case 'i': /* insert */ - value = argv[argpos++]; - assert(RedisModule_ListInsert(key, index, value) == REDISMODULE_OK); - index += reverse ? -1 : 1; - num_inserts++; - break; - case 'd': /* delete */ - assert(RedisModule_ListDelete(key, index) == REDISMODULE_OK); - num_deletes++; - break; - case 'r': /* replace */ - value = argv[argpos++]; - assert(RedisModule_ListSet(key, index, value) == REDISMODULE_OK); - index += reverse ? -1 : 1; - num_replaces++; - break; - case 'k': /* keep */ - index += reverse ? -1 : 1; - break; - } - } - - RedisModuleString *v = RedisModule_ListGet(key, index); - RedisModule_ReplyWithMap(ctx, v ? 5 : 4); - RedisModule_ReplyWithCString(ctx, "i"); - RedisModule_ReplyWithLongLong(ctx, num_inserts); - RedisModule_ReplyWithCString(ctx, "d"); - RedisModule_ReplyWithLongLong(ctx, num_deletes); - RedisModule_ReplyWithCString(ctx, "r"); - RedisModule_ReplyWithLongLong(ctx, num_replaces); - RedisModule_ReplyWithCString(ctx, "index"); - RedisModule_ReplyWithLongLong(ctx, index); - if (v) { - RedisModule_ReplyWithCString(ctx, "entry"); - RedisModule_ReplyWithString(ctx, v); - RedisModule_FreeString(ctx, v); - } - - RedisModule_CloseKey(key); - return REDISMODULE_OK; -} - -/* Reply based on errno as set by the List API functions. */ -static int replyByErrno(RedisModuleCtx *ctx) { - switch (errno) { - case EDOM: - return RedisModule_ReplyWithError(ctx, "ERR index out of bounds"); - case ENOTSUP: - return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); - default: assert(0); /* Can't happen */ - } -} - -/* LIST.GET key index */ -int list_get(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 3) return RedisModule_WrongArity(ctx); - long long index; - if (RedisModule_StringToLongLong(argv[2], &index) != REDISMODULE_OK) { - return RedisModule_ReplyWithError(ctx, "ERR index must be a number"); - } - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ); - RedisModuleString *value = RedisModule_ListGet(key, index); - if (value) { - RedisModule_ReplyWithString(ctx, value); - RedisModule_FreeString(ctx, value); - } else { - replyByErrno(ctx); - } - RedisModule_CloseKey(key); - return REDISMODULE_OK; -} - -/* LIST.SET key index value */ -int list_set(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 4) return RedisModule_WrongArity(ctx); - long long index; - if (RedisModule_StringToLongLong(argv[2], &index) != REDISMODULE_OK) { - RedisModule_ReplyWithError(ctx, "ERR index must be a number"); - return REDISMODULE_OK; - } - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE); - if (RedisModule_ListSet(key, index, argv[3]) == REDISMODULE_OK) { - RedisModule_ReplyWithSimpleString(ctx, "OK"); - } else { - replyByErrno(ctx); - } - RedisModule_CloseKey(key); - return REDISMODULE_OK; -} - -/* LIST.INSERT key index value - * - * If index is negative, value is inserted after, otherwise before the element - * at index. - */ -int list_insert(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 4) return RedisModule_WrongArity(ctx); - long long index; - if (RedisModule_StringToLongLong(argv[2], &index) != REDISMODULE_OK) { - RedisModule_ReplyWithError(ctx, "ERR index must be a number"); - return REDISMODULE_OK; - } - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE); - if (RedisModule_ListInsert(key, index, argv[3]) == REDISMODULE_OK) { - RedisModule_ReplyWithSimpleString(ctx, "OK"); - } else { - replyByErrno(ctx); - } - RedisModule_CloseKey(key); - return REDISMODULE_OK; -} - -/* LIST.DELETE key index */ -int list_delete(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 3) return RedisModule_WrongArity(ctx); - long long index; - if (RedisModule_StringToLongLong(argv[2], &index) != REDISMODULE_OK) { - RedisModule_ReplyWithError(ctx, "ERR index must be a number"); - return REDISMODULE_OK; - } - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE); - if (RedisModule_ListDelete(key, index) == REDISMODULE_OK) { - RedisModule_ReplyWithSimpleString(ctx, "OK"); - } else { - replyByErrno(ctx); - } - RedisModule_CloseKey(key); - return REDISMODULE_OK; -} - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - if (RedisModule_Init(ctx, "list", 1, REDISMODULE_APIVER_1) == REDISMODULE_OK && - RedisModule_CreateCommand(ctx, "list.getall", list_getall, "", - 1, 1, 1) == REDISMODULE_OK && - RedisModule_CreateCommand(ctx, "list.edit", list_edit, "write", - 1, 1, 1) == REDISMODULE_OK && - RedisModule_CreateCommand(ctx, "list.get", list_get, "write", - 1, 1, 1) == REDISMODULE_OK && - RedisModule_CreateCommand(ctx, "list.set", list_set, "write", - 1, 1, 1) == REDISMODULE_OK && - RedisModule_CreateCommand(ctx, "list.insert", list_insert, "write", - 1, 1, 1) == REDISMODULE_OK && - RedisModule_CreateCommand(ctx, "list.delete", list_delete, "write", - 1, 1, 1) == REDISMODULE_OK) { - return REDISMODULE_OK; - } else { - return REDISMODULE_ERR; - } -} diff --git a/examples/redis-unstable/tests/modules/mallocsize.c b/examples/redis-unstable/tests/modules/mallocsize.c deleted file mode 100644 index a1d31c1..0000000 --- a/examples/redis-unstable/tests/modules/mallocsize.c +++ /dev/null @@ -1,237 +0,0 @@ -#include "redismodule.h" -#include <string.h> -#include <assert.h> -#include <unistd.h> - -#define UNUSED(V) ((void) V) - -/* Registered type */ -RedisModuleType *mallocsize_type = NULL; - -typedef enum { - UDT_RAW, - UDT_STRING, - UDT_DICT -} udt_type_t; - -typedef struct { - void *ptr; - size_t len; -} raw_t; - -typedef struct { - udt_type_t type; - union { - raw_t raw; - RedisModuleString *str; - RedisModuleDict *dict; - } data; -} udt_t; - -void udt_free(void *value) { - udt_t *udt = value; - switch (udt->type) { - case (UDT_RAW): { - RedisModule_Free(udt->data.raw.ptr); - break; - } - case (UDT_STRING): { - RedisModule_FreeString(NULL, udt->data.str); - break; - } - case (UDT_DICT): { - RedisModuleString *dk, *dv; - RedisModuleDictIter *iter = RedisModule_DictIteratorStartC(udt->data.dict, "^", NULL, 0); - while((dk = RedisModule_DictNext(NULL, iter, (void **)&dv)) != NULL) { - RedisModule_FreeString(NULL, dk); - RedisModule_FreeString(NULL, dv); - } - RedisModule_DictIteratorStop(iter); - RedisModule_FreeDict(NULL, udt->data.dict); - break; - } - } - RedisModule_Free(udt); -} - -void udt_rdb_save(RedisModuleIO *rdb, void *value) { - udt_t *udt = value; - RedisModule_SaveUnsigned(rdb, udt->type); - switch (udt->type) { - case (UDT_RAW): { - RedisModule_SaveStringBuffer(rdb, udt->data.raw.ptr, udt->data.raw.len); - break; - } - case (UDT_STRING): { - RedisModule_SaveString(rdb, udt->data.str); - break; - } - case (UDT_DICT): { - RedisModule_SaveUnsigned(rdb, RedisModule_DictSize(udt->data.dict)); - RedisModuleString *dk, *dv; - RedisModuleDictIter *iter = RedisModule_DictIteratorStartC(udt->data.dict, "^", NULL, 0); - while((dk = RedisModule_DictNext(NULL, iter, (void **)&dv)) != NULL) { - RedisModule_SaveString(rdb, dk); - RedisModule_SaveString(rdb, dv); - RedisModule_FreeString(NULL, dk); /* Allocated by RedisModule_DictNext */ - } - RedisModule_DictIteratorStop(iter); - break; - } - } -} - -void *udt_rdb_load(RedisModuleIO *rdb, int encver) { - if (encver != 0) - return NULL; - udt_t *udt = RedisModule_Alloc(sizeof(*udt)); - udt->type = RedisModule_LoadUnsigned(rdb); - switch (udt->type) { - case (UDT_RAW): { - udt->data.raw.ptr = RedisModule_LoadStringBuffer(rdb, &udt->data.raw.len); - break; - } - case (UDT_STRING): { - udt->data.str = RedisModule_LoadString(rdb); - break; - } - case (UDT_DICT): { - long long dict_len = RedisModule_LoadUnsigned(rdb); - udt->data.dict = RedisModule_CreateDict(NULL); - for (int i = 0; i < dict_len; i += 2) { - RedisModuleString *key = RedisModule_LoadString(rdb); - RedisModuleString *val = RedisModule_LoadString(rdb); - RedisModule_DictSet(udt->data.dict, key, val); - } - break; - } - } - - return udt; -} - -size_t udt_mem_usage(RedisModuleKeyOptCtx *ctx, const void *value, size_t sample_size) { - UNUSED(ctx); - UNUSED(sample_size); - - const udt_t *udt = value; - size_t size = sizeof(*udt); - - switch (udt->type) { - case (UDT_RAW): { - size += RedisModule_MallocSize(udt->data.raw.ptr); - break; - } - case (UDT_STRING): { - size += RedisModule_MallocSizeString(udt->data.str); - break; - } - case (UDT_DICT): { - void *dk; - size_t keylen; - RedisModuleString *dv; - RedisModuleDictIter *iter = RedisModule_DictIteratorStartC(udt->data.dict, "^", NULL, 0); - while((dk = RedisModule_DictNextC(iter, &keylen, (void **)&dv)) != NULL) { - size += keylen; - size += RedisModule_MallocSizeString(dv); - } - RedisModule_DictIteratorStop(iter); - break; - } - } - - return size; -} - -/* MALLOCSIZE.SETRAW key len */ -int cmd_setraw(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 3) - return RedisModule_WrongArity(ctx); - - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE); - - udt_t *udt = RedisModule_Alloc(sizeof(*udt)); - udt->type = UDT_RAW; - - long long raw_len; - RedisModule_StringToLongLong(argv[2], &raw_len); - udt->data.raw.ptr = RedisModule_Alloc(raw_len); - udt->data.raw.len = raw_len; - - RedisModule_ModuleTypeSetValue(key, mallocsize_type, udt); - RedisModule_CloseKey(key); - - return RedisModule_ReplyWithSimpleString(ctx, "OK"); -} - -/* MALLOCSIZE.SETSTR key string */ -int cmd_setstr(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 3) - return RedisModule_WrongArity(ctx); - - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE); - - udt_t *udt = RedisModule_Alloc(sizeof(*udt)); - udt->type = UDT_STRING; - - udt->data.str = argv[2]; - RedisModule_RetainString(ctx, argv[2]); - - RedisModule_ModuleTypeSetValue(key, mallocsize_type, udt); - RedisModule_CloseKey(key); - - return RedisModule_ReplyWithSimpleString(ctx, "OK"); -} - -/* MALLOCSIZE.SETDICT key field value [field value ...] */ -int cmd_setdict(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc < 4 || argc % 2) - return RedisModule_WrongArity(ctx); - - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE); - - udt_t *udt = RedisModule_Alloc(sizeof(*udt)); - udt->type = UDT_DICT; - - udt->data.dict = RedisModule_CreateDict(ctx); - for (int i = 2; i < argc; i += 2) { - RedisModule_DictSet(udt->data.dict, argv[i], argv[i+1]); - /* No need to retain argv[i], it is copied as the rax key */ - RedisModule_RetainString(ctx, argv[i+1]); - } - - RedisModule_ModuleTypeSetValue(key, mallocsize_type, udt); - RedisModule_CloseKey(key); - - return RedisModule_ReplyWithSimpleString(ctx, "OK"); -} - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - UNUSED(argv); - UNUSED(argc); - if (RedisModule_Init(ctx,"mallocsize",1,REDISMODULE_APIVER_1)== REDISMODULE_ERR) - return REDISMODULE_ERR; - - RedisModuleTypeMethods tm = { - .version = REDISMODULE_TYPE_METHOD_VERSION, - .rdb_load = udt_rdb_load, - .rdb_save = udt_rdb_save, - .free = udt_free, - .mem_usage2 = udt_mem_usage, - }; - - mallocsize_type = RedisModule_CreateDataType(ctx, "allocsize", 0, &tm); - if (mallocsize_type == NULL) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "mallocsize.setraw", cmd_setraw, "", 1, 1, 1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "mallocsize.setstr", cmd_setstr, "", 1, 1, 1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "mallocsize.setdict", cmd_setdict, "", 1, 1, 1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/misc.c b/examples/redis-unstable/tests/modules/misc.c deleted file mode 100644 index dbf0fec..0000000 --- a/examples/redis-unstable/tests/modules/misc.c +++ /dev/null @@ -1,642 +0,0 @@ -#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, (size_t)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, (size_t)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, (size_t)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 + ClusterKeySlotC + 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)); - size_t len; - const char *key_c = RedisModule_StringPtrLen(key, &len); - assert(slot == RedisModule_ClusterKeySlotC(key_c, len)); - 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; -} - -/* Test command for RM_SignalModifiedKey. */ -int test_signalmodifiedkey(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) return RedisModule_WrongArity(ctx); - - /* Manually signal that the key was modified */ - RedisModule_SignalModifiedKey(ctx, argv[1]); - 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; - if (RedisModule_CreateCommand(ctx, "test.signalmodifiedkey", test_signalmodifiedkey, "write", 1, 1, 1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/moduleauthtwo.c b/examples/redis-unstable/tests/modules/moduleauthtwo.c deleted file mode 100644 index 0a4f56b..0000000 --- a/examples/redis-unstable/tests/modules/moduleauthtwo.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "redismodule.h" - -#include <string.h> - -/* This is a second sample module to validate that module authentication callbacks can be registered - * from multiple modules. */ - -/* Non Blocking Module Auth callback / implementation. */ -int auth_cb(RedisModuleCtx *ctx, RedisModuleString *username, RedisModuleString *password, RedisModuleString **err) { - const char *user = RedisModule_StringPtrLen(username, NULL); - const char *pwd = RedisModule_StringPtrLen(password, NULL); - if (!strcmp(user,"foo") && !strcmp(pwd,"allow_two")) { - RedisModule_AuthenticateClientWithACLUser(ctx, "foo", 3, NULL, NULL, NULL); - return REDISMODULE_AUTH_HANDLED; - } - else if (!strcmp(user,"foo") && !strcmp(pwd,"deny_two")) { - RedisModuleString *log = RedisModule_CreateString(ctx, "Module Auth", 11); - RedisModule_ACLAddLogEntryByUserName(ctx, username, log, REDISMODULE_ACL_LOG_AUTH); - RedisModule_FreeString(ctx, log); - const char *err_msg = "Auth denied by Misc Module."; - *err = RedisModule_CreateString(ctx, err_msg, strlen(err_msg)); - return REDISMODULE_AUTH_HANDLED; - } - return REDISMODULE_AUTH_NOT_HANDLED; -} - -int test_rm_register_auth_cb(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - RedisModule_RegisterAuthCallback(ctx, auth_cb); - 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,"moduleauthtwo",1,REDISMODULE_APIVER_1)== REDISMODULE_ERR) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx,"testmoduletwo.rm_register_auth_cb", test_rm_register_auth_cb,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - return REDISMODULE_OK; -}
\ No newline at end of file diff --git a/examples/redis-unstable/tests/modules/moduleconfigs.c b/examples/redis-unstable/tests/modules/moduleconfigs.c deleted file mode 100644 index 04974f7..0000000 --- a/examples/redis-unstable/tests/modules/moduleconfigs.c +++ /dev/null @@ -1,287 +0,0 @@ -#include "redismodule.h" -#include <strings.h> -int mutable_bool_val, no_prefix_bool, no_prefix_bool2; -int immutable_bool_val; -long long longval, no_prefix_longval; -long long memval, no_prefix_memval; -RedisModuleString *strval = NULL; -RedisModuleString *strval2 = NULL; -int enumval, no_prefix_enumval; -int flagsval; - -/* Series of get and set callbacks for each type of config, these rely on the privdata ptr - * to point to the config, and they register the configs as such. Note that one could also just - * use names if they wanted, and store anything in privdata. */ -int getBoolConfigCommand(const char *name, void *privdata) { - REDISMODULE_NOT_USED(name); - return (*(int *)privdata); -} - -int setBoolConfigCommand(const char *name, int new, void *privdata, RedisModuleString **err) { - REDISMODULE_NOT_USED(name); - REDISMODULE_NOT_USED(err); - *(int *)privdata = new; - return REDISMODULE_OK; -} - -long long getNumericConfigCommand(const char *name, void *privdata) { - REDISMODULE_NOT_USED(name); - return (*(long long *) privdata); -} - -int setNumericConfigCommand(const char *name, long long new, void *privdata, RedisModuleString **err) { - REDISMODULE_NOT_USED(name); - REDISMODULE_NOT_USED(err); - *(long long *)privdata = new; - return REDISMODULE_OK; -} - -RedisModuleString *getStringConfigCommand(const char *name, void *privdata) { - REDISMODULE_NOT_USED(name); - REDISMODULE_NOT_USED(privdata); - return strval; -} -int setStringConfigCommand(const char *name, RedisModuleString *new, void *privdata, RedisModuleString **err) { - REDISMODULE_NOT_USED(name); - REDISMODULE_NOT_USED(err); - REDISMODULE_NOT_USED(privdata); - size_t len; - if (!strcasecmp(RedisModule_StringPtrLen(new, &len), "rejectisfreed")) { - *err = RedisModule_CreateString(NULL, "Cannot set string to 'rejectisfreed'", 36); - return REDISMODULE_ERR; - } - if (strval) RedisModule_FreeString(NULL, strval); - RedisModule_RetainString(NULL, new); - strval = new; - return REDISMODULE_OK; -} - -int getEnumConfigCommand(const char *name, void *privdata) { - REDISMODULE_NOT_USED(name); - REDISMODULE_NOT_USED(privdata); - return enumval; -} - -int setEnumConfigCommand(const char *name, int val, void *privdata, RedisModuleString **err) { - REDISMODULE_NOT_USED(name); - REDISMODULE_NOT_USED(err); - REDISMODULE_NOT_USED(privdata); - enumval = val; - return REDISMODULE_OK; -} - -int getFlagsConfigCommand(const char *name, void *privdata) { - REDISMODULE_NOT_USED(name); - REDISMODULE_NOT_USED(privdata); - return flagsval; -} - -int setFlagsConfigCommand(const char *name, int val, void *privdata, RedisModuleString **err) { - REDISMODULE_NOT_USED(name); - REDISMODULE_NOT_USED(err); - REDISMODULE_NOT_USED(privdata); - flagsval = val; - return REDISMODULE_OK; -} - -int boolApplyFunc(RedisModuleCtx *ctx, void *privdata, RedisModuleString **err) { - REDISMODULE_NOT_USED(ctx); - REDISMODULE_NOT_USED(privdata); - if (mutable_bool_val && immutable_bool_val) { - *err = RedisModule_CreateString(NULL, "Bool configs cannot both be yes.", 32); - return REDISMODULE_ERR; - } - return REDISMODULE_OK; -} - -int longlongApplyFunc(RedisModuleCtx *ctx, void *privdata, RedisModuleString **err) { - REDISMODULE_NOT_USED(ctx); - REDISMODULE_NOT_USED(privdata); - if (longval == memval) { - *err = RedisModule_CreateString(NULL, "These configs cannot equal each other.", 38); - return REDISMODULE_ERR; - } - return REDISMODULE_OK; -} - -RedisModuleString *getStringConfigUnprefix(const char *name, void *privdata) { - REDISMODULE_NOT_USED(name); - REDISMODULE_NOT_USED(privdata); - return strval2; -} - -int setStringConfigUnprefix(const char *name, RedisModuleString *new, void *privdata, RedisModuleString **err) { - REDISMODULE_NOT_USED(name); - REDISMODULE_NOT_USED(err); - REDISMODULE_NOT_USED(privdata); - if (strval2) RedisModule_FreeString(NULL, strval2); - RedisModule_RetainString(NULL, new); - strval2 = new; - return REDISMODULE_OK; -} - -int getEnumConfigUnprefix(const char *name, void *privdata) { - REDISMODULE_NOT_USED(name); - REDISMODULE_NOT_USED(privdata); - return no_prefix_enumval; -} - -int setEnumConfigUnprefix(const char *name, int val, void *privdata, RedisModuleString **err) { - REDISMODULE_NOT_USED(name); - REDISMODULE_NOT_USED(err); - REDISMODULE_NOT_USED(privdata); - no_prefix_enumval = val; - return REDISMODULE_OK; -} - -int registerBlockCheck(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - int response_ok = 0; - int result = RedisModule_RegisterBoolConfig(ctx, "mutable_bool", 1, REDISMODULE_CONFIG_DEFAULT, getBoolConfigCommand, setBoolConfigCommand, boolApplyFunc, &mutable_bool_val); - response_ok |= (result == REDISMODULE_OK); - - result = RedisModule_RegisterStringConfig(ctx, "string", "secret password", REDISMODULE_CONFIG_DEFAULT, getStringConfigCommand, setStringConfigCommand, NULL, NULL); - response_ok |= (result == REDISMODULE_OK); - - const char *enum_vals[] = {"none", "five", "one", "two", "four"}; - const int int_vals[] = {0, 5, 1, 2, 4}; - result = RedisModule_RegisterEnumConfig(ctx, "enum", 1, REDISMODULE_CONFIG_DEFAULT, enum_vals, int_vals, 5, getEnumConfigCommand, setEnumConfigCommand, NULL, NULL); - response_ok |= (result == REDISMODULE_OK); - - result = RedisModule_RegisterNumericConfig(ctx, "numeric", -1, REDISMODULE_CONFIG_DEFAULT, -5, 2000, getNumericConfigCommand, setNumericConfigCommand, longlongApplyFunc, &longval); - response_ok |= (result == REDISMODULE_OK); - - result = RedisModule_LoadConfigs(ctx); - response_ok |= (result == REDISMODULE_OK); - - /* This validates that it's not possible to register/load configs outside OnLoad, - * thus returns an error if they succeed. */ - if (response_ok) { - RedisModule_ReplyWithError(ctx, "UNEXPECTEDOK"); - } else { - RedisModule_ReplyWithSimpleString(ctx, "OK"); - } - return REDISMODULE_OK; -} - -void cleanup(RedisModuleCtx *ctx) { - if (strval) { - RedisModule_FreeString(ctx, strval); - strval = NULL; - } - if (strval2) { - RedisModule_FreeString(ctx, strval2); - strval2 = NULL; - } -} - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - if (RedisModule_Init(ctx, "moduleconfigs", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR) { - RedisModule_Log(ctx, "warning", "Failed to init module"); - return REDISMODULE_ERR; - } - - if (RedisModule_RegisterBoolConfig(ctx, "mutable_bool", 1, REDISMODULE_CONFIG_DEFAULT, getBoolConfigCommand, setBoolConfigCommand, boolApplyFunc, &mutable_bool_val) == REDISMODULE_ERR) { - RedisModule_Log(ctx, "warning", "Failed to register mutable_bool"); - return REDISMODULE_ERR; - } - /* Immutable config here. */ - if (RedisModule_RegisterBoolConfig(ctx, "immutable_bool", 0, REDISMODULE_CONFIG_IMMUTABLE, getBoolConfigCommand, setBoolConfigCommand, boolApplyFunc, &immutable_bool_val) == REDISMODULE_ERR) { - RedisModule_Log(ctx, "warning", "Failed to register immutable_bool"); - return REDISMODULE_ERR; - } - if (RedisModule_RegisterStringConfig(ctx, "string", "secret password", REDISMODULE_CONFIG_DEFAULT, getStringConfigCommand, setStringConfigCommand, NULL, NULL) == REDISMODULE_ERR) { - RedisModule_Log(ctx, "warning", "Failed to register string"); - return REDISMODULE_ERR; - } - - /* On the stack to make sure we're copying them. */ - const char *enum_vals[] = {"none", "five", "one", "two", "four"}; - const int int_vals[] = {0, 5, 1, 2, 4}; - - if (RedisModule_RegisterEnumConfig(ctx, "enum", 1, REDISMODULE_CONFIG_DEFAULT, enum_vals, int_vals, 5, getEnumConfigCommand, setEnumConfigCommand, NULL, NULL) == REDISMODULE_ERR) { - RedisModule_Log(ctx, "warning", "Failed to register enum"); - return REDISMODULE_ERR; - } - if (RedisModule_RegisterEnumConfig(ctx, "flags", 3, REDISMODULE_CONFIG_DEFAULT | REDISMODULE_CONFIG_BITFLAGS, enum_vals, int_vals, 5, getFlagsConfigCommand, setFlagsConfigCommand, NULL, NULL) == REDISMODULE_ERR) { - RedisModule_Log(ctx, "warning", "Failed to register flags"); - return REDISMODULE_ERR; - } - /* Memory config here. */ - if (RedisModule_RegisterNumericConfig(ctx, "memory_numeric", 1024, REDISMODULE_CONFIG_DEFAULT | REDISMODULE_CONFIG_MEMORY, 0, 3000000, getNumericConfigCommand, setNumericConfigCommand, longlongApplyFunc, &memval) == REDISMODULE_ERR) { - RedisModule_Log(ctx, "warning", "Failed to register memory_numeric"); - return REDISMODULE_ERR; - } - if (RedisModule_RegisterNumericConfig(ctx, "numeric", -1, REDISMODULE_CONFIG_DEFAULT, -5, 2000, getNumericConfigCommand, setNumericConfigCommand, longlongApplyFunc, &longval) == REDISMODULE_ERR) { - RedisModule_Log(ctx, "warning", "Failed to register numeric"); - return REDISMODULE_ERR; - } - - /*** unprefixed and aliased configuration ***/ - if (RedisModule_RegisterBoolConfig(ctx, "unprefix-bool|unprefix-bool-alias", 1, REDISMODULE_CONFIG_DEFAULT|REDISMODULE_CONFIG_UNPREFIXED, - getBoolConfigCommand, setBoolConfigCommand, NULL, &no_prefix_bool) == REDISMODULE_ERR) { - RedisModule_Log(ctx, "warning", "Failed to register unprefix-bool"); - return REDISMODULE_ERR; - } - if (RedisModule_RegisterBoolConfig(ctx, "unprefix-noalias-bool", 1, REDISMODULE_CONFIG_DEFAULT|REDISMODULE_CONFIG_UNPREFIXED, - getBoolConfigCommand, setBoolConfigCommand, NULL, &no_prefix_bool2) == REDISMODULE_ERR) { - RedisModule_Log(ctx, "warning", "Failed to register unprefix-noalias-bool"); - return REDISMODULE_ERR; - } - if (RedisModule_RegisterNumericConfig(ctx, "unprefix.numeric|unprefix.numeric-alias", -1, REDISMODULE_CONFIG_DEFAULT|REDISMODULE_CONFIG_UNPREFIXED, - -5, 2000, getNumericConfigCommand, setNumericConfigCommand, NULL, &no_prefix_longval) == REDISMODULE_ERR) { - RedisModule_Log(ctx, "warning", "Failed to register unprefix.numeric"); - return REDISMODULE_ERR; - } - if (RedisModule_RegisterStringConfig(ctx, "unprefix-string|unprefix.string-alias", "secret unprefix", REDISMODULE_CONFIG_DEFAULT|REDISMODULE_CONFIG_UNPREFIXED, - getStringConfigUnprefix, setStringConfigUnprefix, NULL, NULL) == REDISMODULE_ERR) { - RedisModule_Log(ctx, "warning", "Failed to register unprefix-string"); - return REDISMODULE_ERR; - } - if (RedisModule_RegisterEnumConfig(ctx, "unprefix-enum|unprefix-enum-alias", 1, REDISMODULE_CONFIG_DEFAULT|REDISMODULE_CONFIG_UNPREFIXED, - enum_vals, int_vals, 5, getEnumConfigUnprefix, setEnumConfigUnprefix, NULL, NULL) == REDISMODULE_ERR) { - RedisModule_Log(ctx, "warning", "Failed to register unprefix-enum"); - return REDISMODULE_ERR; - } - - RedisModule_Log(ctx, "debug", "Registered configuration"); - size_t len; - if (argc && !strcasecmp(RedisModule_StringPtrLen(argv[0], &len), "noload")) { - return REDISMODULE_OK; - } else if (argc && !strcasecmp(RedisModule_StringPtrLen(argv[0], &len), "override-default")) { - if (RedisModule_LoadDefaultConfigs(ctx) == REDISMODULE_ERR) { - RedisModule_Log(ctx, "warning", "Failed to load default configuration"); - goto err; - } - // simulate configuration values being overwritten by the command line - RedisModule_Log(ctx, "debug", "Overriding configuration values"); - if (strval) RedisModule_FreeString(ctx, strval); - strval = RedisModule_CreateString(ctx, "foo", 3); - longval = memval = 123; - } - RedisModule_Log(ctx, "debug", "Loading configuration"); - if (RedisModule_LoadConfigs(ctx) == REDISMODULE_ERR) { - RedisModule_Log(ctx, "warning", "Failed to load configuration"); - goto err; - } - /* Creates a command which registers configs outside OnLoad() function. */ - if (RedisModule_CreateCommand(ctx,"block.register.configs.outside.onload", registerBlockCheck, "write", 0, 0, 0) == REDISMODULE_ERR) { - RedisModule_Log(ctx, "warning", "Failed to register command"); - goto err; - } - - return REDISMODULE_OK; -err: - cleanup(ctx); - return REDISMODULE_ERR; -} - -int RedisModule_OnUnload(RedisModuleCtx *ctx) { - REDISMODULE_NOT_USED(ctx); - cleanup(ctx); - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/moduleconfigstwo.c b/examples/redis-unstable/tests/modules/moduleconfigstwo.c deleted file mode 100644 index c0e8f91..0000000 --- a/examples/redis-unstable/tests/modules/moduleconfigstwo.c +++ /dev/null @@ -1,39 +0,0 @@ -#include "redismodule.h" -#include <strings.h> - -/* Second module configs module, for testing. - * Need to make sure that multiple modules with configs don't interfere with each other */ -int bool_config; - -int getBoolConfigCommand(const char *name, void *privdata) { - REDISMODULE_NOT_USED(privdata); - if (!strcasecmp(name, "test")) { - return bool_config; - } - return 0; -} - -int setBoolConfigCommand(const char *name, int new, void *privdata, RedisModuleString **err) { - REDISMODULE_NOT_USED(privdata); - REDISMODULE_NOT_USED(err); - if (!strcasecmp(name, "test")) { - bool_config = new; - return REDISMODULE_OK; - } - return REDISMODULE_ERR; -} - -/* No arguments are expected */ -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - if (RedisModule_Init(ctx, "configs", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR) return REDISMODULE_ERR; - - if (RedisModule_RegisterBoolConfig(ctx, "test", 1, REDISMODULE_CONFIG_DEFAULT, getBoolConfigCommand, setBoolConfigCommand, NULL, &argc) == REDISMODULE_ERR) { - return REDISMODULE_ERR; - } - if (RedisModule_LoadConfigs(ctx) == REDISMODULE_ERR) { - return REDISMODULE_ERR; - } - return REDISMODULE_OK; -}
\ No newline at end of file diff --git a/examples/redis-unstable/tests/modules/postnotifications.c b/examples/redis-unstable/tests/modules/postnotifications.c deleted file mode 100644 index 96fb859..0000000 --- a/examples/redis-unstable/tests/modules/postnotifications.c +++ /dev/null @@ -1,289 +0,0 @@ -/* This module is used to test the server post keyspace jobs API. - * - * ----------------------------------------------------------------------------- - * - * Copyright (c) 2020-Present, Redis Ltd. - * All rights reserved. - * - * Licensed under your choice of (a) the Redis Source Available License 2.0 - * (RSALv2); or (b) the Server Side Public License v1 (SSPLv1); or (c) the - * GNU Affero General Public License v3 (AGPLv3). - */ - -/* This module allow to verify 'RedisModule_AddPostNotificationJob' by registering to 3 - * key space event: - * * STRINGS - the module register to all strings notifications and set post notification job - * that increase a counter indicating how many times the string key was changed. - * In addition, it increase another counter that counts the total changes that - * was made on all strings keys. - * * EXPIRED - the module register to expired event and set post notification job that that - * counts the total number of expired events. - * * EVICTED - the module register to evicted event and set post notification job that that - * counts the total number of evicted events. - * - * In addition, the module register a new command, 'postnotification.async_set', that performs a set - * command from a background thread. This allows to check the 'RedisModule_AddPostNotificationJob' on - * notifications that was triggered on a background thread. */ - -#define _BSD_SOURCE -#define _DEFAULT_SOURCE /* For usleep */ - -#include "redismodule.h" -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <pthread.h> - -static void KeySpace_PostNotificationStringFreePD(void *pd) { - RedisModule_FreeString(NULL, pd); -} - -static void KeySpace_PostNotificationReadKey(RedisModuleCtx *ctx, void *pd) { - RedisModuleCallReply* rep = RedisModule_Call(ctx, "get", "!s", pd); - RedisModule_FreeCallReply(rep); -} - -static void KeySpace_PostNotificationString(RedisModuleCtx *ctx, void *pd) { - REDISMODULE_NOT_USED(ctx); - RedisModuleCallReply* rep = RedisModule_Call(ctx, "incr", "!s", pd); - RedisModule_FreeCallReply(rep); -} - -static int KeySpace_NotificationExpired(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key){ - REDISMODULE_NOT_USED(type); - REDISMODULE_NOT_USED(event); - REDISMODULE_NOT_USED(key); - - RedisModuleString *new_key = RedisModule_CreateString(NULL, "expired", 7); - int res = RedisModule_AddPostNotificationJob(ctx, KeySpace_PostNotificationString, new_key, KeySpace_PostNotificationStringFreePD); - if (res == REDISMODULE_ERR) KeySpace_PostNotificationStringFreePD(new_key); - return REDISMODULE_OK; -} - -static int KeySpace_NotificationEvicted(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key){ - REDISMODULE_NOT_USED(type); - REDISMODULE_NOT_USED(event); - REDISMODULE_NOT_USED(key); - - const char *key_str = RedisModule_StringPtrLen(key, NULL); - - if (strncmp(key_str, "evicted", 7) == 0) { - return REDISMODULE_OK; /* do not count the evicted key */ - } - - if (strncmp(key_str, "before_evicted", 14) == 0) { - return REDISMODULE_OK; /* do not count the before_evicted key */ - } - - RedisModuleString *new_key = RedisModule_CreateString(NULL, "evicted", 7); - int res = RedisModule_AddPostNotificationJob(ctx, KeySpace_PostNotificationString, new_key, KeySpace_PostNotificationStringFreePD); - if (res == REDISMODULE_ERR) KeySpace_PostNotificationStringFreePD(new_key); - return REDISMODULE_OK; -} - -static int KeySpace_NotificationString(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key){ - REDISMODULE_NOT_USED(ctx); - REDISMODULE_NOT_USED(type); - REDISMODULE_NOT_USED(event); - - const char *key_str = RedisModule_StringPtrLen(key, NULL); - - if (strncmp(key_str, "string_", 7) != 0) { - return REDISMODULE_OK; - } - - if (strcmp(key_str, "string_total") == 0) { - return REDISMODULE_OK; - } - - RedisModuleString *new_key; - if (strncmp(key_str, "string_changed{", 15) == 0) { - new_key = RedisModule_CreateString(NULL, "string_total", 12); - } else { - new_key = RedisModule_CreateStringPrintf(NULL, "string_changed{%s}", key_str); - } - - int res = RedisModule_AddPostNotificationJob(ctx, KeySpace_PostNotificationString, new_key, KeySpace_PostNotificationStringFreePD); - if (res == REDISMODULE_ERR) KeySpace_PostNotificationStringFreePD(new_key); - return REDISMODULE_OK; -} - -static int KeySpace_LazyExpireInsidePostNotificationJob(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key){ - REDISMODULE_NOT_USED(ctx); - REDISMODULE_NOT_USED(type); - REDISMODULE_NOT_USED(event); - - const char *key_str = RedisModule_StringPtrLen(key, NULL); - - if (strncmp(key_str, "read_", 5) != 0) { - return REDISMODULE_OK; - } - - RedisModuleString *new_key = RedisModule_CreateString(NULL, key_str + 5, strlen(key_str) - 5);; - int res = RedisModule_AddPostNotificationJob(ctx, KeySpace_PostNotificationReadKey, new_key, KeySpace_PostNotificationStringFreePD); - if (res == REDISMODULE_ERR) KeySpace_PostNotificationStringFreePD(new_key); - return REDISMODULE_OK; -} - -static int KeySpace_NestedNotification(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key){ - REDISMODULE_NOT_USED(ctx); - REDISMODULE_NOT_USED(type); - REDISMODULE_NOT_USED(event); - - const char *key_str = RedisModule_StringPtrLen(key, NULL); - - if (strncmp(key_str, "write_sync_", 11) != 0) { - return REDISMODULE_OK; - } - - /* This test was only meant to check REDISMODULE_OPTIONS_ALLOW_NESTED_KEYSPACE_NOTIFICATIONS. - * In general it is wrong and discourage to perform any writes inside a notification callback. */ - RedisModuleString *new_key = RedisModule_CreateString(NULL, key_str + 11, strlen(key_str) - 11);; - RedisModuleCallReply* rep = RedisModule_Call(ctx, "set", "!sc", new_key, "1"); - RedisModule_FreeCallReply(rep); - RedisModule_FreeString(NULL, new_key); - return REDISMODULE_OK; -} - -static void *KeySpace_PostNotificationsAsyncSetInner(void *arg) { - RedisModuleBlockedClient *bc = arg; - RedisModuleCtx *ctx = RedisModule_GetThreadSafeContext(bc); - RedisModule_ThreadSafeContextLock(ctx); - RedisModuleCallReply* rep = RedisModule_Call(ctx, "set", "!cc", "string_x", "1"); - RedisModule_ThreadSafeContextUnlock(ctx); - RedisModule_ReplyWithCallReply(ctx, rep); - RedisModule_FreeCallReply(rep); - - RedisModule_UnblockClient(bc, NULL); - RedisModule_FreeThreadSafeContext(ctx); - return NULL; -} - -static int KeySpace_PostNotificationsAsyncSet(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - if (argc != 1) - return RedisModule_WrongArity(ctx); - - pthread_t tid; - RedisModuleBlockedClient *bc = RedisModule_BlockClient(ctx,NULL,NULL,NULL,0); - - if (pthread_create(&tid,NULL,KeySpace_PostNotificationsAsyncSetInner,bc) != 0) { - RedisModule_AbortBlock(bc); - return RedisModule_ReplyWithError(ctx,"-ERR Can't start thread"); - } - pthread_detach(tid); - return REDISMODULE_OK; -} - -typedef struct KeySpace_EventPostNotificationCtx { - RedisModuleString *triggered_on; - RedisModuleString *new_key; -} KeySpace_EventPostNotificationCtx; - -static void KeySpace_ServerEventPostNotificationFree(void *pd) { - KeySpace_EventPostNotificationCtx *pn_ctx = pd; - RedisModule_FreeString(NULL, pn_ctx->new_key); - RedisModule_FreeString(NULL, pn_ctx->triggered_on); - RedisModule_Free(pn_ctx); -} - -static void KeySpace_ServerEventPostNotification(RedisModuleCtx *ctx, void *pd) { - REDISMODULE_NOT_USED(ctx); - KeySpace_EventPostNotificationCtx *pn_ctx = pd; - RedisModuleCallReply* rep = RedisModule_Call(ctx, "lpush", "!ss", pn_ctx->new_key, pn_ctx->triggered_on); - RedisModule_FreeCallReply(rep); -} - -static void KeySpace_ServerEventCallback(RedisModuleCtx *ctx, RedisModuleEvent eid, uint64_t subevent, void *data) { - REDISMODULE_NOT_USED(eid); - REDISMODULE_NOT_USED(data); - if (subevent > 3) { - RedisModule_Log(ctx, "warning", "Got an unexpected subevent '%llu'", (unsigned long long)subevent); - return; - } - static const char* events[] = { - "before_deleted", - "before_expired", - "before_evicted", - "before_overwritten", - }; - - const RedisModuleString *key_name = RedisModule_GetKeyNameFromModuleKey(((RedisModuleKeyInfo*)data)->key); - const char *key_str = RedisModule_StringPtrLen(key_name, NULL); - - for (int i = 0 ; i < 4 ; ++i) { - const char *event = events[i]; - if (strncmp(key_str, event , strlen(event)) == 0) { - return; /* don't log any event on our tracking keys */ - } - } - - KeySpace_EventPostNotificationCtx *pn_ctx = RedisModule_Alloc(sizeof(*pn_ctx)); - pn_ctx->triggered_on = RedisModule_HoldString(NULL, (RedisModuleString*)key_name); - pn_ctx->new_key = RedisModule_CreateString(NULL, events[subevent], strlen(events[subevent])); - int res = RedisModule_AddPostNotificationJob(ctx, KeySpace_ServerEventPostNotification, pn_ctx, KeySpace_ServerEventPostNotificationFree); - if (res == REDISMODULE_ERR) KeySpace_ServerEventPostNotificationFree(pn_ctx); -} - -/* This function must be present on each Redis module. It is used in order to - * register the commands into the Redis server. */ -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - if (RedisModule_Init(ctx,"postnotifications",1,REDISMODULE_APIVER_1) == REDISMODULE_ERR){ - return REDISMODULE_ERR; - } - - if (!(RedisModule_GetModuleOptionsAll() & REDISMODULE_OPTIONS_ALLOW_NESTED_KEYSPACE_NOTIFICATIONS)) { - return REDISMODULE_ERR; - } - - int with_key_events = 0; - if (argc >= 1) { - const char *arg = RedisModule_StringPtrLen(argv[0], 0); - if (strcmp(arg, "with_key_events") == 0) { - with_key_events = 1; - } - } - - RedisModule_SetModuleOptions(ctx, REDISMODULE_OPTIONS_ALLOW_NESTED_KEYSPACE_NOTIFICATIONS); - - if(RedisModule_SubscribeToKeyspaceEvents(ctx, REDISMODULE_NOTIFY_STRING, KeySpace_NotificationString) != REDISMODULE_OK){ - return REDISMODULE_ERR; - } - - if(RedisModule_SubscribeToKeyspaceEvents(ctx, REDISMODULE_NOTIFY_STRING, KeySpace_LazyExpireInsidePostNotificationJob) != REDISMODULE_OK){ - return REDISMODULE_ERR; - } - - if(RedisModule_SubscribeToKeyspaceEvents(ctx, REDISMODULE_NOTIFY_STRING, KeySpace_NestedNotification) != REDISMODULE_OK){ - return REDISMODULE_ERR; - } - - if(RedisModule_SubscribeToKeyspaceEvents(ctx, REDISMODULE_NOTIFY_EXPIRED, KeySpace_NotificationExpired) != REDISMODULE_OK){ - return REDISMODULE_ERR; - } - - if(RedisModule_SubscribeToKeyspaceEvents(ctx, REDISMODULE_NOTIFY_EVICTED, KeySpace_NotificationEvicted) != REDISMODULE_OK){ - return REDISMODULE_ERR; - } - - if (with_key_events) { - if(RedisModule_SubscribeToServerEvent(ctx, RedisModuleEvent_Key, KeySpace_ServerEventCallback) != REDISMODULE_OK){ - return REDISMODULE_ERR; - } - } - - if (RedisModule_CreateCommand(ctx, "postnotification.async_set", KeySpace_PostNotificationsAsyncSet, - "write", 0, 0, 0) == REDISMODULE_ERR){ - return REDISMODULE_ERR; - } - - return REDISMODULE_OK; -} - -int RedisModule_OnUnload(RedisModuleCtx *ctx) { - REDISMODULE_NOT_USED(ctx); - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/propagate.c b/examples/redis-unstable/tests/modules/propagate.c deleted file mode 100644 index 689d688..0000000 --- a/examples/redis-unstable/tests/modules/propagate.c +++ /dev/null @@ -1,403 +0,0 @@ -/* This module is used to test the propagation (replication + AOF) of - * commands, via the RedisModule_Replicate() interface, in asynchronous - * contexts, such as callbacks not implementing commands, and thread safe - * contexts. - * - * We create a timer callback and a threads using a thread safe context. - * Using both we try to propagate counters increments, and later we check - * if the replica contains the changes as expected. - * - * ----------------------------------------------------------------------------- - * - * Copyright (c) 2019-Present, Redis Ltd. - * All rights reserved. - * - * Licensed under your choice of (a) the Redis Source Available License 2.0 - * (RSALv2); or (b) the Server Side Public License v1 (SSPLv1); or (c) the - * GNU Affero General Public License v3 (AGPLv3). - */ - -#include "redismodule.h" -#include <pthread.h> -#include <errno.h> - -#define UNUSED(V) ((void) V) - -RedisModuleCtx *detached_ctx = NULL; - -static int KeySpace_NotificationGeneric(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key) { - REDISMODULE_NOT_USED(type); - REDISMODULE_NOT_USED(event); - REDISMODULE_NOT_USED(key); - - RedisModuleCallReply* rep = RedisModule_Call(ctx, "INCR", "c!", "notifications"); - RedisModule_FreeCallReply(rep); - - return REDISMODULE_OK; -} - -/* Timer callback. */ -void timerHandler(RedisModuleCtx *ctx, void *data) { - REDISMODULE_NOT_USED(ctx); - REDISMODULE_NOT_USED(data); - - static int times = 0; - - RedisModule_Replicate(ctx,"INCR","c","timer"); - times++; - - if (times < 3) - RedisModule_CreateTimer(ctx,100,timerHandler,NULL); - else - times = 0; -} - -int propagateTestTimerCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModuleTimerID timer_id = - RedisModule_CreateTimer(ctx,100,timerHandler,NULL); - REDISMODULE_NOT_USED(timer_id); - - RedisModule_ReplyWithSimpleString(ctx,"OK"); - return REDISMODULE_OK; -} - -/* Timer callback. */ -void timerNestedHandler(RedisModuleCtx *ctx, void *data) { - int repl = (long long)data; - - /* The goal is the trigger a module command that calls RM_Replicate - * in order to test MULTI/EXEC structure */ - RedisModule_Replicate(ctx,"INCRBY","cc","timer-nested-start","1"); - RedisModuleCallReply *reply = RedisModule_Call(ctx,"propagate-test.nested", repl? "!" : ""); - RedisModule_FreeCallReply(reply); - reply = RedisModule_Call(ctx, "INCR", repl? "c!" : "c", "timer-nested-middle"); - RedisModule_FreeCallReply(reply); - RedisModule_Replicate(ctx,"INCRBY","cc","timer-nested-end","1"); -} - -int propagateTestTimerNestedCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModuleTimerID timer_id = - RedisModule_CreateTimer(ctx,100,timerNestedHandler,(void*)0); - REDISMODULE_NOT_USED(timer_id); - - RedisModule_ReplyWithSimpleString(ctx,"OK"); - return REDISMODULE_OK; -} - -int propagateTestTimerNestedReplCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModuleTimerID timer_id = - RedisModule_CreateTimer(ctx,100,timerNestedHandler,(void*)1); - REDISMODULE_NOT_USED(timer_id); - - RedisModule_ReplyWithSimpleString(ctx,"OK"); - return REDISMODULE_OK; -} - -void timerHandlerMaxmemory(RedisModuleCtx *ctx, void *data) { - REDISMODULE_NOT_USED(ctx); - REDISMODULE_NOT_USED(data); - - RedisModuleCallReply *reply = RedisModule_Call(ctx,"SETEX","ccc!","timer-maxmemory-volatile-start","100","1"); - RedisModule_FreeCallReply(reply); - reply = RedisModule_Call(ctx, "CONFIG", "ccc!", "SET", "maxmemory", "1"); - RedisModule_FreeCallReply(reply); - - RedisModule_Replicate(ctx, "INCR", "c", "timer-maxmemory-middle"); - - reply = RedisModule_Call(ctx,"SETEX","ccc!","timer-maxmemory-volatile-end","100","1"); - RedisModule_FreeCallReply(reply); -} - -int propagateTestTimerMaxmemoryCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModuleTimerID timer_id = - RedisModule_CreateTimer(ctx,100,timerHandlerMaxmemory,(void*)1); - REDISMODULE_NOT_USED(timer_id); - - RedisModule_ReplyWithSimpleString(ctx,"OK"); - return REDISMODULE_OK; -} - -void timerHandlerEval(RedisModuleCtx *ctx, void *data) { - REDISMODULE_NOT_USED(ctx); - REDISMODULE_NOT_USED(data); - - RedisModuleCallReply *reply = RedisModule_Call(ctx,"INCRBY","cc!","timer-eval-start","1"); - RedisModule_FreeCallReply(reply); - reply = RedisModule_Call(ctx, "EVAL", "cccc!", "redis.call('set',KEYS[1],ARGV[1])", "1", "foo", "bar"); - RedisModule_FreeCallReply(reply); - - RedisModule_Replicate(ctx, "INCR", "c", "timer-eval-middle"); - - reply = RedisModule_Call(ctx,"INCRBY","cc!","timer-eval-end","1"); - RedisModule_FreeCallReply(reply); -} - -int propagateTestTimerEvalCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModuleTimerID timer_id = - RedisModule_CreateTimer(ctx,100,timerHandlerEval,(void*)1); - REDISMODULE_NOT_USED(timer_id); - - RedisModule_ReplyWithSimpleString(ctx,"OK"); - return REDISMODULE_OK; -} - -/* The thread entry point. */ -void *threadMain(void *arg) { - REDISMODULE_NOT_USED(arg); - RedisModuleCtx *ctx = RedisModule_GetThreadSafeContext(NULL); - RedisModule_SelectDb(ctx,9); /* Tests ran in database number 9. */ - for (int i = 0; i < 3; i++) { - RedisModule_ThreadSafeContextLock(ctx); - RedisModule_Replicate(ctx,"INCR","c","a-from-thread"); - RedisModuleCallReply *reply = RedisModule_Call(ctx,"INCR","c!","thread-call"); - RedisModule_FreeCallReply(reply); - RedisModule_Replicate(ctx,"INCR","c","b-from-thread"); - RedisModule_ThreadSafeContextUnlock(ctx); - } - RedisModule_FreeThreadSafeContext(ctx); - return NULL; -} - -int propagateTestThreadCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - pthread_t tid; - if (pthread_create(&tid,NULL,threadMain,NULL) != 0) - return RedisModule_ReplyWithError(ctx,"-ERR Can't start thread"); - pthread_detach(tid); - - RedisModule_ReplyWithSimpleString(ctx,"OK"); - return REDISMODULE_OK; -} - -/* The thread entry point. */ -void *threadDetachedMain(void *arg) { - REDISMODULE_NOT_USED(arg); - RedisModule_SelectDb(detached_ctx,9); /* Tests ran in database number 9. */ - - RedisModule_ThreadSafeContextLock(detached_ctx); - RedisModule_Replicate(detached_ctx,"INCR","c","thread-detached-before"); - RedisModuleCallReply *reply = RedisModule_Call(detached_ctx,"INCR","c!","thread-detached-1"); - RedisModule_FreeCallReply(reply); - reply = RedisModule_Call(detached_ctx,"INCR","c!","thread-detached-2"); - RedisModule_FreeCallReply(reply); - RedisModule_Replicate(detached_ctx,"INCR","c","thread-detached-after"); - RedisModule_ThreadSafeContextUnlock(detached_ctx); - - return NULL; -} - -int propagateTestDetachedThreadCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - pthread_t tid; - if (pthread_create(&tid,NULL,threadDetachedMain,NULL) != 0) - return RedisModule_ReplyWithError(ctx,"-ERR Can't start thread"); - pthread_detach(tid); - - RedisModule_ReplyWithSimpleString(ctx,"OK"); - return REDISMODULE_OK; -} - -int propagateTestSimpleCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - /* Replicate two commands to test MULTI/EXEC wrapping. */ - RedisModule_Replicate(ctx,"INCR","c","counter-1"); - RedisModule_Replicate(ctx,"INCR","c","counter-2"); - RedisModule_ReplyWithSimpleString(ctx,"OK"); - return REDISMODULE_OK; -} - -int propagateTestMixedCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - RedisModuleCallReply *reply; - - /* This test mixes multiple propagation systems. */ - reply = RedisModule_Call(ctx, "INCR", "c!", "using-call"); - RedisModule_FreeCallReply(reply); - - RedisModule_Replicate(ctx,"INCR","c","counter-1"); - RedisModule_Replicate(ctx,"INCR","c","counter-2"); - - reply = RedisModule_Call(ctx, "INCR", "c!", "after-call"); - RedisModule_FreeCallReply(reply); - - RedisModule_ReplyWithSimpleString(ctx,"OK"); - return REDISMODULE_OK; -} - -int propagateTestNestedCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - RedisModuleCallReply *reply; - - /* This test mixes multiple propagation systems. */ - reply = RedisModule_Call(ctx, "INCR", "c!", "using-call"); - RedisModule_FreeCallReply(reply); - - reply = RedisModule_Call(ctx,"propagate-test.simple", "!"); - RedisModule_FreeCallReply(reply); - - RedisModule_Replicate(ctx,"INCR","c","counter-3"); - RedisModule_Replicate(ctx,"INCR","c","counter-4"); - - reply = RedisModule_Call(ctx, "INCR", "c!", "after-call"); - RedisModule_FreeCallReply(reply); - - reply = RedisModule_Call(ctx, "INCR", "c!", "before-call-2"); - RedisModule_FreeCallReply(reply); - - reply = RedisModule_Call(ctx, "keyspace.incr_case1", "c!", "asdf"); /* Propagates INCR */ - RedisModule_FreeCallReply(reply); - - reply = RedisModule_Call(ctx, "keyspace.del_key_copy", "c!", "asdf"); /* Propagates DEL */ - RedisModule_FreeCallReply(reply); - - reply = RedisModule_Call(ctx, "INCR", "c!", "after-call-2"); - RedisModule_FreeCallReply(reply); - - RedisModule_ReplyWithSimpleString(ctx,"OK"); - return REDISMODULE_OK; -} - -int propagateTestIncr(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - REDISMODULE_NOT_USED(argc); - RedisModuleCallReply *reply; - - /* This test propagates the module command, not the INCR it executes. */ - reply = RedisModule_Call(ctx, "INCR", "s", argv[1]); - RedisModule_ReplyWithCallReply(ctx,reply); - RedisModule_FreeCallReply(reply); - RedisModule_ReplicateVerbatim(ctx); - return REDISMODULE_OK; -} - -int propagateTestVerbatim(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc < 2){ - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - long long replicate_num; - RedisModule_StringToLongLong(argv[1], &replicate_num); - /* Replicate the command verbatim for the specified number of times. */ - for (long long i = 0; i < replicate_num; i++) - RedisModule_ReplicateVerbatim(ctx); - 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,"propagate-test",1,REDISMODULE_APIVER_1) - == REDISMODULE_ERR) return REDISMODULE_ERR; - - detached_ctx = RedisModule_GetDetachedThreadSafeContext(ctx); - - if (RedisModule_SubscribeToKeyspaceEvents(ctx, REDISMODULE_NOTIFY_ALL, KeySpace_NotificationGeneric) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"propagate-test.timer", - propagateTestTimerCommand, - "",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"propagate-test.timer-nested", - propagateTestTimerNestedCommand, - "",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"propagate-test.timer-nested-repl", - propagateTestTimerNestedReplCommand, - "",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"propagate-test.timer-maxmemory", - propagateTestTimerMaxmemoryCommand, - "",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"propagate-test.timer-eval", - propagateTestTimerEvalCommand, - "",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"propagate-test.thread", - propagateTestThreadCommand, - "",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"propagate-test.detached-thread", - propagateTestDetachedThreadCommand, - "",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"propagate-test.simple", - propagateTestSimpleCommand, - "",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"propagate-test.mixed", - propagateTestMixedCommand, - "write",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"propagate-test.nested", - propagateTestNestedCommand, - "write",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"propagate-test.incr", - propagateTestIncr, - "write",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"propagate-test.verbatim", - propagateTestVerbatim, - "write",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - return REDISMODULE_OK; -} - -int RedisModule_OnUnload(RedisModuleCtx *ctx) { - UNUSED(ctx); - - if (detached_ctx) - RedisModule_FreeThreadSafeContext(detached_ctx); - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/publish.c b/examples/redis-unstable/tests/modules/publish.c deleted file mode 100644 index ff276d8..0000000 --- a/examples/redis-unstable/tests/modules/publish.c +++ /dev/null @@ -1,57 +0,0 @@ -#include "redismodule.h" -#include <string.h> -#include <assert.h> -#include <unistd.h> - -#define UNUSED(V) ((void) V) - -int cmd_publish_classic_multi(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - if (argc < 3) - return RedisModule_WrongArity(ctx); - RedisModule_ReplyWithArray(ctx, argc-2); - for (int i = 2; i < argc; i++) { - int receivers = RedisModule_PublishMessage(ctx, argv[1], argv[i]); - RedisModule_ReplyWithLongLong(ctx, receivers); - } - return REDISMODULE_OK; -} - -int cmd_publish_classic(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - if (argc != 3) - return RedisModule_WrongArity(ctx); - - int receivers = RedisModule_PublishMessage(ctx, argv[1], argv[2]); - RedisModule_ReplyWithLongLong(ctx, receivers); - return REDISMODULE_OK; -} - -int cmd_publish_shard(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - if (argc != 3) - return RedisModule_WrongArity(ctx); - - int receivers = RedisModule_PublishMessageShard(ctx, argv[1], argv[2]); - RedisModule_ReplyWithLongLong(ctx, receivers); - return REDISMODULE_OK; -} - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - UNUSED(argv); - UNUSED(argc); - - if (RedisModule_Init(ctx,"publish",1,REDISMODULE_APIVER_1)== REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"publish.classic",cmd_publish_classic,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"publish.classic_multi",cmd_publish_classic_multi,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"publish.shard",cmd_publish_shard,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/rdbloadsave.c b/examples/redis-unstable/tests/modules/rdbloadsave.c deleted file mode 100644 index 687269a..0000000 --- a/examples/redis-unstable/tests/modules/rdbloadsave.c +++ /dev/null @@ -1,162 +0,0 @@ -#include "redismodule.h" - -#include <stdlib.h> -#include <unistd.h> -#include <fcntl.h> -#include <memory.h> -#include <errno.h> - -/* Sanity tests to verify inputs and return values. */ -int sanity(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - RedisModuleRdbStream *s = RedisModule_RdbStreamCreateFromFile("dbnew.rdb"); - - /* NULL stream should fail. */ - if (RedisModule_RdbLoad(ctx, NULL, 0) == REDISMODULE_OK || errno != EINVAL) { - RedisModule_ReplyWithError(ctx, strerror(errno)); - goto out; - } - - /* Invalid flags should fail. */ - if (RedisModule_RdbLoad(ctx, s, 188) == REDISMODULE_OK || errno != EINVAL) { - RedisModule_ReplyWithError(ctx, strerror(errno)); - goto out; - } - - /* Missing file should fail. */ - if (RedisModule_RdbLoad(ctx, s, 0) == REDISMODULE_OK || errno != ENOENT) { - RedisModule_ReplyWithError(ctx, strerror(errno)); - goto out; - } - - /* Save RDB file. */ - if (RedisModule_RdbSave(ctx, s, 0) != REDISMODULE_OK || errno != 0) { - RedisModule_ReplyWithError(ctx, strerror(errno)); - goto out; - } - - /* Load the saved RDB file. */ - if (RedisModule_RdbLoad(ctx, s, 0) != REDISMODULE_OK || errno != 0) { - RedisModule_ReplyWithError(ctx, strerror(errno)); - goto out; - } - - RedisModule_ReplyWithSimpleString(ctx, "OK"); - - out: - RedisModule_RdbStreamFree(s); - return REDISMODULE_OK; -} - -int cmd_rdbsave(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - size_t len; - const char *filename = RedisModule_StringPtrLen(argv[1], &len); - - char tmp[len + 1]; - memcpy(tmp, filename, len); - tmp[len] = '\0'; - - RedisModuleRdbStream *stream = RedisModule_RdbStreamCreateFromFile(tmp); - - if (RedisModule_RdbSave(ctx, stream, 0) != REDISMODULE_OK || errno != 0) { - RedisModule_ReplyWithError(ctx, strerror(errno)); - goto out; - } - - RedisModule_ReplyWithSimpleString(ctx, "OK"); - -out: - RedisModule_RdbStreamFree(stream); - return REDISMODULE_OK; -} - -/* Fork before calling RM_RdbSave(). */ -int cmd_rdbsave_fork(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - size_t len; - const char *filename = RedisModule_StringPtrLen(argv[1], &len); - - char tmp[len + 1]; - memcpy(tmp, filename, len); - tmp[len] = '\0'; - - int fork_child_pid = RedisModule_Fork(NULL, NULL); - if (fork_child_pid < 0) { - RedisModule_ReplyWithError(ctx, strerror(errno)); - return REDISMODULE_OK; - } else if (fork_child_pid > 0) { - /* parent */ - RedisModule_ReplyWithSimpleString(ctx, "OK"); - return REDISMODULE_OK; - } - - RedisModuleRdbStream *stream = RedisModule_RdbStreamCreateFromFile(tmp); - - int ret = 0; - if (RedisModule_RdbSave(ctx, stream, 0) != REDISMODULE_OK) { - ret = errno; - } - RedisModule_RdbStreamFree(stream); - - RedisModule_ExitFromChild(ret); - return REDISMODULE_OK; -} - -int cmd_rdbload(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - size_t len; - const char *filename = RedisModule_StringPtrLen(argv[1], &len); - - char tmp[len + 1]; - memcpy(tmp, filename, len); - tmp[len] = '\0'; - - RedisModuleRdbStream *stream = RedisModule_RdbStreamCreateFromFile(tmp); - - if (RedisModule_RdbLoad(ctx, stream, 0) != REDISMODULE_OK || errno != 0) { - RedisModule_RdbStreamFree(stream); - RedisModule_ReplyWithError(ctx, strerror(errno)); - return REDISMODULE_OK; - } - - RedisModule_RdbStreamFree(stream); - 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, "rdbloadsave", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "test.sanity", sanity, "", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "test.rdbsave", cmd_rdbsave, "", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "test.rdbsave_fork", cmd_rdbsave_fork, "", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "test.rdbload", cmd_rdbload, "", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/reply.c b/examples/redis-unstable/tests/modules/reply.c deleted file mode 100644 index c5baa66..0000000 --- a/examples/redis-unstable/tests/modules/reply.c +++ /dev/null @@ -1,214 +0,0 @@ -/* - * A module the tests RM_ReplyWith family of commands - */ - -#include "redismodule.h" -#include <math.h> - -int rw_string(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) return RedisModule_WrongArity(ctx); - - return RedisModule_ReplyWithString(ctx, argv[1]); -} - -int rw_cstring(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - if (argc != 1) return RedisModule_WrongArity(ctx); - - return RedisModule_ReplyWithSimpleString(ctx, "A simple string"); -} - -int rw_int(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) return RedisModule_WrongArity(ctx); - - long long integer; - if (RedisModule_StringToLongLong(argv[1], &integer) != REDISMODULE_OK) - return RedisModule_ReplyWithError(ctx, "Arg cannot be parsed as an integer"); - - return RedisModule_ReplyWithLongLong(ctx, integer); -} - -/* When one argument is given, it is returned as a double, - * when two arguments are given, it returns a/b. */ -int rw_double(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc==1) - return RedisModule_ReplyWithDouble(ctx, NAN); - - if (argc != 2 && argc != 3) return RedisModule_WrongArity(ctx); - - double dbl, dbl2; - if (RedisModule_StringToDouble(argv[1], &dbl) != REDISMODULE_OK) - return RedisModule_ReplyWithError(ctx, "Arg cannot be parsed as a double"); - if (argc == 3) { - if (RedisModule_StringToDouble(argv[2], &dbl2) != REDISMODULE_OK) - return RedisModule_ReplyWithError(ctx, "Arg cannot be parsed as a double"); - dbl /= dbl2; - } - - return RedisModule_ReplyWithDouble(ctx, dbl); -} - -int rw_longdouble(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) return RedisModule_WrongArity(ctx); - - long double longdbl; - if (RedisModule_StringToLongDouble(argv[1], &longdbl) != REDISMODULE_OK) - return RedisModule_ReplyWithError(ctx, "Arg cannot be parsed as a double"); - - return RedisModule_ReplyWithLongDouble(ctx, longdbl); -} - -int rw_bignumber(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) return RedisModule_WrongArity(ctx); - - size_t bignum_len; - const char *bignum_str = RedisModule_StringPtrLen(argv[1], &bignum_len); - - return RedisModule_ReplyWithBigNumber(ctx, bignum_str, bignum_len); -} - -int rw_array(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) return RedisModule_WrongArity(ctx); - - long long integer; - if (RedisModule_StringToLongLong(argv[1], &integer) != REDISMODULE_OK) - return RedisModule_ReplyWithError(ctx, "Arg cannot be parsed as a integer"); - - RedisModule_ReplyWithArray(ctx, integer); - for (int i = 0; i < integer; ++i) { - RedisModule_ReplyWithLongLong(ctx, i); - } - - return REDISMODULE_OK; -} - -int rw_map(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) return RedisModule_WrongArity(ctx); - - long long integer; - if (RedisModule_StringToLongLong(argv[1], &integer) != REDISMODULE_OK) - return RedisModule_ReplyWithError(ctx, "Arg cannot be parsed as a integer"); - - RedisModule_ReplyWithMap(ctx, integer); - for (int i = 0; i < integer; ++i) { - RedisModule_ReplyWithLongLong(ctx, i); - RedisModule_ReplyWithDouble(ctx, i * 1.5); - } - - return REDISMODULE_OK; -} - -int rw_set(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) return RedisModule_WrongArity(ctx); - - long long integer; - if (RedisModule_StringToLongLong(argv[1], &integer) != REDISMODULE_OK) - return RedisModule_ReplyWithError(ctx, "Arg cannot be parsed as a integer"); - - RedisModule_ReplyWithSet(ctx, integer); - for (int i = 0; i < integer; ++i) { - RedisModule_ReplyWithLongLong(ctx, i); - } - - return REDISMODULE_OK; -} - -int rw_attribute(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) return RedisModule_WrongArity(ctx); - - long long integer; - if (RedisModule_StringToLongLong(argv[1], &integer) != REDISMODULE_OK) - return RedisModule_ReplyWithError(ctx, "Arg cannot be parsed as a integer"); - - if (RedisModule_ReplyWithAttribute(ctx, integer) != REDISMODULE_OK) { - return RedisModule_ReplyWithError(ctx, "Attributes aren't supported by RESP 2"); - } - - for (int i = 0; i < integer; ++i) { - RedisModule_ReplyWithLongLong(ctx, i); - RedisModule_ReplyWithDouble(ctx, i * 1.5); - } - - RedisModule_ReplyWithSimpleString(ctx, "OK"); - return REDISMODULE_OK; -} - -int rw_bool(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - if (argc != 1) return RedisModule_WrongArity(ctx); - - RedisModule_ReplyWithArray(ctx, 2); - RedisModule_ReplyWithBool(ctx, 0); - return RedisModule_ReplyWithBool(ctx, 1); -} - -int rw_null(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - if (argc != 1) return RedisModule_WrongArity(ctx); - - return RedisModule_ReplyWithNull(ctx); -} - -int rw_error(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - if (argc != 1) return RedisModule_WrongArity(ctx); - - return RedisModule_ReplyWithError(ctx, "An error"); -} - -int rw_error_format(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 3) return RedisModule_WrongArity(ctx); - - return RedisModule_ReplyWithErrorFormat(ctx, - RedisModule_StringPtrLen(argv[1], NULL), - RedisModule_StringPtrLen(argv[2], NULL)); -} - -int rw_verbatim(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) return RedisModule_WrongArity(ctx); - - size_t verbatim_len; - const char *verbatim_str = RedisModule_StringPtrLen(argv[1], &verbatim_len); - - return RedisModule_ReplyWithVerbatimString(ctx, verbatim_str, verbatim_len); -} - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - if (RedisModule_Init(ctx, "replywith", 1, REDISMODULE_APIVER_1) != REDISMODULE_OK) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"rw.string",rw_string,"",0,0,0) != REDISMODULE_OK) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx,"rw.cstring",rw_cstring,"",0,0,0) != REDISMODULE_OK) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx,"rw.bignumber",rw_bignumber,"",0,0,0) != REDISMODULE_OK) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx,"rw.int",rw_int,"",0,0,0) != REDISMODULE_OK) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx,"rw.double",rw_double,"",0,0,0) != REDISMODULE_OK) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx,"rw.longdouble",rw_longdouble,"",0,0,0) != REDISMODULE_OK) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx,"rw.array",rw_array,"",0,0,0) != REDISMODULE_OK) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx,"rw.map",rw_map,"",0,0,0) != REDISMODULE_OK) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx,"rw.attribute",rw_attribute,"",0,0,0) != REDISMODULE_OK) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx,"rw.set",rw_set,"",0,0,0) != REDISMODULE_OK) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx,"rw.bool",rw_bool,"",0,0,0) != REDISMODULE_OK) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx,"rw.null",rw_null,"",0,0,0) != REDISMODULE_OK) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx,"rw.error",rw_error,"",0,0,0) != REDISMODULE_OK) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx,"rw.error_format",rw_error_format,"",0,0,0) != REDISMODULE_OK) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx,"rw.verbatim",rw_verbatim,"",0,0,0) != REDISMODULE_OK) - return REDISMODULE_ERR; - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/scan.c b/examples/redis-unstable/tests/modules/scan.c deleted file mode 100644 index 1723d30..0000000 --- a/examples/redis-unstable/tests/modules/scan.c +++ /dev/null @@ -1,121 +0,0 @@ -#include "redismodule.h" - -#include <string.h> -#include <assert.h> -#include <unistd.h> - -typedef struct { - size_t nkeys; -} scan_strings_pd; - -void scan_strings_callback(RedisModuleCtx *ctx, RedisModuleString* keyname, RedisModuleKey* key, void *privdata) { - scan_strings_pd* pd = privdata; - int was_opened = 0; - if (!key) { - key = RedisModule_OpenKey(ctx, keyname, REDISMODULE_READ); - was_opened = 1; - } - - if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_STRING) { - size_t len; - char * data = RedisModule_StringDMA(key, &len, REDISMODULE_READ); - RedisModule_ReplyWithArray(ctx, 2); - RedisModule_ReplyWithString(ctx, keyname); - RedisModule_ReplyWithStringBuffer(ctx, data, len); - pd->nkeys++; - } - if (was_opened) - RedisModule_CloseKey(key); -} - -int scan_strings(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - scan_strings_pd pd = { - .nkeys = 0, - }; - - RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_LEN); - - RedisModuleScanCursor* cursor = RedisModule_ScanCursorCreate(); - while(RedisModule_Scan(ctx, cursor, scan_strings_callback, &pd)); - RedisModule_ScanCursorDestroy(cursor); - - RedisModule_ReplySetArrayLength(ctx, pd.nkeys); - return REDISMODULE_OK; -} - -typedef struct { - RedisModuleCtx *ctx; - size_t nreplies; -} scan_key_pd; - -void scan_key_callback(RedisModuleKey *key, RedisModuleString* field, RedisModuleString* value, void *privdata) { - REDISMODULE_NOT_USED(key); - scan_key_pd* pd = privdata; - RedisModule_ReplyWithArray(pd->ctx, 2); - size_t fieldCStrLen; - - // The implementation of RedisModuleString is robj with lots of encodings. - // We want to make sure the robj that passes to this callback in - // String encoded, this is why we use RedisModule_StringPtrLen and - // RedisModule_ReplyWithStringBuffer instead of directly use - // RedisModule_ReplyWithString. - const char* fieldCStr = RedisModule_StringPtrLen(field, &fieldCStrLen); - RedisModule_ReplyWithStringBuffer(pd->ctx, fieldCStr, fieldCStrLen); - if(value){ - size_t valueCStrLen; - const char* valueCStr = RedisModule_StringPtrLen(value, &valueCStrLen); - RedisModule_ReplyWithStringBuffer(pd->ctx, valueCStr, valueCStrLen); - } else { - RedisModule_ReplyWithNull(pd->ctx); - } - - pd->nreplies++; -} - -int scan_key(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - if (argc != 2) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - scan_key_pd pd = { - .ctx = ctx, - .nreplies = 0, - }; - - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ); - if (!key) { - RedisModule_ReplyWithError(ctx, "not found"); - return REDISMODULE_OK; - } - - RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN); - - RedisModuleScanCursor* cursor = RedisModule_ScanCursorCreate(); - while(RedisModule_ScanKey(key, cursor, scan_key_callback, &pd)); - RedisModule_ScanCursorDestroy(cursor); - - RedisModule_ReplySetArrayLength(ctx, pd.nreplies); - RedisModule_CloseKey(key); - return REDISMODULE_OK; -} - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - if (RedisModule_Init(ctx, "scan", 1, REDISMODULE_APIVER_1)== REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "scan.scan_strings", scan_strings, "", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "scan.scan_key", scan_key, "", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - return REDISMODULE_OK; -} - - diff --git a/examples/redis-unstable/tests/modules/stream.c b/examples/redis-unstable/tests/modules/stream.c deleted file mode 100644 index 65762a3..0000000 --- a/examples/redis-unstable/tests/modules/stream.c +++ /dev/null @@ -1,258 +0,0 @@ -#include "redismodule.h" - -#include <string.h> -#include <strings.h> -#include <assert.h> -#include <unistd.h> -#include <errno.h> - -/* Command which adds a stream entry with automatic ID, like XADD *. - * - * Syntax: STREAM.ADD key field1 value1 [ field2 value2 ... ] - * - * The response is the ID of the added stream entry or an error message. - */ -int stream_add(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc < 2 || argc % 2 != 0) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE); - RedisModuleStreamID id; - if (RedisModule_StreamAdd(key, REDISMODULE_STREAM_ADD_AUTOID, &id, - &argv[2], (argc-2)/2) == REDISMODULE_OK) { - RedisModuleString *id_str = RedisModule_CreateStringFromStreamID(ctx, &id); - RedisModule_ReplyWithString(ctx, id_str); - RedisModule_FreeString(ctx, id_str); - } else { - RedisModule_ReplyWithError(ctx, "ERR StreamAdd failed"); - } - RedisModule_CloseKey(key); - return REDISMODULE_OK; -} - -/* Command which adds a stream entry N times. - * - * Syntax: STREAM.ADD key N field1 value1 [ field2 value2 ... ] - * - * Returns the number of successfully added entries. - */ -int stream_addn(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc < 3 || argc % 2 == 0) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - long long n, i; - if (RedisModule_StringToLongLong(argv[2], &n) == REDISMODULE_ERR) { - RedisModule_ReplyWithError(ctx, "N must be a number"); - return REDISMODULE_OK; - } - - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE); - for (i = 0; i < n; i++) { - if (RedisModule_StreamAdd(key, REDISMODULE_STREAM_ADD_AUTOID, NULL, - &argv[3], (argc-3)/2) == REDISMODULE_ERR) - break; - } - RedisModule_ReplyWithLongLong(ctx, i); - RedisModule_CloseKey(key); - return REDISMODULE_OK; -} - -/* STREAM.DELETE key stream-id */ -int stream_delete(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 3) return RedisModule_WrongArity(ctx); - RedisModuleStreamID id; - if (RedisModule_StringToStreamID(argv[2], &id) != REDISMODULE_OK) { - return RedisModule_ReplyWithError(ctx, "Invalid stream ID"); - } - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE); - if (RedisModule_StreamDelete(key, &id) == REDISMODULE_OK) { - RedisModule_ReplyWithSimpleString(ctx, "OK"); - } else { - RedisModule_ReplyWithError(ctx, "ERR StreamDelete failed"); - } - RedisModule_CloseKey(key); - return REDISMODULE_OK; -} - -/* STREAM.RANGE key start-id end-id - * - * Returns an array of stream items. Each item is an array on the form - * [stream-id, [field1, value1, field2, value2, ...]]. - * - * A funny side-effect used for testing RM_StreamIteratorDelete() is that if any - * entry has a field named "selfdestruct", the stream entry is deleted. It is - * however included in the results of this command. - */ -int stream_range(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 4) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - RedisModuleStreamID startid, endid; - if (RedisModule_StringToStreamID(argv[2], &startid) != REDISMODULE_OK || - RedisModule_StringToStreamID(argv[3], &endid) != REDISMODULE_OK) { - RedisModule_ReplyWithError(ctx, "Invalid stream ID"); - return REDISMODULE_OK; - } - - /* If startid > endid, we swap and set the reverse flag. */ - int flags = 0; - if (startid.ms > endid.ms || - (startid.ms == endid.ms && startid.seq > endid.seq)) { - RedisModuleStreamID tmp = startid; - startid = endid; - endid = tmp; - flags |= REDISMODULE_STREAM_ITERATOR_REVERSE; - } - - /* Open key and start iterator. */ - int openflags = REDISMODULE_READ | REDISMODULE_WRITE; - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], openflags); - if (RedisModule_StreamIteratorStart(key, flags, - &startid, &endid) != REDISMODULE_OK) { - /* Key is not a stream, etc. */ - RedisModule_ReplyWithError(ctx, "ERR StreamIteratorStart failed"); - RedisModule_CloseKey(key); - return REDISMODULE_OK; - } - - /* Check error handling: Delete current entry when no current entry. */ - assert(RedisModule_StreamIteratorDelete(key) == - REDISMODULE_ERR); - assert(errno == ENOENT); - - /* Check error handling: Fetch fields when no current entry. */ - assert(RedisModule_StreamIteratorNextField(key, NULL, NULL) == - REDISMODULE_ERR); - assert(errno == ENOENT); - - /* Return array. */ - RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_LEN); - RedisModule_AutoMemory(ctx); - RedisModuleStreamID id; - long numfields; - long len = 0; - while (RedisModule_StreamIteratorNextID(key, &id, - &numfields) == REDISMODULE_OK) { - RedisModule_ReplyWithArray(ctx, 2); - RedisModuleString *id_str = RedisModule_CreateStringFromStreamID(ctx, &id); - RedisModule_ReplyWithString(ctx, id_str); - RedisModule_ReplyWithArray(ctx, numfields * 2); - int delete = 0; - RedisModuleString *field, *value; - for (long i = 0; i < numfields; i++) { - assert(RedisModule_StreamIteratorNextField(key, &field, &value) == - REDISMODULE_OK); - RedisModule_ReplyWithString(ctx, field); - RedisModule_ReplyWithString(ctx, value); - /* check if this is a "selfdestruct" field */ - size_t field_len; - const char *field_str = RedisModule_StringPtrLen(field, &field_len); - if (!strncmp(field_str, "selfdestruct", field_len)) delete = 1; - } - if (delete) { - assert(RedisModule_StreamIteratorDelete(key) == REDISMODULE_OK); - } - /* check error handling: no more fields to fetch */ - assert(RedisModule_StreamIteratorNextField(key, &field, &value) == - REDISMODULE_ERR); - assert(errno == ENOENT); - len++; - } - RedisModule_ReplySetArrayLength(ctx, len); - RedisModule_StreamIteratorStop(key); - RedisModule_CloseKey(key); - return REDISMODULE_OK; -} - -/* - * STREAM.TRIM key (MAXLEN (=|~) length | MINID (=|~) id) - */ -int stream_trim(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 5) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - /* Parse args */ - int trim_by_id = 0; /* 0 = maxlen, 1 = minid */ - long long maxlen; - RedisModuleStreamID minid; - size_t arg_len; - const char *arg = RedisModule_StringPtrLen(argv[2], &arg_len); - if (!strcasecmp(arg, "minid")) { - trim_by_id = 1; - if (RedisModule_StringToStreamID(argv[4], &minid) != REDISMODULE_OK) { - RedisModule_ReplyWithError(ctx, "ERR Invalid stream ID"); - return REDISMODULE_OK; - } - } else if (!strcasecmp(arg, "maxlen")) { - if (RedisModule_StringToLongLong(argv[4], &maxlen) == REDISMODULE_ERR) { - RedisModule_ReplyWithError(ctx, "ERR Maxlen must be a number"); - return REDISMODULE_OK; - } - } else { - RedisModule_ReplyWithError(ctx, "ERR Invalid arguments"); - return REDISMODULE_OK; - } - - /* Approx or exact */ - int flags; - arg = RedisModule_StringPtrLen(argv[3], &arg_len); - if (arg_len == 1 && arg[0] == '~') { - flags = REDISMODULE_STREAM_TRIM_APPROX; - } else if (arg_len == 1 && arg[0] == '=') { - flags = 0; - } else { - RedisModule_ReplyWithError(ctx, "ERR Invalid approx-or-exact mark"); - return REDISMODULE_OK; - } - - /* Trim */ - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE); - long long trimmed; - if (trim_by_id) { - trimmed = RedisModule_StreamTrimByID(key, flags, &minid); - } else { - trimmed = RedisModule_StreamTrimByLength(key, flags, maxlen); - } - - /* Return result */ - if (trimmed < 0) { - RedisModule_ReplyWithError(ctx, "ERR Trimming failed"); - } else { - RedisModule_ReplyWithLongLong(ctx, trimmed); - } - RedisModule_CloseKey(key); - return REDISMODULE_OK; -} - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - if (RedisModule_Init(ctx, "stream", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "stream.add", stream_add, "write", - 1, 1, 1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx, "stream.addn", stream_addn, "write", - 1, 1, 1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx, "stream.delete", stream_delete, "write", - 1, 1, 1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx, "stream.range", stream_range, "write", - 1, 1, 1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx, "stream.trim", stream_trim, "write", - 1, 1, 1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/subcommands.c b/examples/redis-unstable/tests/modules/subcommands.c deleted file mode 100644 index 0961032..0000000 --- a/examples/redis-unstable/tests/modules/subcommands.c +++ /dev/null @@ -1,119 +0,0 @@ -#include "redismodule.h" - -#define UNUSED(V) ((void) V) - -int cmd_set(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - UNUSED(argv); - UNUSED(argc); - RedisModule_ReplyWithSimpleString(ctx, "OK"); - return REDISMODULE_OK; -} - -int cmd_get(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - UNUSED(argv); - - if (argc > 4) /* For testing */ - return RedisModule_WrongArity(ctx); - - RedisModule_ReplyWithSimpleString(ctx, "OK"); - return REDISMODULE_OK; -} - -int cmd_get_fullname(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - UNUSED(argv); - UNUSED(argc); - - const char *command_name = RedisModule_GetCurrentCommandName(ctx); - RedisModule_ReplyWithSimpleString(ctx, command_name); - return REDISMODULE_OK; -} - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - if (RedisModule_Init(ctx, "subcommands", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - /* Module command names cannot contain special characters. */ - RedisModule_Assert(RedisModule_CreateCommand(ctx,"subcommands.char\r",NULL,"",0,0,0) == REDISMODULE_ERR); - RedisModule_Assert(RedisModule_CreateCommand(ctx,"subcommands.char\n",NULL,"",0,0,0) == REDISMODULE_ERR); - RedisModule_Assert(RedisModule_CreateCommand(ctx,"subcommands.char ",NULL,"",0,0,0) == REDISMODULE_ERR); - - if (RedisModule_CreateCommand(ctx,"subcommands.bitarray",NULL,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - RedisModuleCommand *parent = RedisModule_GetCommand(ctx,"subcommands.bitarray"); - - if (RedisModule_CreateSubcommand(parent,"set",cmd_set,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - /* Module subcommand names cannot contain special characters. */ - RedisModule_Assert(RedisModule_CreateSubcommand(parent,"char|",cmd_set,"",0,0,0) == REDISMODULE_ERR); - RedisModule_Assert(RedisModule_CreateSubcommand(parent,"char@",cmd_set,"",0,0,0) == REDISMODULE_ERR); - RedisModule_Assert(RedisModule_CreateSubcommand(parent,"char=",cmd_set,"",0,0,0) == REDISMODULE_ERR); - - RedisModuleCommand *subcmd = RedisModule_GetCommand(ctx,"subcommands.bitarray|set"); - RedisModuleCommandInfo cmd_set_info = { - .version = REDISMODULE_COMMAND_INFO_VERSION, - .key_specs = (RedisModuleCommandKeySpec[]){ - { - .flags = REDISMODULE_CMD_KEY_RW | REDISMODULE_CMD_KEY_UPDATE, - .begin_search_type = REDISMODULE_KSPEC_BS_INDEX, - .bs.index.pos = 1, - .find_keys_type = REDISMODULE_KSPEC_FK_RANGE, - .fk.range = {0,1,0} - }, - {0} - } - }; - if (RedisModule_SetCommandInfo(subcmd, &cmd_set_info) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateSubcommand(parent,"get",cmd_get,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - subcmd = RedisModule_GetCommand(ctx,"subcommands.bitarray|get"); - RedisModuleCommandInfo cmd_get_info = { - .version = REDISMODULE_COMMAND_INFO_VERSION, - .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} - }, - {0} - } - }; - if (RedisModule_SetCommandInfo(subcmd, &cmd_get_info) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - /* Get the name of the command currently running. */ - if (RedisModule_CreateCommand(ctx,"subcommands.parent_get_fullname",cmd_get_fullname,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - /* Get the name of the subcommand currently running. */ - if (RedisModule_CreateCommand(ctx,"subcommands.sub",NULL,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - RedisModuleCommand *fullname_parent = RedisModule_GetCommand(ctx,"subcommands.sub"); - if (RedisModule_CreateSubcommand(fullname_parent,"get_fullname",cmd_get_fullname,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - /* Sanity */ - - /* Trying to create the same subcommand fails */ - RedisModule_Assert(RedisModule_CreateSubcommand(parent,"get",NULL,"",0,0,0) == REDISMODULE_ERR); - - /* Trying to create a sub-subcommand fails */ - RedisModule_Assert(RedisModule_CreateSubcommand(subcmd,"get",NULL,"",0,0,0) == REDISMODULE_ERR); - - /* Internal container command for testing */ - if (RedisModule_CreateCommand(ctx,"subcommands.internal_container",NULL,"internal",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - RedisModuleCommand *internal_parent = RedisModule_GetCommand(ctx,"subcommands.internal_container"); - if (RedisModule_CreateSubcommand(internal_parent,"test",cmd_set,"internal",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/test_keymeta.c b/examples/redis-unstable/tests/modules/test_keymeta.c deleted file mode 100644 index ca5d622..0000000 --- a/examples/redis-unstable/tests/modules/test_keymeta.c +++ /dev/null @@ -1,585 +0,0 @@ -/* An example module for attaching metadata to keys. - * - * This example lets tests create metadata-key classes and then SET and GET metadata - * to keys. The 8-byte slot stores a handle to a module-managed allocation; here - * we use to attach a string per-key. - * - * The module pre-registers several metadata classes during initialization and exposes - * the following commands (via RedisModule_CreateCommand): - * - * 1) KEYMETA.REGISTER <4-char-id> <version> [FLAGS] - * Register a new metadata-key class during module load. - * Returns the <keymeta-class-id> index (Returned from RedisModule_CreateKeyMetaClass) - * On failure, returns nil - * In a real module it should be registered "automatically" via OnLoad. - * - * FLAGS (colon-separated): - * KEEPONCOPY - Keep metadata on COPY operation - * KEEPONRENAME - Keep metadata on RENAME operation - * KEEPONMOVE - Keep metadata on MOVE operation - * UNLINKFREE - Use unlink callback for async free - * RDBLOAD - Enable rdb_load callback (metadata can be loaded from RDB) - * RDBSAVE - Enable rdb_save callback (metadata can be saved to RDB) - * ALLOWIGNORE - Enable ALLOW_IGNORE flag (graceful discard on load if - * class not registered or no rdb_load callback) - * - * Example: > keymeta.register KMT1 1 KEEPONCOPY:KEEPONRENAME:ALLOWIGNORE:RDBLOAD:RDBSAVE - * Example: > keymeta.register KMT2 1 ALLOWIGNORE - * - * 2) KEYMETA.SET <4-char-id> <key> <string-value> - * Set the string value as metadata to given key. - * Note: - * - If already set earlier, then it is expected that it will released before setting a - * new string. That is why this command should start with trying to get first - * metadata for given key. - * - * 3) KEYMETA.GET <4-char-id> <key> - * Get the metadata attached to the key for the given class. - * Returns a string attached to the given key. Or nil if nothing is attached. - * - * 4) KEYMETA.UNREGISTER <4-char-id> - * This will mark the key metadata class as released. It can later be reused again - * by the same class (consider comment above). - * Return REDISMODULE_OK/REDISMODULE_ERR. - * - * 5) KEYMETA.ACTIVE - * Return total number of active metadata at the moment. - */ - -#include "redismodule.h" -#include <string.h> -#include <stdlib.h> -#include <assert.h> - -/* Virtualize class IDs for testing. Values: 0 unused, 1..7 used, -1 released */ -RedisModuleKeyMetaClassId class_ids[8] = { 0 }; - -/* Mapping from 4-char-id to class-id */ -typedef struct { - char name[5]; /* 4 chars + null terminator */ - RedisModuleKeyMetaClassId class_id; -} ClassMapping; - -#define MAX_CLASS_MAPPINGS 8 -static ClassMapping class_mappings[MAX_CLASS_MAPPINGS]; -static int num_class_mappings = 0; - -/* Reverse lookup: given a class_id, find the 4-char-id name */ -static const char* lookupClassName(RedisModuleKeyMetaClassId class_id) { - for (int i = 0; i < num_class_mappings; i++) { - if (class_mappings[i].class_id == class_id) { - return class_mappings[i].name; - } - } - return NULL; -} - -/* Track active metadata instances (not yet freed) */ -static long long active_metadata_count = 0; - -/* Helper functions for class mapping */ - -/* Add a mapping from 4-char-id to class-id */ -static int addClassMapping(const char *name, RedisModuleKeyMetaClassId class_id) { - if (num_class_mappings >= MAX_CLASS_MAPPINGS) { - return 0; /* No space */ - } - strncpy(class_mappings[num_class_mappings].name, name, 4); - class_mappings[num_class_mappings].name[4] = '\0'; - class_mappings[num_class_mappings].class_id = class_id; - num_class_mappings++; - return 1; -} - -/* Lookup class-id by 4-char-id. Returns -1 if not found. */ -static RedisModuleKeyMetaClassId lookupClassId(const char *name) { - for (int i = 0; i < num_class_mappings; i++) { - if (strncmp(class_mappings[i].name, name, 4) == 0) { - return class_mappings[i].class_id; - } - } - return -1; -} - -/* Remove a mapping by 4-char-id */ -static int removeClassMapping(const char *name) { - for (int i = 0; i < num_class_mappings; i++) { - if (strncmp(class_mappings[i].name, name, 4) == 0) { - /* Shift remaining entries down */ - for (int j = i; j < num_class_mappings - 1; j++) { - class_mappings[j] = class_mappings[j + 1]; - } - num_class_mappings--; - return 1; - } - } - return 0; -} - -/* Callback functions for metadata lifecycle */ - -/* Copy callback - called when a key is copied */ -static int KeyMetaCopyCallback(RedisModuleKeyOptCtx *ctx, uint64_t *meta) { - REDISMODULE_NOT_USED(ctx); - char *str = (char *)*meta; - /* Note, condition is redundant since cb only invoked when meta != reset_value */ - if (str) { - char *new_str = strdup(str); - *meta = (uint64_t)new_str; - active_metadata_count++; /* New metadata instance created */ - } - return 1; /* Keep metadata */ -} - -/* Rename callback - called when a key is renamed. */ -static int KeyMetaRenameDiscardCallback(RedisModuleKeyOptCtx *ctx, uint64_t *meta) { - REDISMODULE_NOT_USED(ctx); - REDISMODULE_NOT_USED(meta); - return 0; -} - -/* Unlink callback - called when a key is unlinked */ -static void KeyMetaUnlinkCallback(RedisModuleKeyOptCtx *ctx, uint64_t *meta) { - /* Let's challenge and free early on before free callback */ - /* Note, condition is redundant since cb only invoked when meta != reset_value */ - if (*meta != 0) { - char *str = (char *)*meta; - free(str); - *meta = 0; /* Set to reset_value !!! */ - active_metadata_count--; /* Metadata instance freed */ - } - REDISMODULE_NOT_USED(ctx); -} - -/* Free callback - called when metadata needs to be freed */ -static void KeyMetaFreeCallback(const char *keyname, uint64_t meta) { - REDISMODULE_NOT_USED(keyname); - /* Note, condition is redundant since cb only invoked when meta != reset_value */ - if (meta != 0) { - char *str = (char *)meta; - free(str); - active_metadata_count--; /* Metadata instance freed */ - } -} - -static int KeyMetaMoveDiscardCallback(RedisModuleKeyOptCtx *ctx, uint64_t *meta) { - REDISMODULE_NOT_USED(ctx); - REDISMODULE_NOT_USED(meta); - return 0; /* discard metadata */ -} - -/* RDB Save Callback - Serialize metadata to RDB - * This callback is invoked during RDB save to write the metadata value. - * - * Parameters: - * - rdb: RedisModuleIO context for writing to RDB - * - value: The kvobj (key-value object) - not used in this implementation - * - meta: Pointer to the 8-byte metadata value (pointer to our string) - */ -static void KeyMetaRDBSaveCallback(RedisModuleIO *rdb, void *value, uint64_t *meta) { - REDISMODULE_NOT_USED(value); - - /* If metadata is NULL (reset_value), don't save anything */ - if (*meta == 0) return; - - /* Extract the string from the metadata pointer */ - char *metadata_string = (char *)*meta; - - /* Save the string to RDB using SaveStringBuffer */ - RedisModule_SaveStringBuffer(rdb, metadata_string, strlen(metadata_string)); - /* Save more silly data */ - RedisModule_SaveSigned(rdb, 1); - RedisModule_SaveFloat(rdb, 1.5); - RedisModule_SaveLongDouble(rdb, 0.333333333333333333L); -} - -/* RDB Load Callback - Deserialize metadata from RDB - * This callback is invoked during RDB load to read the metadata value. - * - * Parameters: - * - rdb: RedisModuleIO context for reading from RDB - * - meta: Pointer to store the loaded 8-byte metadata value - * - encver: Encoding version (class version from RDB) - * - * Returns: - * - 1: Attach metadata to key (success) - * - 0: Ignore/skip metadata (not an error) - * - -1: Error - abort RDB load - */ -static int KeyMetaRDBLoadCallback(RedisModuleIO *rdb, uint64_t *meta, int encver) { - REDISMODULE_NOT_USED(encver); - - /* Load the string from RDB using LoadStringBuffer */ - size_t len; - char *loaded_string = RedisModule_LoadStringBuffer(rdb, &len); - - if (loaded_string == NULL) { - /* Error loading string */ - return -1; - } - - /* Allocate and copy the string (LoadStringBuffer returns a buffer that must be freed) */ - char *metadata_string = malloc(len + 1); - if (metadata_string == NULL) { - RedisModule_Free(loaded_string); - return -1; - } - - memcpy(metadata_string, loaded_string, len); - metadata_string[len] = '\0'; - RedisModule_Free(loaded_string); - - /* Load the additional data that was saved (must match rdb_save) */ - int64_t signed_val = RedisModule_LoadSigned(rdb); - float float_val = RedisModule_LoadFloat(rdb); - long double ldouble_val = RedisModule_LoadLongDouble(rdb); - /* We don't use these values, just need to consume them from the stream */ - (void)signed_val; - (void)float_val; - (void)ldouble_val; - - /* Store the pointer in metadata */ - *meta = (uint64_t)metadata_string; - active_metadata_count++; /* New metadata instance created */ - - /* Return 1 to attach metadata to the key */ - return 1; -} - -/* AOF Rewrite Callback - Common implementation for all classes - * This callback is invoked during AOF rewrite to emit commands that will - * recreate the metadata when the AOF is loaded. - * - * Parameters: - * - aof: RedisModuleIO context for writing to AOF - * - value: The kvobj (key-value object) - not used in this implementation - * - meta: The 8-byte metadata value (pointer to our string) - * - class_id: The class ID for this metadata - */ -static void KeyMetaAOFRewriteCallback_Class(RedisModuleIO *aof, void *value, uint64_t meta, RedisModuleKeyMetaClassId class_id) { - REDISMODULE_NOT_USED(value); - - /* If metadata is NULL (reset_value), don't emit anything */ - if (meta == 0) return; - - /* Extract the string from the metadata pointer */ - char *metadata_string = (char *)meta; - - /* Lookup the 9-byte-id name for this class */ - const char *class_name = lookupClassName(class_id); - if (!class_name) { - /* This shouldn't happen, but handle gracefully */ - return; - } - - /* Get the key name from the AOF IO context */ - const RedisModuleString *key = RedisModule_GetKeyNameFromIO(aof); - if (!key) { - /* Key name not available - shouldn't happen during AOF rewrite */ - return; - } - - /* Emit the KEYMETA.SET command to recreate this metadata - * Format: KEYMETA.SET <9-byte-id> <key> <string-value> */ - RedisModule_EmitAOF(aof, "KEYMETA.SET", "csc", - class_name, /* c: 9-byte-id (C string) */ - key, /* s: key name (RedisModuleString) */ - metadata_string); /* c: metadata value (C string) */ -} - -/* Individual AOF rewrite callbacks for each class (1-7) - * Each callback wraps the common implementation with its specific class ID */ -static void KeyMetaAOFRewriteCb1(RedisModuleIO *aof, void *value, uint64_t meta) { - KeyMetaAOFRewriteCallback_Class(aof, value, meta, 1); -} - -static void KeyMetaAOFRewriteCb2(RedisModuleIO *aof, void *value, uint64_t meta) { - KeyMetaAOFRewriteCallback_Class(aof, value, meta, 2); -} - -static void KeyMetaAOFRewriteCb3(RedisModuleIO *aof, void *value, uint64_t meta) { - KeyMetaAOFRewriteCallback_Class(aof, value, meta, 3); -} - -static void KeyMetaAOFRewriteCb4(RedisModuleIO *aof, void *value, uint64_t meta) { - KeyMetaAOFRewriteCallback_Class(aof, value, meta, 4); -} - -static void KeyMetaAOFRewriteCb5(RedisModuleIO *aof, void *value, uint64_t meta) { - KeyMetaAOFRewriteCallback_Class(aof, value, meta, 5); -} - -static void KeyMetaAOFRewriteCb6(RedisModuleIO *aof, void *value, uint64_t meta) { - KeyMetaAOFRewriteCallback_Class(aof, value, meta, 6); -} - -static void KeyMetaAOFRewriteCb7(RedisModuleIO *aof, void *value, uint64_t meta) { - KeyMetaAOFRewriteCallback_Class(aof, value, meta, 7); -} - -/* KEYMETA.REGISTER <4-char-id> <version> [KEEPONCOPY:KEEPONRENAME:UNLINKFREE:ALLOWIGNORE:NORDBLOAD:NORDBSAVE] */ -static int KeyMetaRegister_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc < 3 || argc > 4) { - return RedisModule_WrongArity(ctx); - } - - /* argv[1]: key metadata class name */ - size_t namelen; - const char *metaname = RedisModule_StringPtrLen(argv[1], &namelen); - - /* argv[2]: key metadata class version */ - long long metaver; - if (RedisModule_StringToLongLong(argv[2], &metaver) != REDISMODULE_OK) { - RedisModule_ReplyWithError(ctx, "ERR invalid version number"); - return REDISMODULE_OK; - } - - /* Parse optional callback flags */ - int keep_on_copy = 0, keep_on_rename = 0, unlink_free = 0, keep_on_move = 0; - int allow_ignore = 0; /* Default: ALLOW_IGNORE disabled */ - int rdb_load = 0; /* Default: rdb_load disabled */ - int rdb_save = 0; /* Default: rdb_save disabled */ - - if (argc == 4) { - const char *flags = RedisModule_StringPtrLen(argv[3], NULL); - if (strstr(flags, "KEEPONCOPY")) keep_on_copy = 1; - if (strstr(flags, "KEEPONRENAME")) keep_on_rename = 1; - if (strstr(flags, "UNLINKFREE")) unlink_free = 1; - if (strstr(flags, "KEEPONMOVE")) keep_on_move = 1; - if (strstr(flags, "ALLOWIGNORE")) allow_ignore = 1; /* Enable ALLOW_IGNORE */ - if (strstr(flags, "RDBLOAD")) rdb_load = 1; /* Enable rdb_load */ - if (strstr(flags, "RDBSAVE")) rdb_save = 1; /* Enable rdb_save */ - } - - /* Setup configuration */ - RedisModuleKeyMetaClassConfig config = {0}; - config.version = REDISMODULE_KEY_META_VERSION; - config.flags = allow_ignore ? (1 << REDISMODULE_META_ALLOW_IGNORE) : 0; - config.reset_value = (uint64_t)NULL; /* NULL pointer means no resource to free */ - config.rdb_load = rdb_load ? KeyMetaRDBLoadCallback : NULL; - config.rdb_save = rdb_save ? KeyMetaRDBSaveCallback : NULL; - switch (num_class_mappings + 1) { /* distinct cb per class */ - case 1: config.aof_rewrite = KeyMetaAOFRewriteCb1; break; - case 2: config.aof_rewrite = KeyMetaAOFRewriteCb2; break; - case 3: config.aof_rewrite = KeyMetaAOFRewriteCb3; break; - case 4: config.aof_rewrite = KeyMetaAOFRewriteCb4; break; - case 5: config.aof_rewrite = KeyMetaAOFRewriteCb5; break; - case 6: config.aof_rewrite = KeyMetaAOFRewriteCb6; break; - case 7: config.aof_rewrite = KeyMetaAOFRewriteCb7; break; - default: config.aof_rewrite = NULL; break; - } - config.free = KeyMetaFreeCallback; - config.copy = keep_on_copy ? KeyMetaCopyCallback : NULL; - config.rename = keep_on_rename ? NULL : KeyMetaRenameDiscardCallback; - config.move = keep_on_move ? NULL : KeyMetaMoveDiscardCallback; - config.defrag = NULL; - config.unlink = unlink_free ? KeyMetaUnlinkCallback : NULL; - config.mem_usage = NULL; - config.free_effort = NULL; - - /* Create the metadata class */ - RedisModuleKeyMetaClassId class_id = RedisModule_CreateKeyMetaClass(ctx, metaname, (int)metaver, &config); - - if (class_id < 0) { - RedisModule_ReplyWithError(ctx, "ERR failed to create metadata class"); - return REDISMODULE_OK; - } else { - /* Store the mapping from 9-byte-id to class-id */ - if (!addClassMapping(metaname, class_id)) { - RedisModule_ReplyWithError(ctx, "ERR failed to store class mapping"); - return REDISMODULE_OK; - } - RedisModule_ReplyWithLongLong(ctx, class_id); - } - - return REDISMODULE_OK; -} - -/* KEYMETA.SET <9-byte-id> <key> <string-value> */ -static int KeyMetaSet_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 4) { - return RedisModule_WrongArity(ctx); - } - - /* Parse arguments */ - const char *metaname = RedisModule_StringPtrLen(argv[1], NULL); - RedisModuleString *keyname = argv[2]; - const char *value = RedisModule_StringPtrLen(argv[3], NULL); - - /* Lookup the metadata class by name */ - RedisModuleKeyMetaClassId class_id = lookupClassId(metaname); - if (class_id < 0) { - RedisModule_ReplyWithError(ctx, "ERR metadata class not found"); - return REDISMODULE_OK; - } - - /* Open the key for writing */ - RedisModuleKey *key = RedisModule_OpenKey(ctx, keyname, REDISMODULE_READ | REDISMODULE_WRITE); - - if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_EMPTY) { - RedisModule_ReplyWithNull(ctx); - RedisModule_CloseKey(key); - return REDISMODULE_OK; - } - - /* Check if metadata already exists and free it first. - * - * Note: The caller is responsible for retrieving and freeing any existing - * pointer-based metadata before RM_SetKeyMeta() to a new value - */ - uint64_t meta = 0; - if (RedisModule_GetKeyMeta(class_id, key, &meta) == REDISMODULE_OK) { - if (meta != 0) { - free((char *)meta); - active_metadata_count--; /* Old metadata freed */ - } - } - - char *new_str = strdup(value); - int res = RedisModule_SetKeyMeta(class_id, key, (uint64_t)new_str); - - if (res == REDISMODULE_OK) { - active_metadata_count++; /* New metadata instance created */ - } - - RedisModule_CloseKey(key); - - if (res == REDISMODULE_OK) { - RedisModule_ReplyWithSimpleString(ctx, "OK"); - } else { - free(new_str); - RedisModule_ReplyWithError(ctx, "ERR failed to set metadata"); - } - return REDISMODULE_OK; -} - -/* KEYMETA.GET <9-byte-id> <key> */ -static int KeyMetaGet_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 3) { - return RedisModule_WrongArity(ctx); - } - - /* Parse arguments */ - const char *metaname = RedisModule_StringPtrLen(argv[1], NULL); - RedisModuleString *keyname = argv[2]; - - /* Lookup the metadata class by name */ - RedisModuleKeyMetaClassId class_id = lookupClassId(metaname); - if (class_id < 0) { - RedisModule_ReplyWithError(ctx, "ERR metadata class not found"); - return REDISMODULE_OK; - } - - /* Open the key for reading */ - RedisModuleKey *key = RedisModule_OpenKey(ctx, keyname, REDISMODULE_READ); - if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_EMPTY) { - RedisModule_ReplyWithNull(ctx); - RedisModule_CloseKey(key); - return REDISMODULE_OK; - } - - /* Get the metadata */ - uint64_t meta = 0; - int result = RedisModule_GetKeyMeta(class_id, key, &meta); - - RedisModule_CloseKey(key); - - if (result == REDISMODULE_OK && meta != 0) { - char *str = (char *)meta; - RedisModule_ReplyWithCString(ctx, str); - } else { - RedisModule_ReplyWithNull(ctx); - } - - return REDISMODULE_OK; -} - -/* KEYMETA.UNREGISTER <9-byte-id> */ -static int KeyMetaUnregister_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) { - return RedisModule_WrongArity(ctx); - } - - /* Parse arguments */ - const char *metaname = RedisModule_StringPtrLen(argv[1], NULL); - - /* Lookup the metadata class by name */ - RedisModuleKeyMetaClassId class_id = lookupClassId(metaname); - if (class_id < 0) { - RedisModule_ReplyWithError(ctx, "ERR metadata class not found"); - return REDISMODULE_OK; - } - - /* Release the metadata class */ - int result = RedisModule_ReleaseKeyMetaClass(class_id); - - if (result == REDISMODULE_OK) { - /* Remove the mapping */ - removeClassMapping(metaname); - RedisModule_ReplyWithSimpleString(ctx, "OK"); - } else { - RedisModule_ReplyWithError(ctx, "ERR failed to unregister class"); - } - return REDISMODULE_OK; -} - -/* KEYMETA.ACTIVE - * Returns the total number of active metadata instances that haven't been freed yet. - * This is useful for testing to verify that metadata is properly cleaned up. */ -static int KeyMetaActive_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 1) { - return RedisModule_WrongArity(ctx); - } - REDISMODULE_NOT_USED(argv); - - RedisModule_ReplyWithLongLong(ctx, active_metadata_count); - return REDISMODULE_OK; -} - -/* Module initialization */ -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - if (RedisModule_Init(ctx, "test_metakey", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR) { - return REDISMODULE_ERR; - } - - /* Register commands */ - if (RedisModule_CreateCommand(ctx, "keymeta.register", - KeyMetaRegister_RedisCommand, "write", 0, 0, 0) == REDISMODULE_ERR) { - return REDISMODULE_ERR; - } - - if (RedisModule_CreateCommand(ctx, "keymeta.set", - KeyMetaSet_RedisCommand, "write deny-oom", 1, 1, 1) == REDISMODULE_ERR) { - return REDISMODULE_ERR; - } - - if (RedisModule_CreateCommand(ctx, "keymeta.get", - KeyMetaGet_RedisCommand, "readonly", 1, 1, 1) == REDISMODULE_ERR) { - return REDISMODULE_ERR; - } - - if (RedisModule_CreateCommand(ctx, "keymeta.unregister", - KeyMetaUnregister_RedisCommand, "write", 0, 0, 0) == REDISMODULE_ERR) { - return REDISMODULE_ERR; - } - - if (RedisModule_CreateCommand(ctx, "keymeta.active", - KeyMetaActive_RedisCommand, "readonly fast", 0, 0, 0) == REDISMODULE_ERR) { - return REDISMODULE_ERR; - } - - return REDISMODULE_OK; -} - -int RedisModule_OnUnload(RedisModuleCtx *ctx) { - REDISMODULE_NOT_USED(ctx); - long unsigned int i; - for (i = 0 ; i < sizeof(class_ids) / sizeof(class_ids[0]); i++) { - if (class_ids[i] > 0) - RedisModule_ReleaseKeyMetaClass(class_ids[i]); - } - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/test_lazyfree.c b/examples/redis-unstable/tests/modules/test_lazyfree.c deleted file mode 100644 index 7ba213f..0000000 --- a/examples/redis-unstable/tests/modules/test_lazyfree.c +++ /dev/null @@ -1,196 +0,0 @@ -/* This module emulates a linked list for lazyfree testing of modules, which - is a simplified version of 'hellotype.c' - */ -#include "redismodule.h" -#include <stdio.h> -#include <stdlib.h> -#include <ctype.h> -#include <string.h> -#include <stdint.h> - -static RedisModuleType *LazyFreeLinkType; - -struct LazyFreeLinkNode { - int64_t value; - struct LazyFreeLinkNode *next; -}; - -struct LazyFreeLinkObject { - struct LazyFreeLinkNode *head; - size_t len; /* Number of elements added. */ -}; - -struct LazyFreeLinkObject *createLazyFreeLinkObject(void) { - struct LazyFreeLinkObject *o; - o = RedisModule_Alloc(sizeof(*o)); - o->head = NULL; - o->len = 0; - return o; -} - -void LazyFreeLinkInsert(struct LazyFreeLinkObject *o, int64_t ele) { - struct LazyFreeLinkNode *next = o->head, *newnode, *prev = NULL; - - while(next && next->value < ele) { - prev = next; - next = next->next; - } - newnode = RedisModule_Alloc(sizeof(*newnode)); - newnode->value = ele; - newnode->next = next; - if (prev) { - prev->next = newnode; - } else { - o->head = newnode; - } - o->len++; -} - -void LazyFreeLinkReleaseObject(struct LazyFreeLinkObject *o) { - struct LazyFreeLinkNode *cur, *next; - cur = o->head; - while(cur) { - next = cur->next; - RedisModule_Free(cur); - cur = next; - } - RedisModule_Free(o); -} - -/* LAZYFREELINK.INSERT key value */ -int LazyFreeLinkInsert_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - RedisModule_AutoMemory(ctx); /* Use automatic memory management. */ - - if (argc != 3) return RedisModule_WrongArity(ctx); - RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], - REDISMODULE_READ|REDISMODULE_WRITE); - int type = RedisModule_KeyType(key); - if (type != REDISMODULE_KEYTYPE_EMPTY && - RedisModule_ModuleTypeGetType(key) != LazyFreeLinkType) - { - return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); - } - - long long value; - if ((RedisModule_StringToLongLong(argv[2],&value) != REDISMODULE_OK)) { - return RedisModule_ReplyWithError(ctx,"ERR invalid value: must be a signed 64 bit integer"); - } - - struct LazyFreeLinkObject *hto; - if (type == REDISMODULE_KEYTYPE_EMPTY) { - hto = createLazyFreeLinkObject(); - RedisModule_ModuleTypeSetValue(key,LazyFreeLinkType,hto); - } else { - hto = RedisModule_ModuleTypeGetValue(key); - } - - LazyFreeLinkInsert(hto,value); - RedisModule_SignalKeyAsReady(ctx,argv[1]); - - RedisModule_ReplyWithLongLong(ctx,hto->len); - RedisModule_ReplicateVerbatim(ctx); - return REDISMODULE_OK; -} - -/* LAZYFREELINK.LEN key */ -int LazyFreeLinkLen_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - RedisModule_AutoMemory(ctx); /* Use automatic memory management. */ - - if (argc != 2) return RedisModule_WrongArity(ctx); - RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], - REDISMODULE_READ); - int type = RedisModule_KeyType(key); - if (type != REDISMODULE_KEYTYPE_EMPTY && - RedisModule_ModuleTypeGetType(key) != LazyFreeLinkType) - { - return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE); - } - - struct LazyFreeLinkObject *hto = RedisModule_ModuleTypeGetValue(key); - RedisModule_ReplyWithLongLong(ctx,hto ? hto->len : 0); - return REDISMODULE_OK; -} - -void *LazyFreeLinkRdbLoad(RedisModuleIO *rdb, int encver) { - if (encver != 0) { - return NULL; - } - uint64_t elements = RedisModule_LoadUnsigned(rdb); - struct LazyFreeLinkObject *hto = createLazyFreeLinkObject(); - while(elements--) { - int64_t ele = RedisModule_LoadSigned(rdb); - LazyFreeLinkInsert(hto,ele); - } - return hto; -} - -void LazyFreeLinkRdbSave(RedisModuleIO *rdb, void *value) { - struct LazyFreeLinkObject *hto = value; - struct LazyFreeLinkNode *node = hto->head; - RedisModule_SaveUnsigned(rdb,hto->len); - while(node) { - RedisModule_SaveSigned(rdb,node->value); - node = node->next; - } -} - -void LazyFreeLinkAofRewrite(RedisModuleIO *aof, RedisModuleString *key, void *value) { - struct LazyFreeLinkObject *hto = value; - struct LazyFreeLinkNode *node = hto->head; - while(node) { - RedisModule_EmitAOF(aof,"LAZYFREELINK.INSERT","sl",key,node->value); - node = node->next; - } -} - -void LazyFreeLinkFree(void *value) { - LazyFreeLinkReleaseObject(value); -} - -size_t LazyFreeLinkFreeEffort(RedisModuleString *key, const void *value) { - REDISMODULE_NOT_USED(key); - const struct LazyFreeLinkObject *hto = value; - return hto->len; -} - -void LazyFreeLinkUnlink(RedisModuleString *key, const void *value) { - REDISMODULE_NOT_USED(key); - REDISMODULE_NOT_USED(value); - /* Here you can know which key and value is about to be freed. */ -} - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - if (RedisModule_Init(ctx,"lazyfreetest",1,REDISMODULE_APIVER_1) - == REDISMODULE_ERR) return REDISMODULE_ERR; - - /* We only allow our module to be loaded when the redis core version is greater than the version of my module */ - if (RedisModule_GetTypeMethodVersion() < REDISMODULE_TYPE_METHOD_VERSION) { - return REDISMODULE_ERR; - } - - RedisModuleTypeMethods tm = { - .version = REDISMODULE_TYPE_METHOD_VERSION, - .rdb_load = LazyFreeLinkRdbLoad, - .rdb_save = LazyFreeLinkRdbSave, - .aof_rewrite = LazyFreeLinkAofRewrite, - .free = LazyFreeLinkFree, - .free_effort = LazyFreeLinkFreeEffort, - .unlink = LazyFreeLinkUnlink, - }; - - LazyFreeLinkType = RedisModule_CreateDataType(ctx,"test_lazy",0,&tm); - if (LazyFreeLinkType == NULL) return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"lazyfreelink.insert", - LazyFreeLinkInsert_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"lazyfreelink.len", - LazyFreeLinkLen_RedisCommand,"readonly",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/testrdb.c b/examples/redis-unstable/tests/modules/testrdb.c deleted file mode 100644 index c31aebb..0000000 --- a/examples/redis-unstable/tests/modules/testrdb.c +++ /dev/null @@ -1,405 +0,0 @@ -#include "redismodule.h" - -#include <string.h> -#include <assert.h> - -/* Module configuration, save aux or not? */ -#define CONF_AUX_OPTION_NO_AUX 0 -#define CONF_AUX_OPTION_SAVE2 1 << 0 -#define CONF_AUX_OPTION_BEFORE_KEYSPACE 1 << 1 -#define CONF_AUX_OPTION_AFTER_KEYSPACE 1 << 2 -#define CONF_AUX_OPTION_NO_DATA 1 << 3 -long long conf_aux_count = 0; - -/* Registered type */ -RedisModuleType *testrdb_type = NULL; - -/* Global values to store and persist to aux */ -RedisModuleString *before_str = NULL; -RedisModuleString *after_str = NULL; - -/* Global values used to keep aux from db being loaded (in case of async_loading) */ -RedisModuleString *before_str_temp = NULL; -RedisModuleString *after_str_temp = NULL; - -/* Indicates whether there is an async replication in progress. - * We control this value from RedisModuleEvent_ReplAsyncLoad events. */ -int async_loading = 0; - -int n_aux_load_called = 0; - -void replAsyncLoadCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data) -{ - REDISMODULE_NOT_USED(e); - REDISMODULE_NOT_USED(data); - - switch (sub) { - case REDISMODULE_SUBEVENT_REPL_ASYNC_LOAD_STARTED: - assert(async_loading == 0); - async_loading = 1; - break; - case REDISMODULE_SUBEVENT_REPL_ASYNC_LOAD_ABORTED: - /* Discard temp aux */ - if (before_str_temp) - RedisModule_FreeString(ctx, before_str_temp); - if (after_str_temp) - RedisModule_FreeString(ctx, after_str_temp); - before_str_temp = NULL; - after_str_temp = NULL; - - async_loading = 0; - break; - case REDISMODULE_SUBEVENT_REPL_ASYNC_LOAD_COMPLETED: - if (before_str) - RedisModule_FreeString(ctx, before_str); - if (after_str) - RedisModule_FreeString(ctx, after_str); - before_str = before_str_temp; - after_str = after_str_temp; - - before_str_temp = NULL; - after_str_temp = NULL; - - async_loading = 0; - break; - default: - assert(0); - } -} - -void *testrdb_type_load(RedisModuleIO *rdb, int encver) { - int count = RedisModule_LoadSigned(rdb); - RedisModuleString *str = RedisModule_LoadString(rdb); - float f = RedisModule_LoadFloat(rdb); - long double ld = RedisModule_LoadLongDouble(rdb); - if (RedisModule_IsIOError(rdb)) { - RedisModuleCtx *ctx = RedisModule_GetContextFromIO(rdb); - if (str) - RedisModule_FreeString(ctx, str); - return NULL; - } - /* Using the values only after checking for io errors. */ - assert(count==1); - assert(encver==1); - assert(f==1.5f); - assert(ld==0.333333333333333333L); - return str; -} - -void testrdb_type_save(RedisModuleIO *rdb, void *value) { - RedisModuleString *str = (RedisModuleString*)value; - RedisModule_SaveSigned(rdb, 1); - RedisModule_SaveString(rdb, str); - RedisModule_SaveFloat(rdb, 1.5); - RedisModule_SaveLongDouble(rdb, 0.333333333333333333L); -} - -void testrdb_aux_save(RedisModuleIO *rdb, int when) { - if (!(conf_aux_count & CONF_AUX_OPTION_BEFORE_KEYSPACE)) assert(when == REDISMODULE_AUX_AFTER_RDB); - if (!(conf_aux_count & CONF_AUX_OPTION_AFTER_KEYSPACE)) assert(when == REDISMODULE_AUX_BEFORE_RDB); - assert(conf_aux_count!=CONF_AUX_OPTION_NO_AUX); - if (when == REDISMODULE_AUX_BEFORE_RDB) { - if (before_str) { - RedisModule_SaveSigned(rdb, 1); - RedisModule_SaveString(rdb, before_str); - } else { - RedisModule_SaveSigned(rdb, 0); - } - } else { - if (after_str) { - RedisModule_SaveSigned(rdb, 1); - RedisModule_SaveString(rdb, after_str); - } else { - RedisModule_SaveSigned(rdb, 0); - } - } -} - -int testrdb_aux_load(RedisModuleIO *rdb, int encver, int when) { - assert(encver == 1); - if (!(conf_aux_count & CONF_AUX_OPTION_BEFORE_KEYSPACE)) assert(when == REDISMODULE_AUX_AFTER_RDB); - if (!(conf_aux_count & CONF_AUX_OPTION_AFTER_KEYSPACE)) assert(when == REDISMODULE_AUX_BEFORE_RDB); - assert(conf_aux_count!=CONF_AUX_OPTION_NO_AUX); - RedisModuleCtx *ctx = RedisModule_GetContextFromIO(rdb); - if (when == REDISMODULE_AUX_BEFORE_RDB) { - if (async_loading == 0) { - if (before_str) - RedisModule_FreeString(ctx, before_str); - before_str = NULL; - int count = RedisModule_LoadSigned(rdb); - if (RedisModule_IsIOError(rdb)) - return REDISMODULE_ERR; - if (count) - before_str = RedisModule_LoadString(rdb); - } else { - if (before_str_temp) - RedisModule_FreeString(ctx, before_str_temp); - before_str_temp = NULL; - int count = RedisModule_LoadSigned(rdb); - if (RedisModule_IsIOError(rdb)) - return REDISMODULE_ERR; - if (count) - before_str_temp = RedisModule_LoadString(rdb); - } - } else { - if (async_loading == 0) { - if (after_str) - RedisModule_FreeString(ctx, after_str); - after_str = NULL; - int count = RedisModule_LoadSigned(rdb); - if (RedisModule_IsIOError(rdb)) - return REDISMODULE_ERR; - if (count) - after_str = RedisModule_LoadString(rdb); - } else { - if (after_str_temp) - RedisModule_FreeString(ctx, after_str_temp); - after_str_temp = NULL; - int count = RedisModule_LoadSigned(rdb); - if (RedisModule_IsIOError(rdb)) - return REDISMODULE_ERR; - if (count) - after_str_temp = RedisModule_LoadString(rdb); - } - } - - if (RedisModule_IsIOError(rdb)) - return REDISMODULE_ERR; - return REDISMODULE_OK; -} - -void testrdb_type_free(void *value) { - if (value) - RedisModule_FreeString(NULL, (RedisModuleString*)value); -} - -int testrdb_set_before(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - if (argc != 2) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - if (before_str) - RedisModule_FreeString(ctx, before_str); - before_str = argv[1]; - RedisModule_RetainString(ctx, argv[1]); - RedisModule_ReplyWithLongLong(ctx, 1); - return REDISMODULE_OK; -} - -int testrdb_get_before(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - REDISMODULE_NOT_USED(argv); - if (argc != 1){ - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - if (before_str) - RedisModule_ReplyWithString(ctx, before_str); - else - RedisModule_ReplyWithStringBuffer(ctx, "", 0); - return REDISMODULE_OK; -} - -/* For purpose of testing module events, expose variable state during async_loading. */ -int testrdb_async_loading_get_before(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - REDISMODULE_NOT_USED(argv); - if (argc != 1){ - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - if (before_str_temp) - RedisModule_ReplyWithString(ctx, before_str_temp); - else - RedisModule_ReplyWithStringBuffer(ctx, "", 0); - return REDISMODULE_OK; -} - -int testrdb_set_after(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - if (argc != 2){ - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - if (after_str) - RedisModule_FreeString(ctx, after_str); - after_str = argv[1]; - RedisModule_RetainString(ctx, argv[1]); - RedisModule_ReplyWithLongLong(ctx, 1); - return REDISMODULE_OK; -} - -int testrdb_get_after(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - REDISMODULE_NOT_USED(argv); - if (argc != 1){ - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - if (after_str) - RedisModule_ReplyWithString(ctx, after_str); - else - RedisModule_ReplyWithStringBuffer(ctx, "", 0); - return REDISMODULE_OK; -} - -int testrdb_set_key(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - if (argc != 3){ - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE); - RedisModuleString *str = RedisModule_ModuleTypeGetValue(key); - if (str) - RedisModule_FreeString(ctx, str); - RedisModule_ModuleTypeSetValue(key, testrdb_type, argv[2]); - RedisModule_RetainString(ctx, argv[2]); - RedisModule_CloseKey(key); - RedisModule_ReplyWithLongLong(ctx, 1); - return REDISMODULE_OK; -} - -int testrdb_get_key(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - if (argc != 2){ - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ); - RedisModuleString *str = RedisModule_ModuleTypeGetValue(key); - RedisModule_CloseKey(key); - RedisModule_ReplyWithString(ctx, str); - return REDISMODULE_OK; -} - -int testrdb_get_n_aux_load_called(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - REDISMODULE_NOT_USED(ctx); - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - RedisModule_ReplyWithLongLong(ctx, n_aux_load_called); - return REDISMODULE_OK; -} - -int test2rdb_aux_load(RedisModuleIO *rdb, int encver, int when) { - REDISMODULE_NOT_USED(rdb); - REDISMODULE_NOT_USED(encver); - REDISMODULE_NOT_USED(when); - n_aux_load_called++; - return REDISMODULE_OK; -} - -void test2rdb_aux_save(RedisModuleIO *rdb, int when) { - REDISMODULE_NOT_USED(rdb); - REDISMODULE_NOT_USED(when); -} - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - if (RedisModule_Init(ctx,"testrdb",1,REDISMODULE_APIVER_1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - RedisModule_SetModuleOptions(ctx, REDISMODULE_OPTIONS_HANDLE_IO_ERRORS | REDISMODULE_OPTIONS_HANDLE_REPL_ASYNC_LOAD); - - if (argc > 0) - RedisModule_StringToLongLong(argv[0], &conf_aux_count); - - if (conf_aux_count==CONF_AUX_OPTION_NO_AUX) { - RedisModuleTypeMethods datatype_methods = { - .version = 1, - .rdb_load = testrdb_type_load, - .rdb_save = testrdb_type_save, - .aof_rewrite = NULL, - .digest = NULL, - .free = testrdb_type_free, - }; - - testrdb_type = RedisModule_CreateDataType(ctx, "test__rdb", 1, &datatype_methods); - if (testrdb_type == NULL) - return REDISMODULE_ERR; - } else if (!(conf_aux_count & CONF_AUX_OPTION_NO_DATA)) { - RedisModuleTypeMethods datatype_methods = { - .version = REDISMODULE_TYPE_METHOD_VERSION, - .rdb_load = testrdb_type_load, - .rdb_save = testrdb_type_save, - .aof_rewrite = NULL, - .digest = NULL, - .free = testrdb_type_free, - .aux_load = testrdb_aux_load, - .aux_save = testrdb_aux_save, - .aux_save_triggers = ((conf_aux_count & CONF_AUX_OPTION_BEFORE_KEYSPACE) ? REDISMODULE_AUX_BEFORE_RDB : 0) | - ((conf_aux_count & CONF_AUX_OPTION_AFTER_KEYSPACE) ? REDISMODULE_AUX_AFTER_RDB : 0) - }; - - if (conf_aux_count & CONF_AUX_OPTION_SAVE2) { - datatype_methods.aux_save2 = testrdb_aux_save; - } - - testrdb_type = RedisModule_CreateDataType(ctx, "test__rdb", 1, &datatype_methods); - if (testrdb_type == NULL) - return REDISMODULE_ERR; - } else { - - /* Used to verify that aux_save2 api without any data, saves nothing to the RDB. */ - RedisModuleTypeMethods datatype_methods = { - .version = REDISMODULE_TYPE_METHOD_VERSION, - .aux_load = test2rdb_aux_load, - .aux_save = test2rdb_aux_save, - .aux_save_triggers = ((conf_aux_count & CONF_AUX_OPTION_BEFORE_KEYSPACE) ? REDISMODULE_AUX_BEFORE_RDB : 0) | - ((conf_aux_count & CONF_AUX_OPTION_AFTER_KEYSPACE) ? REDISMODULE_AUX_AFTER_RDB : 0) - }; - if (conf_aux_count & CONF_AUX_OPTION_SAVE2) { - datatype_methods.aux_save2 = test2rdb_aux_save; - } - - RedisModule_CreateDataType(ctx, "test__rdb", 1, &datatype_methods); - } - - if (RedisModule_CreateCommand(ctx,"testrdb.set.before", testrdb_set_before,"deny-oom",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"testrdb.get.before", testrdb_get_before,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"testrdb.async_loading.get.before", testrdb_async_loading_get_before,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"testrdb.set.after", testrdb_set_after,"deny-oom",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"testrdb.get.after", testrdb_get_after,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"testrdb.set.key", testrdb_set_key,"deny-oom",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"testrdb.get.key", testrdb_get_key,"",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"testrdb.get.n_aux_load_called", testrdb_get_n_aux_load_called,"",1,1,1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - RedisModule_SubscribeToServerEvent(ctx, - RedisModuleEvent_ReplAsyncLoad, replAsyncLoadCallback); - - return REDISMODULE_OK; -} - -int RedisModule_OnUnload(RedisModuleCtx *ctx) { - if (before_str) - RedisModule_FreeString(ctx, before_str); - if (after_str) - RedisModule_FreeString(ctx, after_str); - if (before_str_temp) - RedisModule_FreeString(ctx, before_str_temp); - if (after_str_temp) - RedisModule_FreeString(ctx, after_str_temp); - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/timer.c b/examples/redis-unstable/tests/modules/timer.c deleted file mode 100644 index c9bd636..0000000 --- a/examples/redis-unstable/tests/modules/timer.c +++ /dev/null @@ -1,102 +0,0 @@ - -#include "redismodule.h" - -static void timer_callback(RedisModuleCtx *ctx, void *data) -{ - RedisModuleString *keyname = data; - RedisModuleCallReply *reply; - - reply = RedisModule_Call(ctx, "INCR", "s", keyname); - if (reply != NULL) - RedisModule_FreeCallReply(reply); - RedisModule_FreeString(ctx, keyname); -} - -int test_createtimer(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - if (argc != 3) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - long long period; - if (RedisModule_StringToLongLong(argv[1], &period) == REDISMODULE_ERR) { - RedisModule_ReplyWithError(ctx, "Invalid time specified."); - return REDISMODULE_OK; - } - - RedisModuleString *keyname = argv[2]; - RedisModule_RetainString(ctx, keyname); - - RedisModuleTimerID id = RedisModule_CreateTimer(ctx, period, timer_callback, keyname); - RedisModule_ReplyWithLongLong(ctx, id); - - return REDISMODULE_OK; -} - -int test_gettimer(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - if (argc != 2) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - long long id; - if (RedisModule_StringToLongLong(argv[1], &id) == REDISMODULE_ERR) { - RedisModule_ReplyWithError(ctx, "Invalid id specified."); - return REDISMODULE_OK; - } - - uint64_t remaining; - RedisModuleString *keyname; - if (RedisModule_GetTimerInfo(ctx, id, &remaining, (void **)&keyname) == REDISMODULE_ERR) { - RedisModule_ReplyWithNull(ctx); - } else { - RedisModule_ReplyWithArray(ctx, 2); - RedisModule_ReplyWithString(ctx, keyname); - RedisModule_ReplyWithLongLong(ctx, remaining); - } - - return REDISMODULE_OK; -} - -int test_stoptimer(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - if (argc != 2) { - RedisModule_WrongArity(ctx); - return REDISMODULE_OK; - } - - long long id; - if (RedisModule_StringToLongLong(argv[1], &id) == REDISMODULE_ERR) { - RedisModule_ReplyWithError(ctx, "Invalid id specified."); - return REDISMODULE_OK; - } - - int ret = 0; - RedisModuleString *keyname; - if (RedisModule_StopTimer(ctx, id, (void **) &keyname) == REDISMODULE_OK) { - RedisModule_FreeString(ctx, keyname); - ret = 1; - } - - RedisModule_ReplyWithLongLong(ctx, ret); - return REDISMODULE_OK; -} - - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - if (RedisModule_Init(ctx,"timer",1,REDISMODULE_APIVER_1)== REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"test.createtimer", test_createtimer,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx,"test.gettimer", test_gettimer,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - if (RedisModule_CreateCommand(ctx,"test.stoptimer", test_stoptimer,"",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/usercall.c b/examples/redis-unstable/tests/modules/usercall.c deleted file mode 100644 index cc28817..0000000 --- a/examples/redis-unstable/tests/modules/usercall.c +++ /dev/null @@ -1,230 +0,0 @@ -#include "redismodule.h" -#include <pthread.h> -#include <assert.h> - -#define UNUSED(V) ((void) V) - -RedisModuleUser *user = NULL; - -int call_without_user(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, (size_t)argc - 2); - if (!rep) { - RedisModule_ReplyWithError(ctx, "NULL reply returned"); - } else { - RedisModule_ReplyWithCallReply(ctx, rep); - RedisModule_FreeCallReply(rep); - } - return REDISMODULE_OK; -} - -int call_with_user_flag(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc < 3) { - return RedisModule_WrongArity(ctx); - } - - RedisModule_SetContextUser(ctx, user); - - /* 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, (size_t)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 add_to_acl(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 2) { - return RedisModule_WrongArity(ctx); - } - - size_t acl_len; - const char *acl = RedisModule_StringPtrLen(argv[1], &acl_len); - - RedisModuleString *error; - int ret = RedisModule_SetModuleUserACLString(ctx, user, acl, &error); - if (ret) { - size_t len; - const char * e = RedisModule_StringPtrLen(error, &len); - RedisModule_ReplyWithError(ctx, e); - return REDISMODULE_OK; - } - - RedisModule_ReplyWithSimpleString(ctx, "OK"); - - return REDISMODULE_OK; -} - -int get_acl(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - - if (argc != 1) { - return RedisModule_WrongArity(ctx); - } - - RedisModule_Assert(user != NULL); - - RedisModuleString *acl = RedisModule_GetModuleUserACLString(user); - - RedisModule_ReplyWithString(ctx, acl); - - RedisModule_FreeString(NULL, acl); - - return REDISMODULE_OK; -} - -int reset_user(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - - if (argc != 1) { - return RedisModule_WrongArity(ctx); - } - - if (user != NULL) { - RedisModule_FreeModuleUser(user); - } - - user = RedisModule_CreateModuleUser("module_user"); - - RedisModule_ReplyWithSimpleString(ctx, "OK"); - - return REDISMODULE_OK; -} - -typedef struct { - RedisModuleString **argv; - int argc; - RedisModuleBlockedClient *bc; -} bg_call_data; - -void *bg_call_worker(void *arg) { - bg_call_data *bg = arg; - RedisModuleBlockedClient *bc = bg->bc; - - // Get Redis module context - RedisModuleCtx *ctx = RedisModule_GetThreadSafeContext(bg->bc); - - // Acquire GIL - RedisModule_ThreadSafeContextLock(ctx); - - // Set user - RedisModule_SetContextUser(ctx, user); - - // Call the command - size_t format_len; - RedisModuleString *format_redis_str = RedisModule_CreateString(NULL, "v", 1); - const char *format = RedisModule_StringPtrLen(bg->argv[1], &format_len); - RedisModule_StringAppendBuffer(NULL, format_redis_str, format, format_len); - RedisModule_StringAppendBuffer(NULL, format_redis_str, "E", 1); - format = RedisModule_StringPtrLen(format_redis_str, NULL); - const char *cmd = RedisModule_StringPtrLen(bg->argv[2], NULL); - RedisModuleCallReply *rep = RedisModule_Call(ctx, cmd, format, bg->argv + 3, (size_t)bg->argc - 3); - RedisModule_FreeString(NULL, format_redis_str); - - /* Free the arguments within GIL to prevent simultaneous freeing in main thread. */ - for (int i=0; i<bg->argc; i++) - RedisModule_FreeString(ctx, bg->argv[i]); - RedisModule_Free(bg->argv); - RedisModule_Free(bg); - - // Release GIL - RedisModule_ThreadSafeContextUnlock(ctx); - - // Reply to client - if (!rep) { - RedisModule_ReplyWithError(ctx, "NULL reply returned"); - } else { - RedisModule_ReplyWithCallReply(ctx, rep); - RedisModule_FreeCallReply(rep); - } - - // Unblock client - RedisModule_UnblockClient(bc, NULL); - - // Free the Redis module context - RedisModule_FreeThreadSafeContext(ctx); - - return NULL; -} - -int call_with_user_bg(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) -{ - UNUSED(argv); - UNUSED(argc); - - /* Make sure we're not trying to block a client when we shouldn't */ - int flags = RedisModule_GetContextFlags(ctx); - int allFlags = RedisModule_GetContextFlagsAll(); - if ((allFlags & REDISMODULE_CTX_FLAGS_MULTI) && - (flags & REDISMODULE_CTX_FLAGS_MULTI)) { - RedisModule_ReplyWithSimpleString(ctx, "Blocked client is not supported inside multi"); - return REDISMODULE_OK; - } - if ((allFlags & REDISMODULE_CTX_FLAGS_DENY_BLOCKING) && - (flags & REDISMODULE_CTX_FLAGS_DENY_BLOCKING)) { - RedisModule_ReplyWithSimpleString(ctx, "Blocked client is not allowed"); - return REDISMODULE_OK; - } - - /* Make a copy of the arguments and pass them to the thread. */ - bg_call_data *bg = RedisModule_Alloc(sizeof(bg_call_data)); - bg->argv = RedisModule_Alloc(sizeof(RedisModuleString*)*argc); - bg->argc = argc; - for (int i=0; i<argc; i++) - bg->argv[i] = RedisModule_HoldString(ctx, argv[i]); - - /* Block the client */ - bg->bc = RedisModule_BlockClient(ctx, NULL, NULL, NULL, 0); - - /* Start a thread to handle the request */ - pthread_t tid; - int res = pthread_create(&tid, NULL, bg_call_worker, bg); - assert(res == 0); - pthread_detach(tid); - - return REDISMODULE_OK; -} - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - - if (RedisModule_Init(ctx,"usercall",1,REDISMODULE_APIVER_1)== REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"usercall.call_without_user", call_without_user,"write",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"usercall.call_with_user_flag", call_with_user_flag,"write",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "usercall.call_with_user_bg", call_with_user_bg, "write", 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "usercall.add_to_acl", add_to_acl, "write",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"usercall.reset_user", reset_user,"write",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx,"usercall.get_acl", get_acl,"write",0,0,0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - return REDISMODULE_OK; -} diff --git a/examples/redis-unstable/tests/modules/zset.c b/examples/redis-unstable/tests/modules/zset.c deleted file mode 100644 index adb318d..0000000 --- a/examples/redis-unstable/tests/modules/zset.c +++ /dev/null @@ -1,180 +0,0 @@ -#include "redismodule.h" -#include <math.h> -#include <errno.h> - -/* ZSET.REM key element - * - * Removes an occurrence of an element from a sorted set. Replies with the - * number of removed elements (0 or 1). - */ -int zset_rem(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 3) return RedisModule_WrongArity(ctx); - RedisModule_AutoMemory(ctx); - int keymode = REDISMODULE_READ | REDISMODULE_WRITE; - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], keymode); - int deleted; - if (RedisModule_ZsetRem(key, argv[2], &deleted) == REDISMODULE_OK) - return RedisModule_ReplyWithLongLong(ctx, deleted); - else - return RedisModule_ReplyWithError(ctx, "ERR ZsetRem failed"); -} - -/* ZSET.ADD key score member - * - * Adds a specified member with the specified score to the sorted - * set stored at key. - */ -int zset_add(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 4) return RedisModule_WrongArity(ctx); - RedisModule_AutoMemory(ctx); - int keymode = REDISMODULE_READ | REDISMODULE_WRITE; - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], keymode); - - size_t len; - double score; - char *endptr; - const char *str = RedisModule_StringPtrLen(argv[2], &len); - score = strtod(str, &endptr); - if (*endptr != '\0' || errno == ERANGE) - return RedisModule_ReplyWithError(ctx, "value is not a valid float"); - - if (RedisModule_ZsetAdd(key, score, argv[3], NULL) == REDISMODULE_OK) - return RedisModule_ReplyWithSimpleString(ctx, "OK"); - else - return RedisModule_ReplyWithError(ctx, "ERR ZsetAdd failed"); -} - -/* ZSET.INCRBY key member increment - * - * Increments the score stored at member in the sorted set stored at key by increment. - * Replies with the new score of this element. - */ -int zset_incrby(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 4) return RedisModule_WrongArity(ctx); - RedisModule_AutoMemory(ctx); - int keymode = REDISMODULE_READ | REDISMODULE_WRITE; - RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], keymode); - - size_t len; - double score, newscore; - char *endptr; - const char *str = RedisModule_StringPtrLen(argv[3], &len); - score = strtod(str, &endptr); - if (*endptr != '\0' || errno == ERANGE) - return RedisModule_ReplyWithError(ctx, "value is not a valid float"); - - if (RedisModule_ZsetIncrby(key, score, argv[2], NULL, &newscore) == REDISMODULE_OK) - return RedisModule_ReplyWithDouble(ctx, newscore); - else - return RedisModule_ReplyWithError(ctx, "ERR ZsetIncrby failed"); -} - -/* Structure to hold data for the delall scan callback */ -typedef struct { - RedisModuleCtx *ctx; - RedisModuleString **keys_to_delete; - size_t keys_capacity; - size_t keys_count; -} zset_delall_data; - -/* Callback function for scanning keys and collecting zset keys to delete */ -void zset_delall_callback(RedisModuleCtx *ctx, RedisModuleString *keyname, RedisModuleKey *key, void *privdata) { - zset_delall_data *data = privdata; - int was_opened = 0; - - /* Open the key if it wasn't already opened */ - if (!key) { - key = RedisModule_OpenKey(ctx, keyname, REDISMODULE_READ); - was_opened = 1; - } - - /* Check if the key is a zset and add it to the list */ - if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_ZSET) { - /* Expand the array if needed */ - if (data->keys_count >= data->keys_capacity) { - data->keys_capacity = data->keys_capacity ? data->keys_capacity * 2 : 16; - data->keys_to_delete = RedisModule_Realloc(data->keys_to_delete, - data->keys_capacity * sizeof(RedisModuleString*)); - } - - /* Store the key name (retain it so it doesn't get freed) */ - data->keys_to_delete[data->keys_count] = keyname; - RedisModule_RetainString(ctx, keyname); - data->keys_count++; - } - - /* Close the key if we opened it */ - if (was_opened) { - RedisModule_CloseKey(key); - } -} - -/* ZSET.DELALL - * - * Iterates through the keyspace and deletes all keys of type "zset". - * Returns the number of deleted keys. - */ -int zset_delall(RedisModuleCtx *ctx, REDISMODULE_ATTR_UNUSED RedisModuleString **argv, int argc) { - if (argc != 1) return RedisModule_WrongArity(ctx); - RedisModule_AutoMemory(ctx); - - zset_delall_data data = { - .ctx = ctx, - .keys_to_delete = NULL, - .keys_capacity = 0, - .keys_count = 0 - }; - - /* Create a scan cursor and iterate through all keys */ - RedisModuleScanCursor *cursor = RedisModule_ScanCursorCreate(); - while (RedisModule_Scan(ctx, cursor, zset_delall_callback, &data)); - RedisModule_ScanCursorDestroy(cursor); - - /* Delete all the collected zset keys after scan is complete */ - size_t deleted_count = 0; - for (size_t i = 0; i < data.keys_count; i++) { - RedisModuleCallReply *reply = RedisModule_Call(ctx, "DEL", "s!", data.keys_to_delete[i]); - if (reply && RedisModule_CallReplyType(reply) == REDISMODULE_REPLY_INTEGER) { - long long del_result = RedisModule_CallReplyInteger(reply); - if (del_result > 0) { - deleted_count++; - } - } - if (reply) { - RedisModule_FreeCallReply(reply); - } - RedisModule_FreeString(ctx, data.keys_to_delete[i]); - } - - /* Free the keys array */ - if (data.keys_to_delete) { - RedisModule_Free(data.keys_to_delete); - } - - return RedisModule_ReplyWithLongLong(ctx, deleted_count); -} - -int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - if (RedisModule_Init(ctx, "zset", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "zset.rem", zset_rem, "write", - 1, 1, 1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "zset.add", zset_add, "write", - 1, 1, 1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "zset.incrby", zset_incrby, "write", - 1, 1, 1) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - if (RedisModule_CreateCommand(ctx, "zset.delall", zset_delall, "write touches-arbitrary-keys", - 0, 0, 0) == REDISMODULE_ERR) - return REDISMODULE_ERR; - - return REDISMODULE_OK; -} |
