diff options
Diffstat (limited to 'src/module.c')
-rw-r--r-- | src/module.c | 326 |
1 files changed, 228 insertions, 98 deletions
diff --git a/src/module.c b/src/module.c index 274210590..05bf3a275 100644 --- a/src/module.c +++ b/src/module.c @@ -27,6 +27,30 @@ * POSSIBILITY OF SUCH DAMAGE. */ +/* -------------------------------------------------------------------------- + * Modules API documentation information + * + * The comments in this file are used to generate the API documentation on the + * Redis website. + * + * Each function starting with RM_ and preceded by a block comment is included + * in the API documentation. To hide an RM_ function, put a blank line between + * the comment and the function definition or put the comment inside the + * function body. + * + * The functions are divided into sections. Each section is preceded by a + * documentation block, which is comment block starting with a markdown level 2 + * heading, i.e. a line starting with ##, on the first line of the comment block + * (with the exception of a ----- line which can appear first). Other comment + * blocks, which are not intended for the modules API user, such as this comment + * block, do NOT start with a markdown level 2 heading, so they are included in + * the generated a API documentation. + * + * The documentation comments may contain markdown formatting. Some automatic + * replacements are done, such as the replacement of RM with RedisModule in + * function names. For details, see the script src/modules/gendoc.rb. + * -------------------------------------------------------------------------- */ + #include "server.h" #include "cluster.h" #include "slowlog.h" @@ -169,6 +193,7 @@ typedef struct RedisModuleCtx RedisModuleCtx; #define REDISMODULE_CTX_THREAD_SAFE (1<<4) #define REDISMODULE_CTX_BLOCKED_DISCONNECTED (1<<5) #define REDISMODULE_CTX_MODULE_COMMAND_CALL (1<<6) +#define REDISMODULE_CTX_MULTI_EMITTED (1<<7) /* This represents a Redis key opened with RM_OpenKey(). */ struct RedisModuleKey { @@ -396,7 +421,10 @@ void RM_FreeDict(RedisModuleCtx *ctx, RedisModuleDict *d); void RM_FreeServerInfo(RedisModuleCtx *ctx, RedisModuleServerInfoData *data); /* -------------------------------------------------------------------------- - * Heap allocation raw functions + * ## Heap allocation raw functions + * + * Memory allocated with these functions are taken into account by Redis key + * eviction algorithms and are reported in Redis memory usage information. * -------------------------------------------------------------------------- */ /* Use like malloc(). Memory allocated with this function is reported in @@ -579,13 +607,13 @@ int moduleDelKeyIfEmpty(RedisModuleKey *key) { * defined in the main executable having the same names. * -------------------------------------------------------------------------- */ -/* Lookup the requested module API and store the function pointer into the - * target pointer. The function returns REDISMODULE_ERR if there is no such - * named API, otherwise REDISMODULE_OK. - * - * This function is not meant to be used by modules developer, it is only - * used implicitly by including redismodule.h. */ int RM_GetApi(const char *funcname, void **targetPtrPtr) { + /* Lookup the requested module API and store the function pointer into the + * target pointer. The function returns REDISMODULE_ERR if there is no such + * named API, otherwise REDISMODULE_OK. + * + * This function is not meant to be used by modules developer, it is only + * used implicitly by including redismodule.h. */ dictEntry *he = dictFind(server.moduleapi, funcname); if (!he) return REDISMODULE_ERR; *targetPtrPtr = dictGetVal(he); @@ -599,17 +627,21 @@ void moduleHandlePropagationAfterCommandCallback(RedisModuleCtx *ctx) { /* We don't need to do anything here if the context was never used * in order to propagate commands. */ + if (!(ctx->flags & REDISMODULE_CTX_MULTI_EMITTED)) return; + + /* We don't need to do anything here if the server isn't inside + * a transaction. */ if (!server.propagate_in_transaction) return; - /* If this command is executed from with Lua or MULTI/EXEC we do noy + /* If this command is executed from with Lua or MULTI/EXEC we do not * need to propagate EXEC */ if (server.in_eval || server.in_exec) return; /* Handle the replication of the final EXEC, since whatever a command * emits is always wrapped around MULTI/EXEC. */ - beforePropagateMultiOrExec(0); alsoPropagate(server.execCommand,c->db->id,&shared.exec,1, PROPAGATE_AOF|PROPAGATE_REPL); + afterPropagateExec(); /* If this is not a module command context (but is instead a simple * callback context), we have to handle directly the "also propagate" @@ -708,6 +740,14 @@ int moduleGetCommandKeysViaAPI(struct redisCommand *cmd, robj **argv, int argc, return result->numkeys; } +/* -------------------------------------------------------------------------- + * ## Commands API + * + * These functions are used to implement custom Redis commands. + * + * For examples, see https://redis.io/topics/modules-intro. + * -------------------------------------------------------------------------- */ + /* Return non-zero if a module command, that was declared with the * flag "getkeys-api", is called in a special way to get the keys positions * and not to get executed. Otherwise zero is returned. */ @@ -882,11 +922,15 @@ int RM_CreateCommand(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc c return REDISMODULE_OK; } -/* Called by RM_Init() to setup the `ctx->module` structure. - * - * This is an internal function, Redis modules developers don't need - * to use it. */ +/* -------------------------------------------------------------------------- + * ## Module information and time measurement + * -------------------------------------------------------------------------- */ + void RM_SetModuleAttribs(RedisModuleCtx *ctx, const char *name, int ver, int apiver) { + /* Called by RM_Init() to setup the `ctx->module` structure. + * + * This is an internal function, Redis modules developers don't need + * to use it. */ RedisModule *module; if (ctx->module != NULL) return; @@ -952,20 +996,29 @@ int RM_BlockedClientMeasureTimeEnd(RedisModuleBlockedClient *bc) { * repl-diskless-load to work if enabled. * The module should use RedisModule_IsIOError after reads, before using the * data that was read, and in case of error, propagate it upwards, and also be - * able to release the partially populated value and all it's allocations. */ + * able to release the partially populated value and all it's allocations. + * + * REDISMODULE_OPTION_NO_IMPLICIT_SIGNAL_MODIFIED: + * See RM_SignalModifiedKey(). + */ 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 - * and client side caching). */ + * and client side caching). + * + * This is done automatically when a key opened for writing is closed, unless + * the option REDISMODULE_OPTION_NO_IMPLICIT_SIGNAL_MODIFIED has been set using + * RM_SetModuleOptions(). +*/ int RM_SignalModifiedKey(RedisModuleCtx *ctx, RedisModuleString *keyname) { signalModifiedKey(ctx->client,ctx->client->db,keyname); return REDISMODULE_OK; } /* -------------------------------------------------------------------------- - * Automatic memory management for modules + * ## Automatic memory management for modules * -------------------------------------------------------------------------- */ /* Enable automatic memory management. @@ -1061,7 +1114,7 @@ void autoMemoryCollect(RedisModuleCtx *ctx) { } /* -------------------------------------------------------------------------- - * String objects APIs + * ## String objects APIs * -------------------------------------------------------------------------- */ /* Create a new module string object. The returned string must be freed @@ -1330,14 +1383,6 @@ int RM_StringToLongDouble(const RedisModuleString *str, long double *ld) { * Returns REDISMODULE_OK on success and returns REDISMODULE_ERR if the string * is not a valid string representation of a stream ID. The special IDs "+" and * "-" are allowed. - * - * RedisModuleStreamID is a struct with two 64-bit fields, which is used in - * stream functions and defined as - * - * typedef struct RedisModuleStreamID { - * uint64_t ms; - * uint64_t seq; - * } RedisModuleStreamID; */ int RM_StringToStreamID(const RedisModuleString *str, RedisModuleStreamID *id) { streamID streamid; @@ -1392,13 +1437,15 @@ int RM_StringAppendBuffer(RedisModuleCtx *ctx, RedisModuleString *str, const cha } /* -------------------------------------------------------------------------- - * Reply APIs + * ## Reply APIs + * + * These functions are used for sending replies to the client. * * Most functions always return REDISMODULE_OK so you can use it with * 'return' in order to return from the command implementation with: * * if (... some condition ...) - * return RM_ReplyWithLongLong(ctx,mycount); + * return RedisModule_ReplyWithLongLong(ctx,mycount); * -------------------------------------------------------------------------- */ /* Send an error about the number of arguments given to the command, @@ -1687,7 +1734,7 @@ int RM_ReplyWithLongDouble(RedisModuleCtx *ctx, long double ld) { } /* -------------------------------------------------------------------------- - * Commands replication API + * ## Commands replication API * -------------------------------------------------------------------------- */ /* Helper function to replicate MULTI the first time we replicate something @@ -1696,7 +1743,7 @@ int RM_ReplyWithLongDouble(RedisModuleCtx *ctx, long double ld) { void moduleReplicateMultiIfNeeded(RedisModuleCtx *ctx) { /* Skip this if client explicitly wrap the command with MULTI, or if * the module command was called by a script. */ - if (server.lua_caller || server.in_exec) return; + if (server.in_eval || server.in_exec) return; /* If we already emitted MULTI return ASAP. */ if (server.propagate_in_transaction) return; /* If this is a thread safe context, we do not want to wrap commands @@ -1707,10 +1754,12 @@ void moduleReplicateMultiIfNeeded(RedisModuleCtx *ctx) { * context, we have to setup the op array for the "also propagate" API * so that RM_Replicate() will work. */ if (!(ctx->flags & REDISMODULE_CTX_MODULE_COMMAND_CALL)) { + serverAssert(ctx->saved_oparray.ops == NULL); ctx->saved_oparray = server.also_propagate; redisOpArrayInit(&server.also_propagate); } execCommandPropagateMulti(ctx->client->db->id); + ctx->flags |= REDISMODULE_CTX_MULTI_EMITTED; } /* Replicate the specified command and arguments to slaves and AOF, as effect @@ -1734,7 +1783,7 @@ void moduleReplicateMultiIfNeeded(RedisModuleCtx *ctx) { * the AOF or the replicas from the propagation of the specified command. * Otherwise, by default, the command will be propagated in both channels. * - * ## Note about calling this function from a thread safe context: + * #### Note about calling this function from a thread safe context: * * Normally when you call this function from the callback implementing a * module command, or any other callback provided by the Redis Module API, @@ -1746,7 +1795,7 @@ void moduleReplicateMultiIfNeeded(RedisModuleCtx *ctx) { * and the command specified is inserted in the AOF and replication stream * immediately. * - * ## Return value + * #### Return value * * The command returns REDISMODULE_ERR if the format specifiers are invalid * or the command name does not belong to a known command. */ @@ -1810,7 +1859,7 @@ int RM_ReplicateVerbatim(RedisModuleCtx *ctx) { } /* -------------------------------------------------------------------------- - * DB and Key APIs -- Generic API + * ## DB and Key APIs -- Generic API * -------------------------------------------------------------------------- */ /* Return the ID of the current client calling the currently active module @@ -2077,7 +2126,7 @@ int RM_GetContextFlags(RedisModuleCtx *ctx) { flags |= REDISMODULE_CTX_FLAGS_LOADING; /* Maxmemory and eviction policy */ - if (server.maxmemory > 0) { + if (server.maxmemory > 0 && (!server.masterhost || !server.repl_slave_ignore_maxmemory)) { flags |= REDISMODULE_CTX_FLAGS_MAXMEMORY; if (server.maxmemory_policy != MAXMEMORY_NO_EVICTION) @@ -2327,7 +2376,7 @@ mstime_t RM_GetExpire(RedisModuleKey *key) { * The function returns REDISMODULE_OK on success or REDISMODULE_ERR if * the key was not open for writing or is an empty key. */ int RM_SetExpire(RedisModuleKey *key, mstime_t expire) { - if (!(key->mode & REDISMODULE_WRITE) || key->value == NULL) + if (!(key->mode & REDISMODULE_WRITE) || key->value == NULL || (expire < 0 && expire != REDISMODULE_NO_EXPIRE)) return REDISMODULE_ERR; if (expire != REDISMODULE_NO_EXPIRE) { expire += mstime(); @@ -2338,6 +2387,36 @@ int RM_SetExpire(RedisModuleKey *key, mstime_t expire) { return REDISMODULE_OK; } +/* Return the key expire value, as absolute Unix timestamp. + * If no TTL is associated with the key or if the key is empty, + * REDISMODULE_NO_EXPIRE is returned. */ +mstime_t RM_GetAbsExpire(RedisModuleKey *key) { + mstime_t expire = getExpire(key->db,key->key); + if (expire == -1 || key->value == NULL) + return REDISMODULE_NO_EXPIRE; + return expire; +} + +/* Set a new expire for the key. If the special expire + * REDISMODULE_NO_EXPIRE is set, the expire is cancelled if there was + * one (the same as the PERSIST command). + * + * Note that the expire must be provided as a positive integer representing + * the absolute Unix timestamp the key should have. + * + * The function returns REDISMODULE_OK on success or REDISMODULE_ERR if + * the key was not open for writing or is an empty key. */ +int RM_SetAbsExpire(RedisModuleKey *key, mstime_t expire) { + if (!(key->mode & REDISMODULE_WRITE) || key->value == NULL || (expire < 0 && expire != REDISMODULE_NO_EXPIRE)) + return REDISMODULE_ERR; + if (expire != REDISMODULE_NO_EXPIRE) { + setExpire(key->ctx->client,key->db,key->key,expire); + } else { + removeExpire(key->db,key->key); + } + return REDISMODULE_OK; +} + /* Performs similar operation to FLUSHALL, and optionally start a new AOF file (if enabled) * If restart_aof is true, you must make sure the command that triggered this call is not * propagated to the AOF file. @@ -2361,7 +2440,9 @@ RedisModuleString *RM_RandomKey(RedisModuleCtx *ctx) { } /* -------------------------------------------------------------------------- - * Key API for String type + * ## Key API for String type + * + * See also RM_ValueLength(), which returns the length of a string. * -------------------------------------------------------------------------- */ /* If the key is open for writing, set the specified string 'str' as the @@ -2471,7 +2552,9 @@ int RM_StringTruncate(RedisModuleKey *key, size_t newlen) { } /* -------------------------------------------------------------------------- - * Key API for List type + * ## Key API for List type + * + * See also RM_ValueLength(), which returns the length of a list. * -------------------------------------------------------------------------- */ /* Push an element into a list, on head or tail depending on 'where' argument. @@ -2509,26 +2592,28 @@ RedisModuleString *RM_ListPop(RedisModuleKey *key, int where) { } /* -------------------------------------------------------------------------- - * Key API for Sorted Set type + * ## Key API for Sorted Set type + * + * See also RM_ValueLength(), which returns the length of a sorted set. * -------------------------------------------------------------------------- */ /* Conversion from/to public flags of the Modules API and our private flags, * so that we have everything decoupled. */ int moduleZsetAddFlagsToCoreFlags(int flags) { int retflags = 0; - if (flags & REDISMODULE_ZADD_XX) retflags |= ZADD_XX; - if (flags & REDISMODULE_ZADD_NX) retflags |= ZADD_NX; - if (flags & REDISMODULE_ZADD_GT) retflags |= ZADD_GT; - if (flags & REDISMODULE_ZADD_LT) retflags |= ZADD_LT; + if (flags & REDISMODULE_ZADD_XX) retflags |= ZADD_IN_XX; + if (flags & REDISMODULE_ZADD_NX) retflags |= ZADD_IN_NX; + if (flags & REDISMODULE_ZADD_GT) retflags |= ZADD_IN_GT; + if (flags & REDISMODULE_ZADD_LT) retflags |= ZADD_IN_LT; return retflags; } /* See previous function comment. */ int moduleZsetAddFlagsFromCoreFlags(int flags) { int retflags = 0; - if (flags & ZADD_ADDED) retflags |= REDISMODULE_ZADD_ADDED; - if (flags & ZADD_UPDATED) retflags |= REDISMODULE_ZADD_UPDATED; - if (flags & ZADD_NOP) retflags |= REDISMODULE_ZADD_NOP; + if (flags & ZADD_OUT_ADDED) retflags |= REDISMODULE_ZADD_ADDED; + if (flags & ZADD_OUT_UPDATED) retflags |= REDISMODULE_ZADD_UPDATED; + if (flags & ZADD_OUT_NOP) retflags |= REDISMODULE_ZADD_NOP; return retflags; } @@ -2565,16 +2650,16 @@ int moduleZsetAddFlagsFromCoreFlags(int flags) { * * 'score' double value is not a number (NaN). */ int RM_ZsetAdd(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr) { - int flags = 0; + int in_flags = 0, out_flags = 0; if (!(key->mode & REDISMODULE_WRITE)) return REDISMODULE_ERR; if (key->value && key->value->type != OBJ_ZSET) return REDISMODULE_ERR; if (key->value == NULL) moduleCreateEmptyKey(key,REDISMODULE_KEYTYPE_ZSET); - if (flagsptr) flags = moduleZsetAddFlagsToCoreFlags(*flagsptr); - if (zsetAdd(key->value,score,ele->ptr,&flags,NULL) == 0) { + if (flagsptr) in_flags = moduleZsetAddFlagsToCoreFlags(*flagsptr); + if (zsetAdd(key->value,score,ele->ptr,in_flags,&out_flags,NULL) == 0) { if (flagsptr) *flagsptr = 0; return REDISMODULE_ERR; } - if (flagsptr) *flagsptr = moduleZsetAddFlagsFromCoreFlags(flags); + if (flagsptr) *flagsptr = moduleZsetAddFlagsFromCoreFlags(out_flags); return REDISMODULE_OK; } @@ -2592,22 +2677,17 @@ int RM_ZsetAdd(RedisModuleKey *key, double score, RedisModuleString *ele, int *f * with the new score of the element after the increment, if no error * is returned. */ int RM_ZsetIncrby(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr, double *newscore) { - int flags = 0; + int in_flags = 0, out_flags = 0; if (!(key->mode & REDISMODULE_WRITE)) return REDISMODULE_ERR; if (key->value && key->value->type != OBJ_ZSET) return REDISMODULE_ERR; if (key->value == NULL) moduleCreateEmptyKey(key,REDISMODULE_KEYTYPE_ZSET); - if (flagsptr) flags = moduleZsetAddFlagsToCoreFlags(*flagsptr); - flags |= ZADD_INCR; - if (zsetAdd(key->value,score,ele->ptr,&flags,newscore) == 0) { + if (flagsptr) in_flags = moduleZsetAddFlagsToCoreFlags(*flagsptr); + in_flags |= ZADD_IN_INCR; + if (zsetAdd(key->value,score,ele->ptr,in_flags,&out_flags,newscore) == 0) { if (flagsptr) *flagsptr = 0; return REDISMODULE_ERR; } - /* zsetAdd() may signal back that the resulting score is not a number. */ - if (flagsptr && (*flagsptr & ZADD_NAN)) { - *flagsptr = 0; - return REDISMODULE_ERR; - } - if (flagsptr) *flagsptr = moduleZsetAddFlagsFromCoreFlags(flags); + if (flagsptr) *flagsptr = moduleZsetAddFlagsFromCoreFlags(out_flags); return REDISMODULE_OK; } @@ -2657,7 +2737,7 @@ int RM_ZsetScore(RedisModuleKey *key, RedisModuleString *ele, double *score) { } /* -------------------------------------------------------------------------- - * Key API for Sorted Set iterator + * ## Key API for Sorted Set iterator * -------------------------------------------------------------------------- */ void zsetKeyReset(RedisModuleKey *key) { @@ -2964,7 +3044,9 @@ int RM_ZsetRangePrev(RedisModuleKey *key) { } /* -------------------------------------------------------------------------- - * Key API for Hash type + * ## Key API for Hash type + * + * See also RM_ValueLength(), which returns the number of fields in a hash. * -------------------------------------------------------------------------- */ /* Set the field of the specified hash field to the specified value. @@ -3199,7 +3281,20 @@ int RM_HashGet(RedisModuleKey *key, int flags, ...) { } /* -------------------------------------------------------------------------- - * Key API for the stream type. + * ## Key API for Stream type + * + * For an introduction to streams, see https://redis.io/topics/streams-intro. + * + * The type RedisModuleStreamID, which is used in stream functions, is a struct + * with two 64-bit fields and is defined as + * + * typedef struct RedisModuleStreamID { + * uint64_t ms; + * uint64_t seq; + * } RedisModuleStreamID; + * + * See also RM_ValueLength(), which returns the length of a stream, and the + * conversion functions RM_StringToStreamID() and RM_CreateStringFromStreamID(). * -------------------------------------------------------------------------- */ /* Adds an entry to a stream. Like XADD without trimming. @@ -3366,8 +3461,8 @@ int RM_StreamDelete(RedisModuleKey *key, RedisModuleStreamID *id) { * // * // ... Do stuff ... * // - * RedisModule_Free(field); - * RedisModule_Free(value); + * RedisModule_FreeString(ctx, field); + * RedisModule_FreeString(ctx, value); * } * } * RedisModule_StreamIteratorStop(key); @@ -3648,7 +3743,9 @@ long long RM_StreamTrimByID(RedisModuleKey *key, int flags, RedisModuleStreamID } /* -------------------------------------------------------------------------- - * Redis <-> Modules generic Call() API + * ## Calling Redis commands from modules + * + * RM_Call() sends a command to Redis. The remaining functions handle the reply. * -------------------------------------------------------------------------- */ /* Create a new RedisModuleCallReply object. The processing of the reply @@ -4067,20 +4164,30 @@ RedisModuleCallReply *RM_Call(RedisModuleCtx *ctx, const char *cmdname, const ch } } - /* If we are using single commands replication, we need to wrap what - * we propagate into a MULTI/EXEC block, so that it will be atomic like - * a Lua script in the context of AOF and slaves. */ - if (replicate) moduleReplicateMultiIfNeeded(ctx); + /* We need to use a global replication_allowed flag in order to prevent + * replication of nested RM_Calls. Example: + * 1. module1.foo does RM_Call of module2.bar without replication (i.e. no '!') + * 2. module2.bar internally calls RM_Call of INCR with '!' + * 3. at the end of module1.foo we call RM_ReplicateVerbatim + * We want the replica/AOF to see only module1.foo and not the INCR from module2.bar */ + int prev_replication_allowed = server.replication_allowed; + server.replication_allowed = replicate && server.replication_allowed; /* Run the command */ int call_flags = CMD_CALL_SLOWLOG | CMD_CALL_STATS | CMD_CALL_NOWRAP; if (replicate) { + /* If we are using single commands replication, we need to wrap what + * we propagate into a MULTI/EXEC block, so that it will be atomic like + * a Lua script in the context of AOF and slaves. */ + moduleReplicateMultiIfNeeded(ctx); + if (!(flags & REDISMODULE_ARGV_NO_AOF)) call_flags |= CMD_CALL_PROPAGATE_AOF; if (!(flags & REDISMODULE_ARGV_NO_REPLICAS)) call_flags |= CMD_CALL_PROPAGATE_REPL; } call(c,call_flags); + server.replication_allowed = prev_replication_allowed; serverAssert((c->flags & CLIENT_BLOCKED) == 0); @@ -4121,7 +4228,7 @@ const char *RM_CallReplyProto(RedisModuleCallReply *reply, size_t *len) { } /* -------------------------------------------------------------------------- - * Modules data types + * ## Modules data types * * When String DMA or using existing data structures is not enough, it is * possible to create new data types from scratch and export them to @@ -4264,6 +4371,12 @@ void moduleTypeNameByID(char *name, uint64_t moduleid) { } } +/* Return the name of the module that owns the specified moduleType. */ +const char *moduleTypeModuleName(moduleType *mt) { + if (!mt || !mt->module) return NULL; + return mt->module->name; +} + /* Create a copy of a module type value using the copy callback. If failed * or not supported, produce an error reply and return NULL. */ @@ -4479,7 +4592,7 @@ void *RM_ModuleTypeGetValue(RedisModuleKey *key) { } /* -------------------------------------------------------------------------- - * RDB loading and saving functions + * ## RDB loading and saving functions * -------------------------------------------------------------------------- */ /* Called when there is a load error in the context of a module. On some @@ -4791,7 +4904,7 @@ ssize_t rdbSaveModulesAux(rio *rdb, int when) { } /* -------------------------------------------------------------------------- - * Key digest API (DEBUG DIGEST interface for modules types) + * ## Key digest API (DEBUG DIGEST interface for modules types) * -------------------------------------------------------------------------- */ /* Add a new element to the digest. This function can be called multiple times @@ -4912,7 +5025,7 @@ RedisModuleString *RM_SaveDataTypeToString(RedisModuleCtx *ctx, void *data, cons } /* -------------------------------------------------------------------------- - * AOF API for modules data types + * ## AOF API for modules data types * -------------------------------------------------------------------------- */ /* Emits a command into the AOF during the AOF rewriting process. This function @@ -4967,7 +5080,7 @@ void RM_EmitAOF(RedisModuleIO *io, const char *cmdname, const char *fmt, ...) { } /* -------------------------------------------------------------------------- - * IO context handling + * ## IO context handling * -------------------------------------------------------------------------- */ RedisModuleCtx *RM_GetContextFromIO(RedisModuleIO *io) { @@ -4994,7 +5107,7 @@ const RedisModuleString *RM_GetKeyNameFromModuleKey(RedisModuleKey *key) { } /* -------------------------------------------------------------------------- - * Logging + * ## Logging * -------------------------------------------------------------------------- */ /* This is the low level function implementing both: @@ -5025,10 +5138,10 @@ void moduleLogRaw(RedisModule *module, const char *levelstr, const char *fmt, va * printf-alike specifiers, while level is a string describing the log * level to use when emitting the log, and must be one of the following: * - * * "debug" - * * "verbose" - * * "notice" - * * "warning" + * * "debug" (`REDISMODULE_LOGLEVEL_DEBUG`) + * * "verbose" (`REDISMODULE_LOGLEVEL_VERBOSE`) + * * "notice" (`REDISMODULE_LOGLEVEL_NOTICE`) + * * "warning" (`REDISMODULE_LOGLEVEL_WARNING`) * * If the specified log level is invalid, verbose is used by default. * There is a fixed limit to the length of the log line this function is able @@ -5079,7 +5192,10 @@ void RM_LatencyAddSample(const char *event, mstime_t latency) { } /* -------------------------------------------------------------------------- - * Blocking clients from modules + * ## Blocking clients from modules + * + * For a guide about blocking commands in modules, see + * https://redis.io/topics/modules-blocking-ops. * -------------------------------------------------------------------------- */ /* Readable handler for the awake pipe. We do nothing here, the awake bytes @@ -5140,11 +5256,6 @@ void unblockClientFromModule(client *c) { moduleUnblockClient(c); bc->client = NULL; - /* Reset the client for a new query since, for blocking commands implemented - * into modules, we do not it immediately after the command returns (and - * the client blocks) in order to be still able to access the argument - * vector from callbacks. */ - resetClient(c); } /* Block a client in the context of a module: this function implements both @@ -5544,6 +5655,12 @@ void moduleHandleBlockedClients(void) { * API to unblock the client and the memory will be released. */ void moduleBlockedClientTimedOut(client *c) { RedisModuleBlockedClient *bc = c->bpop.module_blocked_handle; + + /* Protect against re-processing: don't serve clients that are already + * in the unblocking list for any reason (including RM_UnblockClient() + * explicit call). See #6798. */ + if (bc->unblocked) return; + RedisModuleCtx ctx = REDISMODULE_CTX_INIT; ctx.flags |= REDISMODULE_CTX_BLOCKED_TIMEOUT; ctx.module = bc->module; @@ -5600,7 +5717,7 @@ int RM_BlockedClientDisconnected(RedisModuleCtx *ctx) { } /* -------------------------------------------------------------------------- - * Thread Safe Contexts + * ## Thread Safe Contexts * -------------------------------------------------------------------------- */ /* Return a context which can be used inside threads to make Redis context @@ -5710,7 +5827,7 @@ void moduleReleaseGIL(void) { /* -------------------------------------------------------------------------- - * Module Keyspace Notifications API + * ## Module Keyspace Notifications API * -------------------------------------------------------------------------- */ /* Subscribe to keyspace notifications. This is a low-level version of the @@ -5734,6 +5851,7 @@ void moduleReleaseGIL(void) { * - REDISMODULE_NOTIFY_EXPIRED: Expiration events * - REDISMODULE_NOTIFY_EVICTED: Eviction events * - REDISMODULE_NOTIFY_STREAM: Stream events + * - REDISMODULE_NOTIFY_MODULE: Module types events * - REDISMODULE_NOTIFY_KEYMISS: Key-miss events * - REDISMODULE_NOTIFY_ALL: All events (Excluding REDISMODULE_NOTIFY_KEYMISS) * - REDISMODULE_NOTIFY_LOADED: A special notification available only for modules, @@ -5843,7 +5961,7 @@ void moduleUnsubscribeNotifications(RedisModule *module) { } /* -------------------------------------------------------------------------- - * Modules Cluster API + * ## Modules Cluster API * -------------------------------------------------------------------------- */ /* The Cluster message callback function pointer type. */ @@ -6098,7 +6216,7 @@ void RM_SetClusterFlags(RedisModuleCtx *ctx, uint64_t flags) { } /* -------------------------------------------------------------------------- - * Modules Timers API + * ## Modules Timers API * * Module timers are an high precision "green timers" abstraction where * every module can register even millions of timers without problems, even if @@ -6272,7 +6390,7 @@ int RM_GetTimerInfo(RedisModuleCtx *ctx, RedisModuleTimerID id, uint64_t *remain } /* -------------------------------------------------------------------------- - * Modules ACL API + * ## Modules ACL API * * Implements a hook into the authentication and authorization within Redis. * --------------------------------------------------------------------------*/ @@ -6502,7 +6620,7 @@ RedisModuleString *RM_GetClientCertificate(RedisModuleCtx *ctx, uint64_t client_ } /* -------------------------------------------------------------------------- - * Modules Dictionary API + * ## Modules Dictionary API * * Implements a sorted dictionary (actually backed by a radix tree) with * the usual get / set / del / num-items API, together with an iterator @@ -6756,7 +6874,7 @@ int RM_DictCompare(RedisModuleDictIter *di, const char *op, RedisModuleString *k /* -------------------------------------------------------------------------- - * Modules Info fields + * ## Modules Info fields * -------------------------------------------------------------------------- */ int RM_InfoEndDictField(RedisModuleInfoCtx *ctx); @@ -7070,7 +7188,7 @@ double RM_ServerInfoGetFieldDouble(RedisModuleServerInfoData *data, const char* } /* -------------------------------------------------------------------------- - * Modules utility APIs + * ## Modules utility APIs * -------------------------------------------------------------------------- */ /* Return random bytes using SHA1 in counter mode with a /dev/urandom @@ -7089,7 +7207,7 @@ void RM_GetRandomHexChars(char *dst, size_t len) { } /* -------------------------------------------------------------------------- - * Modules API exporting / importing + * ## Modules API exporting / importing * -------------------------------------------------------------------------- */ /* This function is called by a module in order to export some API with a @@ -7226,7 +7344,7 @@ int moduleUnregisterFilters(RedisModule *module) { } /* -------------------------------------------------------------------------- - * Module Command Filter API + * ## Module Command Filter API * -------------------------------------------------------------------------- */ /* Register a new command filter function. @@ -7436,7 +7554,7 @@ float RM_GetUsedMemoryRatio(){ } /* -------------------------------------------------------------------------- - * Scanning keyspace and hashes + * ## Scanning keyspace and hashes * -------------------------------------------------------------------------- */ typedef void (*RedisModuleScanCB)(RedisModuleCtx *ctx, RedisModuleString *keyname, RedisModuleKey *key, void *privdata); @@ -7707,7 +7825,7 @@ int RM_ScanKey(RedisModuleKey *key, RedisModuleScanCursor *cursor, RedisModuleSc /* -------------------------------------------------------------------------- - * Module fork API + * ## Module fork API * -------------------------------------------------------------------------- */ /* Create a background child process with the current frozen snaphost of the @@ -7767,7 +7885,7 @@ int TerminateModuleForkChild(int child_pid, int wait) { serverLog(LL_VERBOSE,"Killing running module fork child: %ld", (long) server.child_pid); if (kill(server.child_pid,SIGUSR1) != -1 && wait) { - while(wait4(server.child_pid,&statloc,0,NULL) != + while(waitpid(server.child_pid, &statloc, 0) != server.child_pid); } /* Reset the buffer accumulating changes while the child saves. */ @@ -7801,7 +7919,7 @@ void ModuleForkDoneHandler(int exitcode, int bysignal) { } /* -------------------------------------------------------------------------- - * Server hooks implementation + * ## Server hooks implementation * -------------------------------------------------------------------------- */ /* Register to be notified, via a callback, when the specified server event @@ -8682,6 +8800,10 @@ size_t moduleCount(void) { return dictSize(modules); } +/* -------------------------------------------------------------------------- + * ## Key eviction API + * -------------------------------------------------------------------------- */ + /* Set the key last access time for LRU based eviction. not relevant if the * servers's maxmemory policy is LFU based. Value is idle time in milliseconds. * returns REDISMODULE_OK if the LRU was updated, REDISMODULE_ERR otherwise. */ @@ -8732,6 +8854,10 @@ int RM_GetLFU(RedisModuleKey *key, long long *lfu_freq) { return REDISMODULE_OK; } +/* -------------------------------------------------------------------------- + * ## Miscellaneous APIs + * -------------------------------------------------------------------------- */ + /** * Returns the full ContextFlags mask, using the return value * the module can check if a certain set of flags are supported @@ -8880,6 +9006,10 @@ int *RM_GetCommandKeys(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, return res; } +/* -------------------------------------------------------------------------- + * ## Defrag API + * -------------------------------------------------------------------------- */ + /* The defrag context, used to manage state during calls to the data type * defrag callback. */ |