diff options
author | Meir Shpilraien (Spielrein) <meir@redis.com> | 2023-02-09 14:59:05 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-09 14:59:05 +0200 |
commit | 5c3938d5cc08b42acc99f314d92f9e0d5671f96e (patch) | |
tree | 0494f545c08dfe96af7379dceb35402f42198de1 | |
parent | 66bed3f220d3ae73449b6b9f89ae616e42eff2e4 (diff) | |
download | redis-5c3938d5cc08b42acc99f314d92f9e0d5671f96e.tar.gz |
Match REDISMODULE_OPEN_KEY_* flags to LOOKUP_* flags (#11772)
The PR adds support for the following flags on RedisModule_OpenKey:
* REDISMODULE_OPEN_KEY_NONOTIFY - Don't trigger keyspace event on key misses.
* REDISMODULE_OPEN_KEY_NOSTATS - Don't update keyspace hits/misses counters.
* REDISMODULE_OPEN_KEY_NOEXPIRE - Avoid deleting lazy expired keys.
* REDISMODULE_OPEN_KEY_NOEFFECTS - Avoid any effects from fetching the key
In addition, added `RM_GetOpenKeyModesAll`, which returns the mask of all
supported OpenKey modes. This allows the module to check, in runtime, which
OpenKey modes are supported by the current Redis instance.
-rw-r--r-- | src/blocked.c | 2 | ||||
-rw-r--r-- | src/module.c | 38 | ||||
-rw-r--r-- | src/redismodule.h | 14 | ||||
-rw-r--r-- | src/server.h | 2 | ||||
-rw-r--r-- | tests/modules/misc.c | 59 | ||||
-rw-r--r-- | tests/unit/moduleapi/misc.tcl | 10 |
6 files changed, 119 insertions, 6 deletions
diff --git a/src/blocked.c b/src/blocked.c index ff38f70b3..8fd355852 100644 --- a/src/blocked.c +++ b/src/blocked.c @@ -544,7 +544,7 @@ static void handleClientsBlockedOnKey(readyList *rl) { while((ln = listNext(&li))) { client *receiver = listNodeValue(ln); - robj *o = lookupKeyReadWithFlags(rl->db, rl->key, LOKKUP_NOEFFECTS); + robj *o = lookupKeyReadWithFlags(rl->db, rl->key, LOOKUP_NOEFFECTS); /* 1. In case new key was added/touched we need to verify it satisfy the * blocked type, since we might process the wrong key type. * 2. We want to serve clients blocked on module keys diff --git a/src/module.c b/src/module.c index 2eab04b2d..94f420b54 100644 --- a/src/module.c +++ b/src/module.c @@ -3783,17 +3783,29 @@ static void moduleInitKeyTypeSpecific(RedisModuleKey *key) { * The return value is the handle representing the key, that must be * closed with RM_CloseKey(). * - * If the key does not exist and WRITE mode is requested, the handle + * If the key does not exist and REDISMODULE_WRITE mode is requested, the handle * is still returned, since it is possible to perform operations on * a yet not existing key (that will be created, for example, after - * a list push operation). If the mode is just READ instead, and the + * a list push operation). If the mode is just REDISMODULE_READ instead, and the * key does not exist, NULL is returned. However it is still safe to * call RedisModule_CloseKey() and RedisModule_KeyType() on a NULL - * value. */ + * value. + * + * Extra flags that can be pass to the API under the mode argument: + * * REDISMODULE_OPEN_KEY_NOTOUCH - Avoid touching the LRU/LFU of the key when opened. + * * REDISMODULE_OPEN_KEY_NONOTIFY - Don't trigger keyspace event on key misses. + * * REDISMODULE_OPEN_KEY_NOSTATS - Don't update keyspace hits/misses counters. + * * REDISMODULE_OPEN_KEY_NOEXPIRE - Avoid deleting lazy expired keys. + * * REDISMODULE_OPEN_KEY_NOEFFECTS - Avoid any effects from fetching the key. */ RedisModuleKey *RM_OpenKey(RedisModuleCtx *ctx, robj *keyname, int mode) { RedisModuleKey *kp; robj *value; - int flags = mode & REDISMODULE_OPEN_KEY_NOTOUCH? LOOKUP_NOTOUCH: 0; + int flags = 0; + flags |= (mode & REDISMODULE_OPEN_KEY_NOTOUCH? LOOKUP_NOTOUCH: 0); + flags |= (mode & REDISMODULE_OPEN_KEY_NONOTIFY? LOOKUP_NONOTIFY: 0); + flags |= (mode & REDISMODULE_OPEN_KEY_NOSTATS? LOOKUP_NOSTATS: 0); + flags |= (mode & REDISMODULE_OPEN_KEY_NOEXPIRE? LOOKUP_NOEXPIRE: 0); + flags |= (mode & REDISMODULE_OPEN_KEY_NOEFFECTS? LOOKUP_NOEFFECTS: 0); if (mode & REDISMODULE_WRITE) { value = lookupKeyWriteWithFlags(ctx->client->db,keyname, flags); @@ -3811,6 +3823,23 @@ RedisModuleKey *RM_OpenKey(RedisModuleCtx *ctx, robj *keyname, int mode) { return kp; } +/** + * Returns the full OpenKey modes mask, using the return value + * the module can check if a certain set of OpenKey modes are supported + * by the redis server version in use. + * Example: + * + * int supportedMode = RM_GetOpenKeyModesAll(); + * if (supportedMode & REDISMODULE_OPEN_KEY_NOTOUCH) { + * // REDISMODULE_OPEN_KEY_NOTOUCH is supported + * } else{ + * // REDISMODULE_OPEN_KEY_NOTOUCH is not supported + * } + */ +int RM_GetOpenKeyModesAll() { + return _REDISMODULE_OPEN_KEY_ALL; +} + /* Destroy a RedisModuleKey struct (freeing is the responsibility of the caller). */ static void moduleCloseKey(RedisModuleKey *key) { int signal = SHOULD_SIGNAL_MODIFIED_KEYS(key->ctx); @@ -12809,6 +12838,7 @@ void moduleRegisterCoreAPI(void) { REGISTER_API(SelectDb); REGISTER_API(KeyExists); REGISTER_API(OpenKey); + REGISTER_API(GetOpenKeyModesAll); REGISTER_API(CloseKey); REGISTER_API(KeyType); REGISTER_API(ValueLength); diff --git a/src/redismodule.h b/src/redismodule.h index d65687a23..342340af4 100644 --- a/src/redismodule.h +++ b/src/redismodule.h @@ -48,6 +48,18 @@ typedef long long ustime_t; /* RedisModule_OpenKey extra flags for the 'mode' argument. * Avoid touching the LRU/LFU of the key when opened. */ #define REDISMODULE_OPEN_KEY_NOTOUCH (1<<16) +/* Don't trigger keyspace event on key misses. */ +#define REDISMODULE_OPEN_KEY_NONOTIFY (1<<17) +/* Don't update keyspace hits/misses counters. */ +#define REDISMODULE_OPEN_KEY_NOSTATS (1<<18) +/* Avoid deleting lazy expired keys. */ +#define REDISMODULE_OPEN_KEY_NOEXPIRE (1<<19) +/* Avoid any effects from fetching the key */ +#define REDISMODULE_OPEN_KEY_NOEFFECTS (1<<20) +/* Mask of all REDISMODULE_OPEN_KEY_* values. Any new mode should be added to this list. + * Should not be used directly by the module, use RM_GetOpenKeyModesAll instead. + * Located here so when we will add new modes we will not forget to update it. */ +#define _REDISMODULE_OPEN_KEY_ALL REDISMODULE_READ | REDISMODULE_WRITE | REDISMODULE_OPEN_KEY_NOTOUCH | REDISMODULE_OPEN_KEY_NONOTIFY | REDISMODULE_OPEN_KEY_NOSTATS | REDISMODULE_OPEN_KEY_NOEXPIRE | REDISMODULE_OPEN_KEY_NOEFFECTS /* List push and pop */ #define REDISMODULE_LIST_HEAD 0 @@ -955,6 +967,7 @@ REDISMODULE_API int (*RedisModule_GetSelectedDb)(RedisModuleCtx *ctx) REDISMODUL REDISMODULE_API int (*RedisModule_SelectDb)(RedisModuleCtx *ctx, int newid) REDISMODULE_ATTR; REDISMODULE_API int (*RedisModule_KeyExists)(RedisModuleCtx *ctx, RedisModuleString *keyname) REDISMODULE_ATTR; REDISMODULE_API RedisModuleKey * (*RedisModule_OpenKey)(RedisModuleCtx *ctx, RedisModuleString *keyname, int mode) REDISMODULE_ATTR; +REDISMODULE_API int (*RedisModule_GetOpenKeyModesAll)() REDISMODULE_ATTR; REDISMODULE_API void (*RedisModule_CloseKey)(RedisModuleKey *kp) REDISMODULE_ATTR; REDISMODULE_API int (*RedisModule_KeyType)(RedisModuleKey *kp) REDISMODULE_ATTR; REDISMODULE_API size_t (*RedisModule_ValueLength)(RedisModuleKey *kp) REDISMODULE_ATTR; @@ -1326,6 +1339,7 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int REDISMODULE_GET_API(SelectDb); REDISMODULE_GET_API(KeyExists); REDISMODULE_GET_API(OpenKey); + REDISMODULE_GET_API(GetOpenKeyModesAll); REDISMODULE_GET_API(CloseKey); REDISMODULE_GET_API(KeyType); REDISMODULE_GET_API(ValueLength); diff --git a/src/server.h b/src/server.h index bf0a15459..f639ce150 100644 --- a/src/server.h +++ b/src/server.h @@ -3155,7 +3155,7 @@ int objectSetLRUOrLFU(robj *val, long long lfu_freq, long long lru_idle, #define LOOKUP_NOSTATS (1<<2) /* Don't update keyspace hits/misses counters. */ #define LOOKUP_WRITE (1<<3) /* Delete expired keys even in replicas. */ #define LOOKUP_NOEXPIRE (1<<4) /* Avoid deleting lazy expired keys. */ -#define LOKKUP_NOEFFECTS (LOOKUP_NONOTIFY | LOOKUP_NOSTATS | LOOKUP_NOTOUCH | LOOKUP_NOEXPIRE) /* Avoid any effects from fetching the key */ +#define LOOKUP_NOEFFECTS (LOOKUP_NONOTIFY | LOOKUP_NOSTATS | LOOKUP_NOTOUCH | LOOKUP_NOEXPIRE) /* Avoid any effects from fetching the key */ void dbAdd(redisDb *db, robj *key, robj *val); int dbAddRDBLoad(redisDb *db, sds key, robj *val); diff --git a/tests/modules/misc.c b/tests/modules/misc.c index cc159a6ce..46bfcb117 100644 --- a/tests/modules/misc.c +++ b/tests/modules/misc.c @@ -8,6 +8,55 @@ #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) { @@ -460,6 +509,10 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int 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) @@ -507,6 +560,12 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) 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; return REDISMODULE_OK; } diff --git a/tests/unit/moduleapi/misc.tcl b/tests/unit/moduleapi/misc.tcl index 0f076c6b5..8c17ca378 100644 --- a/tests/unit/moduleapi/misc.tcl +++ b/tests/unit/moduleapi/misc.tcl @@ -485,6 +485,16 @@ start_server {tags {"modules"}} { assert_equal [r get x] $x } + test {test silent open key} { + r debug set-active-expire 0 + r test.clear_n_events + r set x 1 PX 10 + after 1000 + # now the key has been expired, open it silently and make sure not event were fired. + assert_error {key not found} {r test.silent_open_key x} + assert_equal {0} [r test.get_n_events] + } + test "Unload the module - misc" { assert_equal {OK} [r module unload misc] } |