Unverified Commit 5c3938d5 authored by Meir Shpilraien (Spielrein)'s avatar Meir Shpilraien (Spielrein) Committed by GitHub
Browse files

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.
parent 66bed3f2
...@@ -544,7 +544,7 @@ static void handleClientsBlockedOnKey(readyList *rl) { ...@@ -544,7 +544,7 @@ static void handleClientsBlockedOnKey(readyList *rl) {
while((ln = listNext(&li))) { while((ln = listNext(&li))) {
client *receiver = listNodeValue(ln); 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 /* 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. * blocked type, since we might process the wrong key type.
* 2. We want to serve clients blocked on module keys * 2. We want to serve clients blocked on module keys
......
...@@ -3783,17 +3783,29 @@ static void moduleInitKeyTypeSpecific(RedisModuleKey *key) { ...@@ -3783,17 +3783,29 @@ static void moduleInitKeyTypeSpecific(RedisModuleKey *key) {
* The return value is the handle representing the key, that must be * The return value is the handle representing the key, that must be
* closed with RM_CloseKey(). * 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 * is still returned, since it is possible to perform operations on
* a yet not existing key (that will be created, for example, after * 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 * key does not exist, NULL is returned. However it is still safe to
* call RedisModule_CloseKey() and RedisModule_KeyType() on a NULL * 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 *RM_OpenKey(RedisModuleCtx *ctx, robj *keyname, int mode) {
RedisModuleKey *kp; RedisModuleKey *kp;
robj *value; 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) { if (mode & REDISMODULE_WRITE) {
value = lookupKeyWriteWithFlags(ctx->client->db,keyname, flags); value = lookupKeyWriteWithFlags(ctx->client->db,keyname, flags);
...@@ -3811,6 +3823,23 @@ RedisModuleKey *RM_OpenKey(RedisModuleCtx *ctx, robj *keyname, int mode) { ...@@ -3811,6 +3823,23 @@ RedisModuleKey *RM_OpenKey(RedisModuleCtx *ctx, robj *keyname, int mode) {
return kp; 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). */ /* Destroy a RedisModuleKey struct (freeing is the responsibility of the caller). */
static void moduleCloseKey(RedisModuleKey *key) { static void moduleCloseKey(RedisModuleKey *key) {
int signal = SHOULD_SIGNAL_MODIFIED_KEYS(key->ctx); int signal = SHOULD_SIGNAL_MODIFIED_KEYS(key->ctx);
...@@ -12809,6 +12838,7 @@ void moduleRegisterCoreAPI(void) { ...@@ -12809,6 +12838,7 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API(SelectDb); REGISTER_API(SelectDb);
REGISTER_API(KeyExists); REGISTER_API(KeyExists);
REGISTER_API(OpenKey); REGISTER_API(OpenKey);
REGISTER_API(GetOpenKeyModesAll);
REGISTER_API(CloseKey); REGISTER_API(CloseKey);
REGISTER_API(KeyType); REGISTER_API(KeyType);
REGISTER_API(ValueLength); REGISTER_API(ValueLength);
......
...@@ -48,6 +48,18 @@ typedef long long ustime_t; ...@@ -48,6 +48,18 @@ typedef long long ustime_t;
/* RedisModule_OpenKey extra flags for the 'mode' argument. /* RedisModule_OpenKey extra flags for the 'mode' argument.
* Avoid touching the LRU/LFU of the key when opened. */ * Avoid touching the LRU/LFU of the key when opened. */
#define REDISMODULE_OPEN_KEY_NOTOUCH (1<<16) #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 */ /* List push and pop */
#define REDISMODULE_LIST_HEAD 0 #define REDISMODULE_LIST_HEAD 0
...@@ -955,6 +967,7 @@ REDISMODULE_API int (*RedisModule_GetSelectedDb)(RedisModuleCtx *ctx) REDISMODUL ...@@ -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_SelectDb)(RedisModuleCtx *ctx, int newid) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_KeyExists)(RedisModuleCtx *ctx, RedisModuleString *keyname) 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 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 void (*RedisModule_CloseKey)(RedisModuleKey *kp) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_KeyType)(RedisModuleKey *kp) REDISMODULE_ATTR; REDISMODULE_API int (*RedisModule_KeyType)(RedisModuleKey *kp) REDISMODULE_ATTR;
REDISMODULE_API size_t (*RedisModule_ValueLength)(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 ...@@ -1326,6 +1339,7 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API(SelectDb); REDISMODULE_GET_API(SelectDb);
REDISMODULE_GET_API(KeyExists); REDISMODULE_GET_API(KeyExists);
REDISMODULE_GET_API(OpenKey); REDISMODULE_GET_API(OpenKey);
REDISMODULE_GET_API(GetOpenKeyModesAll);
REDISMODULE_GET_API(CloseKey); REDISMODULE_GET_API(CloseKey);
REDISMODULE_GET_API(KeyType); REDISMODULE_GET_API(KeyType);
REDISMODULE_GET_API(ValueLength); REDISMODULE_GET_API(ValueLength);
......
...@@ -3155,7 +3155,7 @@ int objectSetLRUOrLFU(robj *val, long long lfu_freq, long long lru_idle, ...@@ -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_NOSTATS (1<<2) /* Don't update keyspace hits/misses counters. */
#define LOOKUP_WRITE (1<<3) /* Delete expired keys even in replicas. */ #define LOOKUP_WRITE (1<<3) /* Delete expired keys even in replicas. */
#define LOOKUP_NOEXPIRE (1<<4) /* Avoid deleting lazy expired keys. */ #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); void dbAdd(redisDb *db, robj *key, robj *val);
int dbAddRDBLoad(redisDb *db, sds key, robj *val); int dbAddRDBLoad(redisDb *db, sds key, robj *val);
......
...@@ -8,6 +8,55 @@ ...@@ -8,6 +8,55 @@
#define UNUSED(x) (void)(x) #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) int test_call_generic(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{ {
if (argc<2) { if (argc<2) {
...@@ -460,6 +509,10 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) ...@@ -460,6 +509,10 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
if (RedisModule_Init(ctx,"misc",1,REDISMODULE_APIVER_1)== REDISMODULE_ERR) if (RedisModule_Init(ctx,"misc",1,REDISMODULE_APIVER_1)== REDISMODULE_ERR)
return 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) if (RedisModule_CreateCommand(ctx,"test.call_generic", test_call_generic,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR; return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.call_info", test_call_info,"",0,0,0) == 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) ...@@ -507,6 +560,12 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
return REDISMODULE_ERR; return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx, "test.rm_call_replicate", test_rm_call_replicate,"allow-stale", 0, 0, 0) == REDISMODULE_ERR) if (RedisModule_CreateCommand(ctx, "test.rm_call_replicate", test_rm_call_replicate,"allow-stale", 0, 0, 0) == REDISMODULE_ERR)
return 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; return REDISMODULE_OK;
} }
...@@ -485,6 +485,16 @@ start_server {tags {"modules"}} { ...@@ -485,6 +485,16 @@ start_server {tags {"modules"}} {
assert_equal [r get x] $x 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" { test "Unload the module - misc" {
assert_equal {OK} [r module unload misc] assert_equal {OK} [r module unload misc]
} }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment