From bebb2e19947b4e72e2c446d66341aa5492663c79 Mon Sep 17 00:00:00 2001 From: antirez Date: Mon, 21 Oct 2019 17:16:34 +0200 Subject: [PATCH] Modules hooks: a first version of events and some API. --- src/module.c | 58 ++++++++++++++++++++++++++++++++++----- src/redismodule.h | 69 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+), 7 deletions(-) diff --git a/src/module.c b/src/module.c index 554a949b9..e3267cdbc 100644 --- a/src/module.c +++ b/src/module.c @@ -345,13 +345,6 @@ static struct RedisModuleForkInfo { #define REDISMODULE_EVENT_ID_MASTER_LINK_UP 14 #define REDISMODULE_EVENT_ID_MASTER_LINK_DOWN 15 -typedef struct RedisModuleEvent { - uint64_t id; /* REDISMODULE_EVENT_ID_... defines. */ - uint64_t dataver; /* Version of the structure we pass as 'data'. */ -} RedisModuleEvent; - -typedef int (*RedisModuleEventCallback)(RedisModuleEvent eid, void *data); - typedef struct RedisModuleEventListener { RedisModule *module; RedisModuleEvent event; @@ -5587,6 +5580,57 @@ void ModuleForkDoneHandler(int exitcode, int bysignal) { moduleForkInfo.done_handler_user_data = NULL; } +/* -------------------------------------------------------------------------- + * Server hooks implementation + * -------------------------------------------------------------------------- */ + +/* Register to be notified, via a callback, when the specified server event + * happens. The callback is called with the event as argument, and an additional + * argument which is a void pointer and should be cased to a specific type + * that is event-specific (but many events will just use NULL since they do not + * have additional information to pass to the callback). + * + * If the callback is NULL and there was a previous subscription, the module + * will be unsubscribed. If there was a previous subscription and the callback + * is not null, the old callback will be replaced with the new one. + * + * The function returns REDISMODULE_OK if the module was successfully subscrived + * for the specified event. If the API is called from a wrong context then + * REDISMODULE_ERR is returned. */ +int RedisModule_SubscribeToServerEvent(RedisModuleCtx *ctx, RedisModuleEvent event, RedisModuleEventCallback callback) { + RedisModuleEventListener *el; + + /* Protect in case of calls from contexts without a module reference. */ + if (ctx->module == NULL) return REDISMODULE_ERR; + + /* Search an event matching this module and event ID. */ + listIter li; + listNode *ln; + listRewind(RedisModule_EventListeners,&li); + while((ln = listNext(&li))) { + el = ln->value; + if (el->module == ctx->module && el->event.id == event.id) + break; /* Matching event found. */ + } + + /* Modify or remove the event listener if we already had one. */ + if (ln) { + if (callback == NULL) + listDelNode(RedisModule_EventListeners,ln); + else + el->callback = callback; /* Update the callback with the new one. */ + return REDISMODULE_OK; + } + + /* No event found, we need to add a new one. */ + el = zmalloc(sizeof(*el)); + el->module = ctx->module; + el->event = event; + el->callback = callback; + listAddNodeTail(RedisModule_EventListeners,el); + return REDISMODULE_OK; +} + /* -------------------------------------------------------------------------- * Modules API internals * -------------------------------------------------------------------------- */ diff --git a/src/redismodule.h b/src/redismodule.h index 19a9cd897..1b8885e04 100644 --- a/src/redismodule.h +++ b/src/redismodule.h @@ -162,6 +162,75 @@ typedef uint64_t RedisModuleTimerID; /* Declare that the module can handle errors with RedisModule_SetModuleOptions. */ #define REDISMODULE_OPTIONS_HANDLE_IO_ERRORS (1<<0) +/* Server events definitions. */ +#define REDISMODULE_EVENT_ID_REPLICATION_ROLE_CHANGED 0 +#define REDISMODULE_EVENT_ID_PERSISTENCE 1 +#define REDISMODULE_EVENT_ID_FLUSHDB 2 +#define REDISMODULE_EVENT_ID_LOADING 3 +#define REDISMODULE_EVENT_ID_CLIENT_CHANGE 4 +#define REDISMODULE_EVENT_ID_SHUTDOWN 5 +#define REDISMODULE_EVENT_ID_REPLICA_CHANGE 6 +#define REDISMODULE_EVENT_ID_MASTER_LINK_CHANGE 7 + +typedef struct RedisModuleEvent { + uint64_t id; /* REDISMODULE_EVENT_ID_... defines. */ + uint64_t dataver; /* Version of the structure we pass as 'data'. */ +} RedisModuleEvent; + +RedisModuleEvent + RedisModuleEvent_ReplicationRoleChanged = { + REDISMODULE_EVENT_ID_REPLICATION_ROLE_CHANGED, + 0 + }, + RedisModuleEvent_Persistence = { + REDISMODULE_EVENT_ID_PERSISTENCE, + 0 + }, + RedisModuleEvent_FlushDB = { + REDISMODULE_EVENT_ID_FLUSHDB, + 0 + }, + RedisModuleEvent_Loading = { + REDISMODULE_EVENT_ID_LOADING, + 0 + }, + RedisModuleEvent_ClientChange = { + REDISMODULE_EVENT_ID_CLIENT_CHANGE, + 0 + }, + RedisModuleEvent_Shutdown = { + REDISMODULE_EVENT_ID_SHUTDOWN, + 0 + }, + RedisModuleEvent_ReplicaChange = { + REDISMODULE_EVENT_ID_REPLICA_CHANGE, + 0 + }, + RedisModuleEvent_MasterLinkChange = { + REDISMODULE_EVENT_ID_MASTER_LINK_CHANGE, + 0 + }; + +typedef int (*RedisModuleEventCallback)(RedisModuleEvent eid, uint64_t subevent, void *data); + +/* Those are values that are used for the 'subevent' callback argument. */ +#define REDISMODULE_EVENT_PERSISTENCE_RDB_START 0 +#define REDISMODULE_EVENT_PERSISTENCE_RDB_END 1 +#define REDISMODULE_EVENT_PERSISTENCE_AOF_START 2 +#define REDISMODULE_EVENT_PERSISTENCE_AOF_END 3 + +#define REDISMODULE_EVENT_LOADING_START 0 +#define REDISMODULE_EVENT_LOADING_END 1 + +#define REDISMODULE_EVENT_CLIENT_CHANGE_CONNECTED 0 +#define REDISMODULE_EVENT_CLIENT_CHANGE_DISCONNECTED 1 + +#define REDISMODULE_EVENT_MASTER_LINK_UP 0 +#define REDISMODULE_EVENT_MASTER_LINK_DOWN 1 + +#define REDISMODULE_EVENT_REPLICA_CHANGE_CONNECTED 0 +#define REDISMODULE_EVENT_REPLICA_CHANGE_DISCONNECTED 1 + /* ------------------------- End of common defines ------------------------ */ #ifndef REDISMODULE_CORE