summaryrefslogtreecommitdiff
path: root/src/module.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/module.c')
-rw-r--r--src/module.c326
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.
*/