summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorantirez <antirez@gmail.com>2019-10-31 17:55:18 +0100
committerantirez <antirez@gmail.com>2019-10-31 17:55:18 +0100
commit1ee195d25514abe77f0d38ae681da9c923de408f (patch)
treefa029fefac468c1b16257804ffaf1538c110b9df
parentdd5feec5e81288950784740ad23d9dc13b7df9a2 (diff)
parent764b420f5fddfe177638828a43c921eb949b140c (diff)
downloadredis-1ee195d25514abe77f0d38ae681da9c923de408f.tar.gz
Merge branch 'unstable' of github.com:/antirez/redis into unstable
-rw-r--r--.github/workflows/ci.yml28
-rwxr-xr-xruntest-moduleapi10
-rw-r--r--src/module.c46
-rw-r--r--src/redismodule.h19
-rw-r--r--tests/modules/Makefile1
-rw-r--r--tests/modules/fork.c6
-rw-r--r--tests/modules/misc.c55
-rw-r--r--tests/unit/moduleapi/misc.tcl19
8 files changed, 179 insertions, 5 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 000000000..847abcf02
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,28 @@
+name: CI
+
+on: [push, pull_request]
+
+jobs:
+ build-ubuntu:
+ strategy:
+ matrix:
+ platform: [ubuntu-latest, ubuntu-16.04]
+ runs-on: ${{ matrix.platform }}
+ steps:
+ - uses: actions/checkout@v1
+ - name: make
+ run: make
+ - name: test
+ run: |
+ sudo apt-get install tcl8.5
+ make test
+
+ build-macos-latest:
+ strategy:
+ matrix:
+ platform: [macos-latest, macOS-10.14]
+ runs-on: ${{ matrix.platform }}
+ steps:
+ - uses: actions/checkout@v1
+ - name: make
+ run: make
diff --git a/runtest-moduleapi b/runtest-moduleapi
index 444204919..9301002c9 100755
--- a/runtest-moduleapi
+++ b/runtest-moduleapi
@@ -13,4 +13,12 @@ then
fi
make -C tests/modules && \
-$TCLSH tests/test_helper.tcl --single unit/moduleapi/commandfilter --single unit/moduleapi/fork --single unit/moduleapi/testrdb --single unit/moduleapi/infotest --single unit/moduleapi/propagate --single unit/moduleapi/hooks "${@}"
+$TCLSH tests/test_helper.tcl \
+--single unit/moduleapi/commandfilter \
+--single unit/moduleapi/fork \
+--single unit/moduleapi/testrdb \
+--single unit/moduleapi/infotest \
+--single unit/moduleapi/propagate \
+--single unit/moduleapi/hooks \
+--single unit/moduleapi/misc \
+"${@}"
diff --git a/src/module.c b/src/module.c
index a4384163a..f298ec760 100644
--- a/src/module.c
+++ b/src/module.c
@@ -327,6 +327,13 @@ static struct RedisModuleForkInfo {
#define REDISMODULE_ARGV_NO_AOF (1<<1)
#define REDISMODULE_ARGV_NO_REPLICAS (1<<2)
+/* Determine whether Redis should signalModifiedKey implicitly.
+ * In case 'ctx' has no 'module' member (and therefore no module->options),
+ * we assume default behavior, that is, Redis signals.
+ * (see RM_GetThreadSafeContext) */
+#define SHOULD_SIGNAL_MODIFIED_KEYS(ctx) \
+ ctx->module? !(ctx->module->options & REDISMODULE_OPTION_NO_IMPLICIT_SIGNAL_MODIFIED) : 1
+
/* Server events hooks data structures and defines: this modules API
* allow modules to subscribe to certain events in Redis, such as
* the start and end of an RDB or AOF save, the change of role in replication,
@@ -827,6 +834,7 @@ void RM_SetModuleAttribs(RedisModuleCtx *ctx, const char *name, int ver, int api
module->filters = listCreate();
module->in_call = 0;
module->in_hook = 0;
+ module->options = 0;
ctx->module = module;
}
@@ -857,6 +865,12 @@ void RM_SetModuleOptions(RedisModuleCtx *ctx, int options) {
ctx->module->options = options;
}
+/* Signals that the key is modified from user's perspective (i.e. invalidate WATCH). */
+int RM_SignalModifiedKey(RedisModuleCtx *ctx, RedisModuleString *keyname) {
+ signalModifiedKey(ctx->client->db,keyname);
+ return REDISMODULE_OK;
+}
+
/* --------------------------------------------------------------------------
* Automatic memory management for modules
* -------------------------------------------------------------------------- */
@@ -1848,7 +1862,9 @@ void *RM_OpenKey(RedisModuleCtx *ctx, robj *keyname, int mode) {
/* Close a key handle. */
void RM_CloseKey(RedisModuleKey *key) {
if (key == NULL) return;
- if (key->mode & REDISMODULE_WRITE) signalModifiedKey(key->db,key->key);
+ int signal = SHOULD_SIGNAL_MODIFIED_KEYS(key->ctx);
+ if ((key->mode & REDISMODULE_WRITE) && signal)
+ signalModifiedKey(key->db,key->key);
/* TODO: if (key->iter) RM_KeyIteratorStop(kp); */
RM_ZsetRangeStop(key);
decrRefCount(key->key);
@@ -3072,7 +3088,10 @@ RedisModuleCallReply *RM_Call(RedisModuleCtx *ctx, const char *cmdname, const ch
/* We handle the above format error only when the client is setup so that
* we can free it normally. */
- if (argv == NULL) goto cleanup;
+ if (argv == NULL) {
+ errno = EINVAL;
+ goto cleanup;
+ }
/* Call command filters */
moduleCallCommandFilters(c);
@@ -3864,6 +3883,11 @@ const RedisModuleString *RM_GetKeyNameFromIO(RedisModuleIO *io) {
return io->key;
}
+/* Returns a RedisModuleString with the name of the key from RedisModuleKey */
+const RedisModuleString *RM_GetKeyNameFromModuleKey(RedisModuleKey *key) {
+ return key ? key->key : NULL;
+}
+
/* --------------------------------------------------------------------------
* Logging
* -------------------------------------------------------------------------- */
@@ -4537,6 +4561,20 @@ int RM_SubscribeToKeyspaceEvents(RedisModuleCtx *ctx, int types, RedisModuleNoti
return REDISMODULE_OK;
}
+/* Get the configured bitmap of notify-keyspace-events (Could be used
+ * for additional filtering in RedisModuleNotificationFunc) */
+int RM_GetNotifyKeyspaceEvents() {
+ return server.notify_keyspace_events;
+}
+
+/* Expose notifyKeyspaceEvent to modules */
+int RM_NotifyKeyspaceEvent(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key) {
+ if (!ctx || !ctx->client)
+ return REDISMODULE_ERR;
+ notifyKeyspaceEvent(type, (char *)event, key, ctx->client->db->id);
+ return REDISMODULE_OK;
+}
+
/* Dispatcher for keyspace notifications to module subscriber functions.
* This gets called only if at least one module requested to be notified on
* keyspace notifications */
@@ -6618,6 +6656,7 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API(ModuleTypeGetValue);
REGISTER_API(IsIOError);
REGISTER_API(SetModuleOptions);
+ REGISTER_API(SignalModifiedKey);
REGISTER_API(SaveUnsigned);
REGISTER_API(LoadUnsigned);
REGISTER_API(SaveSigned);
@@ -6640,6 +6679,7 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API(StringCompare);
REGISTER_API(GetContextFromIO);
REGISTER_API(GetKeyNameFromIO);
+ REGISTER_API(GetKeyNameFromModuleKey);
REGISTER_API(BlockClient);
REGISTER_API(UnblockClient);
REGISTER_API(IsBlockedReplyRequest);
@@ -6654,6 +6694,8 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API(DigestAddStringBuffer);
REGISTER_API(DigestAddLongLong);
REGISTER_API(DigestEndSequence);
+ REGISTER_API(NotifyKeyspaceEvent);
+ REGISTER_API(GetNotifyKeyspaceEvents);
REGISTER_API(SubscribeToKeyspaceEvents);
REGISTER_API(RegisterClusterMessageReceiver);
REGISTER_API(SendClusterMessage);
diff --git a/src/redismodule.h b/src/redismodule.h
index 1b284770b..ea0d6a139 100644
--- a/src/redismodule.h
+++ b/src/redismodule.h
@@ -106,6 +106,11 @@
/* There is currently some background process active. */
#define REDISMODULE_CTX_FLAGS_ACTIVE_CHILD (1<<18)
+/* Keyspace changes notification classes. Every class is associated with a
+ * character for configuration purposes.
+ * NOTE: These have to be in sync with NOTIFY_* in server.h */
+#define REDISMODULE_NOTIFY_KEYSPACE (1<<0) /* K */
+#define REDISMODULE_NOTIFY_KEYEVENT (1<<1) /* E */
#define REDISMODULE_NOTIFY_GENERIC (1<<2) /* g */
#define REDISMODULE_NOTIFY_STRING (1<<3) /* $ */
#define REDISMODULE_NOTIFY_LIST (1<<4) /* l */
@@ -161,6 +166,10 @@ typedef uint64_t RedisModuleTimerID;
/* Declare that the module can handle errors with RedisModule_SetModuleOptions. */
#define REDISMODULE_OPTIONS_HANDLE_IO_ERRORS (1<<0)
+/* When set, Redis will not call RedisModule_SignalModifiedKey(), implicitly in
+ * RedisModule_CloseKey, and the module needs to do that when manually when keys
+ * are modified from the user's sperspective, to invalidate WATCH. */
+#define REDISMODULE_OPTION_NO_IMPLICIT_SIGNAL_MODIFIED (1<<1)
/* Server events definitions. */
#define REDISMODULE_EVENT_REPLICATION_ROLE_CHANGED 0
@@ -181,7 +190,7 @@ typedef struct RedisModuleEvent {
struct RedisModuleCtx;
typedef void (*RedisModuleEventCallback)(struct RedisModuleCtx *ctx, RedisModuleEvent eid, uint64_t subevent, void *data);
-static RedisModuleEvent
+static const RedisModuleEvent
RedisModuleEvent_ReplicationRoleChanged = {
REDISMODULE_EVENT_REPLICATION_ROLE_CHANGED,
1
@@ -434,6 +443,7 @@ RedisModuleType *REDISMODULE_API_FUNC(RedisModule_ModuleTypeGetType)(RedisModule
void *REDISMODULE_API_FUNC(RedisModule_ModuleTypeGetValue)(RedisModuleKey *key);
int REDISMODULE_API_FUNC(RedisModule_IsIOError)(RedisModuleIO *io);
void REDISMODULE_API_FUNC(RedisModule_SetModuleOptions)(RedisModuleCtx *ctx, int options);
+int REDISMODULE_API_FUNC(RedisModule_SignalModifiedKey)(RedisModuleCtx *ctx, RedisModuleString *keyname);
void REDISMODULE_API_FUNC(RedisModule_SaveUnsigned)(RedisModuleIO *io, uint64_t value);
uint64_t REDISMODULE_API_FUNC(RedisModule_LoadUnsigned)(RedisModuleIO *io);
void REDISMODULE_API_FUNC(RedisModule_SaveSigned)(RedisModuleIO *io, int64_t value);
@@ -456,6 +466,7 @@ void REDISMODULE_API_FUNC(RedisModule_RetainString)(RedisModuleCtx *ctx, RedisMo
int REDISMODULE_API_FUNC(RedisModule_StringCompare)(RedisModuleString *a, RedisModuleString *b);
RedisModuleCtx *REDISMODULE_API_FUNC(RedisModule_GetContextFromIO)(RedisModuleIO *io);
const RedisModuleString *REDISMODULE_API_FUNC(RedisModule_GetKeyNameFromIO)(RedisModuleIO *io);
+const RedisModuleString *REDISMODULE_API_FUNC(RedisModule_GetKeyNameFromModuleKey)(RedisModuleKey *key);
long long REDISMODULE_API_FUNC(RedisModule_Milliseconds)(void);
void REDISMODULE_API_FUNC(RedisModule_DigestAddStringBuffer)(RedisModuleDigest *md, unsigned char *ele, size_t len);
void REDISMODULE_API_FUNC(RedisModule_DigestAddLongLong)(RedisModuleDigest *md, long long ele);
@@ -511,6 +522,8 @@ void REDISMODULE_API_FUNC(RedisModule_FreeThreadSafeContext)(RedisModuleCtx *ctx
void REDISMODULE_API_FUNC(RedisModule_ThreadSafeContextLock)(RedisModuleCtx *ctx);
void REDISMODULE_API_FUNC(RedisModule_ThreadSafeContextUnlock)(RedisModuleCtx *ctx);
int REDISMODULE_API_FUNC(RedisModule_SubscribeToKeyspaceEvents)(RedisModuleCtx *ctx, int types, RedisModuleNotificationFunc cb);
+int REDISMODULE_API_FUNC(RedisModule_NotifyKeyspaceEvent)(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key);
+int REDISMODULE_API_FUNC(RedisModule_GetNotifyKeyspaceEvents)();
int REDISMODULE_API_FUNC(RedisModule_BlockedClientDisconnected)(RedisModuleCtx *ctx);
void REDISMODULE_API_FUNC(RedisModule_RegisterClusterMessageReceiver)(RedisModuleCtx *ctx, uint8_t type, RedisModuleClusterMessageReceiver callback);
int REDISMODULE_API_FUNC(RedisModule_SendClusterMessage)(RedisModuleCtx *ctx, char *target_id, uint8_t type, unsigned char *msg, uint32_t len);
@@ -632,6 +645,7 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API(ModuleTypeGetValue);
REDISMODULE_GET_API(IsIOError);
REDISMODULE_GET_API(SetModuleOptions);
+ REDISMODULE_GET_API(SignalModifiedKey);
REDISMODULE_GET_API(SaveUnsigned);
REDISMODULE_GET_API(LoadUnsigned);
REDISMODULE_GET_API(SaveSigned);
@@ -654,6 +668,7 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API(StringCompare);
REDISMODULE_GET_API(GetContextFromIO);
REDISMODULE_GET_API(GetKeyNameFromIO);
+ REDISMODULE_GET_API(GetKeyNameFromModuleKey);
REDISMODULE_GET_API(Milliseconds);
REDISMODULE_GET_API(DigestAddStringBuffer);
REDISMODULE_GET_API(DigestAddLongLong);
@@ -709,6 +724,8 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API(AbortBlock);
REDISMODULE_GET_API(SetDisconnectCallback);
REDISMODULE_GET_API(SubscribeToKeyspaceEvents);
+ REDISMODULE_GET_API(NotifyKeyspaceEvent);
+ REDISMODULE_GET_API(GetNotifyKeyspaceEvents);
REDISMODULE_GET_API(BlockedClientDisconnected);
REDISMODULE_GET_API(RegisterClusterMessageReceiver);
REDISMODULE_GET_API(SendClusterMessage);
diff --git a/tests/modules/Makefile b/tests/modules/Makefile
index f357faad2..71c0b5ef8 100644
--- a/tests/modules/Makefile
+++ b/tests/modules/Makefile
@@ -17,6 +17,7 @@ TEST_MODULES = \
fork.so \
infotest.so \
propagate.so \
+ misc.so \
hooks.so
.PHONY: all
diff --git a/tests/modules/fork.c b/tests/modules/fork.c
index 0804e4355..1a139ef1b 100644
--- a/tests/modules/fork.c
+++ b/tests/modules/fork.c
@@ -1,6 +1,10 @@
#define REDISMODULE_EXPERIMENTAL_API
-#include "redismodule.h"
+/* define macros for having usleep */
+#define _BSD_SOURCE
+#define _DEFAULT_SOURCE
+
+#include "redismodule.h"
#include <string.h>
#include <assert.h>
#include <unistd.h>
diff --git a/tests/modules/misc.c b/tests/modules/misc.c
new file mode 100644
index 000000000..fd892f52c
--- /dev/null
+++ b/tests/modules/misc.c
@@ -0,0 +1,55 @@
+#define REDISMODULE_EXPERIMENTAL_API
+#include "redismodule.h"
+
+#include <string.h>
+#include <assert.h>
+#include <unistd.h>
+#include <errno.h>
+
+int test_call_generic(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+ if (argc<2) {
+ RedisModule_WrongArity(ctx);
+ return REDISMODULE_OK;
+ }
+
+ const char* cmdname = RedisModule_StringPtrLen(argv[1], NULL);
+ RedisModuleCallReply *reply = RedisModule_Call(ctx, cmdname, "v", argv+2, argc-2);
+ if (reply) {
+ RedisModule_ReplyWithCallReply(ctx, reply);
+ RedisModule_FreeCallReply(reply);
+ } else {
+ RedisModule_ReplyWithError(ctx, strerror(errno));
+ }
+ return REDISMODULE_OK;
+}
+
+int test_call_info(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+ RedisModuleCallReply *reply;
+ if (argc>1)
+ reply = RedisModule_Call(ctx, "info", "s", argv[1]);
+ else
+ reply = RedisModule_Call(ctx, "info", "");
+ if (reply) {
+ RedisModule_ReplyWithCallReply(ctx, reply);
+ RedisModule_FreeCallReply(reply);
+ } else {
+ RedisModule_ReplyWithError(ctx, strerror(errno));
+ }
+ return REDISMODULE_OK;
+}
+
+int 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_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;
+
+ return REDISMODULE_OK;
+}
diff --git a/tests/unit/moduleapi/misc.tcl b/tests/unit/moduleapi/misc.tcl
new file mode 100644
index 000000000..d392aeab0
--- /dev/null
+++ b/tests/unit/moduleapi/misc.tcl
@@ -0,0 +1,19 @@
+set testmodule [file normalize tests/modules/misc.so]
+
+
+start_server {tags {"modules"}} {
+ r module load $testmodule
+
+ test {test RM_Call} {
+ set info [r test.call_info commandstats]
+ # cmdstat is not in a default section, so we also test an argument was passed
+ assert { [string match "*cmdstat_module*" $info] }
+ }
+
+ test {test RM_Call args array} {
+ set info [r test.call_generic info commandstats]
+ # cmdstat is not in a default section, so we also test an argument was passed
+ assert { [string match "*cmdstat_module*" $info] }
+ }
+
+}