Modules hooks: event firing logic.

This commit is contained in:
antirez 2019-10-21 17:51:18 +02:00
parent 6e56f513b4
commit b9af7e24e4
2 changed files with 88 additions and 19 deletions

View File

@ -1553,19 +1553,22 @@ unsigned long long RM_GetClientId(RedisModuleCtx *ctx) {
* fields depending on the version provided. If the version is not valid * fields depending on the version provided. If the version is not valid
* then REDISMODULE_ERR is returned. Otherwise the function returns * then REDISMODULE_ERR is returned. Otherwise the function returns
* REDISMODULE_OK and the structure pointed by 'ci' gets populated. */ * REDISMODULE_OK and the structure pointed by 'ci' gets populated. */
/* Note that we may have multiple versions of the client info structure,
* as the API evolves. */
struct moduleClientInfoV1 {
uint64_t version; /* Version of this structure for ABI compat. */
uint64_t flags; /* REDISMODULE_CLIENTINFO_FLAG_* */
uint64_t id; /* Client ID. */
char addr[46]; /* IPv4 or IPv6 address. */
uint16_t port; /* TCP port. */
uint16_t db; /* Selected DB. */
};
int modulePopulateClientInfoStructure(void *ci, client *client, int structver) { int modulePopulateClientInfoStructure(void *ci, client *client, int structver) {
if (structver != 1) return REDISMODULE_ERR; if (structver != 1) return REDISMODULE_ERR;
struct { struct moduleClientInfoV1 *ci1 = ci;
uint64_t version; /* Version of this structure for ABI compat. */
uint64_t flags; /* REDISMODULE_CLIENTINFO_FLAG_* */
uint64_t id; /* Client ID. */
char addr[46]; /* IPv4 or IPv6 address. */
uint16_t port; /* TCP port. */
uint16_t db; /* Selected DB. */
} *ci1;
ci1 = ci;
memset(ci1,0,sizeof(*ci1)); memset(ci1,0,sizeof(*ci1));
ci1->version = structver; ci1->version = structver;
if (client->flags & CLIENT_MULTI) if (client->flags & CLIENT_MULTI)
@ -5685,6 +5688,40 @@ void ModuleForkDoneHandler(int exitcode, int bysignal) {
* will be unsubscribed. If there was a previous subscription and the callback * 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. * is not null, the old callback will be replaced with the new one.
* *
* The callback must be of this type:
*
* int (*RedisModuleEventCallback)(RedisModuleCtx *ctx,
* RedisModuleEvent eid,
* uint64_t subevent,
* void *data);
*
* The 'ctx' is a normal Redis module context that the callback can use in
* order to call other modules APIs. The 'eid' is the event itself, this
* is only useful in the case the module subscribed to multiple events: using
* the 'id' field of this structure it is possible to check if the event
* is one of the events we registered with this callback. The 'subevent' field
* depends on the event that fired. Here is a list of sub events:
*
* REDISMODULE_EVENT_PERSISTENCE_RDB_START
* REDISMODULE_EVENT_PERSISTENCE_RDB_END
* REDISMODULE_EVENT_PERSISTENCE_AOF_START
* REDISMODULE_EVENT_PERSISTENCE_AOF_END
* REDISMODULE_EVENT_LOADING_START
* REDISMODULE_EVENT_LOADING_END
* REDISMODULE_EVENT_CLIENT_CHANGE_CONNECTED
* REDISMODULE_EVENT_CLIENT_CHANGE_DISCONNECTED
* REDISMODULE_EVENT_MASTER_LINK_UP
* REDISMODULE_EVENT_MASTER_LINK_DOWN
* REDISMODULE_EVENT_REPLICA_CHANGE_CONNECTED
* REDISMODULE_EVENT_REPLICA_CHANGE_DISCONNECTED
*
* Finally the 'data' pointer may be populated, only for certain events, with
* more relevant data.
*
* In the case of REDISMODULE_EVENT_CLIENT_CHANGE events it gets populated
* with a RedisModuleClientInfo structure: the module should just cast the
* void pointer to the structure and access its fields.
*
* The function returns REDISMODULE_OK if the module was successfully subscrived * 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 * for the specified event. If the API is called from a wrong context then
* REDISMODULE_ERR is returned. */ * REDISMODULE_ERR is returned. */
@ -5722,6 +5759,38 @@ int RedisModule_SubscribeToServerEvent(RedisModuleCtx *ctx, RedisModuleEvent eve
return REDISMODULE_OK; return REDISMODULE_OK;
} }
/* This is called by the Redis internals every time we want to fire an
* event that can be interceppted by some module. The pointer 'data' is useful
* in order to populate the event-specific structure when needed, in order
* to return the structure with more information to the callback.
*
* 'eid' and 'subid' are just the main event ID and the sub event associated
* with the event, depending on what exactly happened. */
void RedisModule_FireServerEvent(uint64_t eid, int subid, void *data) {
listIter li;
listNode *ln;
listRewind(RedisModule_EventListeners,&li);
while((ln = listNext(&li))) {
RedisModuleEventListener *el = ln->value;
if (el->event.id == eid) {
RedisModuleCtx ctx = REDISMODULE_CTX_INIT;
ctx.module = el->module;
ctx.client = moduleFreeContextReusedClient;
void *moduledata = NULL;
struct moduleClientInfoV1 civ1;
if (eid == REDISMODULE_EVENT_ID_CLIENT_CONNNECTED ||
eid == REDISMODULE_EVENT_ID_CLIENT_DISCONNECTED)
{
modulePopulateClientInfoStructure(&civ1,data,
el->event.dataver);
}
el->callback(&ctx,el->event,subid,moduledata);
moduleFreeContext(&ctx);
}
}
}
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
* Modules API internals * Modules API internals
* -------------------------------------------------------------------------- */ * -------------------------------------------------------------------------- */

View File

@ -180,38 +180,38 @@ typedef struct RedisModuleEvent {
RedisModuleEvent RedisModuleEvent
RedisModuleEvent_ReplicationRoleChanged = { RedisModuleEvent_ReplicationRoleChanged = {
REDISMODULE_EVENT_ID_REPLICATION_ROLE_CHANGED, REDISMODULE_EVENT_ID_REPLICATION_ROLE_CHANGED,
0 1
}, },
RedisModuleEvent_Persistence = { RedisModuleEvent_Persistence = {
REDISMODULE_EVENT_ID_PERSISTENCE, REDISMODULE_EVENT_ID_PERSISTENCE,
0 1
}, },
RedisModuleEvent_FlushDB = { RedisModuleEvent_FlushDB = {
REDISMODULE_EVENT_ID_FLUSHDB, REDISMODULE_EVENT_ID_FLUSHDB,
0 1
}, },
RedisModuleEvent_Loading = { RedisModuleEvent_Loading = {
REDISMODULE_EVENT_ID_LOADING, REDISMODULE_EVENT_ID_LOADING,
0 1
}, },
RedisModuleEvent_ClientChange = { RedisModuleEvent_ClientChange = {
REDISMODULE_EVENT_ID_CLIENT_CHANGE, REDISMODULE_EVENT_ID_CLIENT_CHANGE,
0 1
}, },
RedisModuleEvent_Shutdown = { RedisModuleEvent_Shutdown = {
REDISMODULE_EVENT_ID_SHUTDOWN, REDISMODULE_EVENT_ID_SHUTDOWN,
0 1
}, },
RedisModuleEvent_ReplicaChange = { RedisModuleEvent_ReplicaChange = {
REDISMODULE_EVENT_ID_REPLICA_CHANGE, REDISMODULE_EVENT_ID_REPLICA_CHANGE,
0 1
}, },
RedisModuleEvent_MasterLinkChange = { RedisModuleEvent_MasterLinkChange = {
REDISMODULE_EVENT_ID_MASTER_LINK_CHANGE, REDISMODULE_EVENT_ID_MASTER_LINK_CHANGE,
0 1
}; };
typedef int (*RedisModuleEventCallback)(RedisModuleEvent eid, uint64_t subevent, void *data); typedef int (*RedisModuleEventCallback)(struct RedisModuleCtx *ctx, RedisModuleEvent eid, uint64_t subevent, void *data);
/* Those are values that are used for the 'subevent' callback argument. */ /* Those are values that are used for the 'subevent' callback argument. */
#define REDISMODULE_EVENT_PERSISTENCE_RDB_START 0 #define REDISMODULE_EVENT_PERSISTENCE_RDB_START 0