From 0f8692b4646013b7d98d4b21f86da0686546d43a Mon Sep 17 00:00:00 2001 From: Oran Agra Date: Mon, 11 Nov 2019 13:30:37 +0200 Subject: Add RM_ScanKey to scan hash, set, zset, changes to RM_Scan API - Adding RM_ScanKey - Adding tests for RM_ScanKey - Refactoring RM_Scan API Changes in RM_Scan - cleanup in docs and coding convention - Moving out of experimantal Api - Adding ctx to scan callback - Dont use cursor of -1 as an indication of done (can be a valid cursor) - Set errno when returning 0 for various reasons - Rename Cursor to ScanCursor - Test filters key that are not strings, and opens a key if NULL --- tests/modules/scan.c | 107 ++++++++++++++++++++++++++++++------------ tests/unit/moduleapi/scan.tcl | 45 ++++++++++++++---- 2 files changed, 114 insertions(+), 38 deletions(-) (limited to 'tests') diff --git a/tests/modules/scan.c b/tests/modules/scan.c index 21071720a..afede244b 100644 --- a/tests/modules/scan.c +++ b/tests/modules/scan.c @@ -1,62 +1,109 @@ -#define REDISMODULE_EXPERIMENTAL_API #include "redismodule.h" #include #include #include -#define UNUSED(V) ((void) V) +typedef struct { + size_t nkeys; +} scan_strings_pd; -typedef struct scan_pd{ - size_t nkeys; - RedisModuleCtx *ctx; -} scan_pd; - -void scan_callback(void *privdata, RedisModuleString* keyname, RedisModuleKey* key){ - scan_pd* pd = privdata; - RedisModule_ReplyWithArray(pd->ctx, 2); +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; + } - RedisModule_ReplyWithString(pd->ctx, keyname); - if(key && RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_STRING){ + if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_STRING) { size_t len; char * data = RedisModule_StringDMA(key, &len, REDISMODULE_READ); - RedisModule_ReplyWithStringBuffer(pd->ctx, data, len); - }else{ - RedisModule_ReplyWithNull(pd->ctx); + RedisModule_ReplyWithArray(ctx, 2); + RedisModule_ReplyWithString(ctx, keyname); + RedisModule_ReplyWithStringBuffer(ctx, data, len); + pd->nkeys++; } - pd->nkeys++; + if (was_opened) + RedisModule_CloseKey(key); } -int scan_keys_values(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) +int scan_strings(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - scan_pd pd = { - .nkeys = 0, - .ctx = ctx, + REDISMODULE_NOT_USED(argv); + REDISMODULE_NOT_USED(argc); + scan_strings_pd pd = { + .nkeys = 0, }; RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN); - RedisModuleCursor* cursor = RedisModule_CursorCreate(); - while(RedisModule_Scan(ctx, cursor, scan_callback, &pd)); - RedisModule_CursorDestroy(cursor); + RedisModuleScanCursor* cursor = RedisModule_ScanCursorCreate(); + while(RedisModule_Scan(ctx, cursor, scan_strings_callback, &pd)); + RedisModule_ScanCursorDestroy(cursor); RedisModule_ReplySetArrayLength(ctx, pd.nkeys); - return 0; + 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); + RedisModule_ReplyWithString(pd->ctx, field); + if (value) + RedisModule_ReplyWithString(pd->ctx, value); + 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) { - UNUSED(argv); - UNUSED(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.scankeysvalues", scan_keys_values, "", 0, 0, 0) == 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/tests/unit/moduleapi/scan.tcl b/tests/unit/moduleapi/scan.tcl index 5a77e8195..de1672e0a 100644 --- a/tests/unit/moduleapi/scan.tcl +++ b/tests/unit/moduleapi/scan.tcl @@ -1,18 +1,47 @@ set testmodule [file normalize tests/modules/scan.so] -proc count_log_message {pattern} { - set result [exec grep -c $pattern < [srv 0 stdout]] -} - start_server {tags {"modules"}} { r module load $testmodule - test {Module scan} { - # the module create a scan command which also return values + test {Module scan keyspace} { + # the module create a scan command with filtering which also return values r set x 1 r set y 2 r set z 3 - lsort [r scan.scankeysvalues] + r hset h f v + lsort [r scan.scan_strings] } {{x 1} {y 2} {z 3}} -} \ No newline at end of file + test {Module scan hash ziplist} { + r hmset hh f1 v1 f2 v2 + lsort [r scan.scan_key hh] + } {{f1 v1} {f2 v2}} + + test {Module scan hash dict} { + r config set hash-max-ziplist-entries 2 + r hmset hh f3 v3 + lsort [r scan.scan_key hh] + } {{f1 v1} {f2 v2} {f3 v3}} + + test {Module scan zset ziplist} { + r zadd zz 1 f1 2 f2 + lsort [r scan.scan_key zz] + } {{f1 1} {f2 2}} + + test {Module scan zset dict} { + r config set zset-max-ziplist-entries 2 + r zadd zz 3 f3 + lsort [r scan.scan_key zz] + } {{f1 1} {f2 2} {f3 3}} + + test {Module scan set intset} { + r sadd ss 1 2 + lsort [r scan.scan_key ss] + } {{1 {}} {2 {}}} + + test {Module scan set dict} { + r config set set-max-intset-entries 2 + r sadd ss 3 + lsort [r scan.scan_key ss] + } {{1 {}} {2 {}} {3 {}}} +} -- cgit v1.2.1