Redis Modules APIs 介绍。


源码地址

1. 内存管理

虽然在模块中可以调用 malloc()free() 函数,但是通过 malloc() 分配的内存并不会被计算到 used_memory 信息中去,也不会受到 maxmemory 限制,所以应首先考虑使用 Redis Modules 提供的 APIs。而且,使用 APIs 分配内存实现的自定义数据结构也可以被 RDB 加载函数正确地反序列化。

在函数返回时就需释放的短期对象可使用内存池模式进行分配。内存对齐原则为:

  • 若申请的内存大于 word size,则按 word size 对齐
  • 若申请的内存小于 word size,则按 2 的幂字节对齐
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/* Use like malloc() */
void *RedisModule_Alloc(size_t bytes);

/* Use like calloc() */
void *RedisModule_Calloc(size_t nmemb, size_t size);

/* Use like realloc() for memory obtained with RedisModule_Alloc() */
void *RedisModule_Realloc(void *ptr, size_t bytes);

/* Use like free() for memory obtained by RedisModule_Alloc()
 * and RedisModule_Realloc() */
void RedisModule_Free(void *ptr);

/* Use like strdup() but returns memory allocated with RedisModule_Alloc(). */
char *RedisModule_Strdup(const char *str);

/* Return heap allocated memory that will be freed automatically when the
 * module callback function returns */
void *RedisModule_PoolAlloc(RedisModuleCtx *ctx, size_t bytes);

/* Enable automatic memory management.
 *
 * The function must be called as the first function of a command implementation
 * that wants to use automatic memory. */
void RedisModule_AutoMemory(RedisModuleCtx *ctx);

/* For a given pointer allocated via RedisModule_Alloc() or
 * RedisModule_Realloc(), return the amount of memory allocated for it.
 * Note that this may be different (larger) than the memory we allocated
 * with the allocation calls, since sometimes the underlying allocator
 * will allocate more memory. */
size_t RedisModule_MallocSize(void* ptr);

/* Return the a number between 0 to 1 indicating the amount of memory
 * currently used, relative to the Redis "maxmemory" configuration.
 *
 * 0 - No memory limit configured.
 * Between 0 and 1 - The percentage of the memory used normalized in 0-1 range.
 * Exactly 1 - Memory limit reached.
 * Greater 1 - More memory used than the configured limit. */
float RedisModule_GetUsedMemoryRatio();

2. Service API exported to modules

2.1. 注册命令

1
2
3
4
5
6
7
8
/* Register a new command in the Redis server. The function returns REDISMODULE_ERR
 * if the specified command name is already busy or a set of invalid flags were
 * passed, otherwise REDISMODULE_OK is returned and the new command is registered. */
int RedisModule_CreateCommand(
    RedisModuleCtx *ctx,
    const char *name, RedisModuleCmdFunc cmdfunc,
    const char *strflags,
    int firstkey, int lastkey, int keystep);

该函数必须在 RedisModule_OnLoad() 中调用,命令的执行由传入的 cmdfunc 完成,其 prototype 如下所示:

1
2
3
/* The command function type is the following,
 * and is supposed to always return REDIMODULE_OK. */
int MyCmd_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);

strflags 确定了该命令的行为模式,其值的列表如下,如要传递多个 flags,则以空格隔开.

  • write: The command may modify the data set(it may also read from it)
  • readonly: The command returns data from keys but never writes
  • admin: The command is an administrative command (may change replication or perform similar tasks).
  • deny-oom: The command may use additional memory and should be denied during out of memory conditions.
  • deny-script: Don’t allow this command in Lua scripts.
  • allow-loading: Allow this command while the server is loading data. Only commands not interacting with the data set should be allowed to run in this mode. If not sure don’t use this flag.
  • pubsub: The command publishes things on Pub/Sub channels.
  • random: The command may have different outputs even starting from the same input arguments and key values.
  • allow-stale: The command is allowed to run on slaves that don’t serve stale data. Don’t use if you don’t know what this means.
  • no-monitor: Don’t propagate the command on monitor. Use this if the command has sensible data among the arguments.
  • no-slowlog: Don’t log this command in the slowlog. Use this if the command has sensible data among the arguments.
  • fast: The command time complexity is not greater than O(log(N)) where N is the size of the collection or anything else representing the normal scalability issue with the command.
  • getkeys-api: The command implements the interface to return the arguments that are keys. Used when start/stop/step is not enough because of the command syntax.
  • no-cluster: The command should not register in Redis Cluster since is not designed to work with it because, for example, is unable to report the position of the keys, programmatically creates key names, or any other reason.
  • no-auth: This command can be run by an un-authenticated client. Normally this is used by a command that is used to authenticate a client.

firstkey 表明在执行该注册的命令之时,需要读取的首个 key,其值为 0 则视为无需读取 key,lastkey 亦是如此。

2.2. 其他

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/* 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 RedisModule_GetApi(const char *funcname, void **targetPtrPtr);

/* 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
 * position and not to get executed. Otherwise zero is returned. */
int RedisModule_IsKeysPositionRequest(RedisModuleCtx *ctx);

/* Uses this function to report keys.
 * example:
 *
 *     if (RedisModule_IsKeysPositionRequest(ctx)) {
 *         RedisModule_KeyAtPos(ctx,1);
 *         RedisModule_KeyAtPos(ctx,2);
 *     }
 * */
void RedisModule_KeyAtPos(RedisModuleCtx *ctx, int pos);

/* Called by RM_Init() to setup the ctx->module structure */
void RedisModule_SetModuleAttribs(RedisModuleCtx *ctx,
                                  const char *name, int ver, int apiver);

/* Return non-zero if the module name is busy. Otherwise zero is returned. */
int RedisModule_IsModuleNameBusy(const char *name);

/* Return the current UNIX time in milliseconds. */
long long RedisModule_Milliseconds(void);

/* Set flags defining capabilities or behavior bit flags.
 *
 * i.e. REDISMODULE_OPTIONS_HANDLE_IO_ERRORS:
 * Generally, modules don't need to bother with this, as the process will just
 * terminate if a read error happens, however, setting this flag would allow
 * 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. */
void RedisModule_SetModuleOptions(RedisModuleCtx *ctx, int options);

/* Signals that the key is modified from user's perspective
 * i.e. invalidate WATCH and client side caching. */
int RedisModule_SignalModifiedKey(RedisModuleCtx *ctx, RedisModuleString *key);

3. RedisModuleString objects APIs

3.1. Create

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/* Create a new module string object by copying the len bytes starting ar ptr.
 * No reference is retained to the passed buffer. */
RedisModuleString *RedisModule_CreateString(RedisModuleCtx *ctx,
                                            const char *ptr, size_t len);

/* Create a new module string object from a printf format and arguments.
 * The string is created using the sds formatter function sdscatvprintf(). */
RedisModuleString *RedisModule_CreateStringPrintf(RedisModuleCtx *ctx,
                                                  const char *fmt, ...);

/* Create a new module string from a long long integer */
RedisModuleString *RedisModule_CreateStringFromLongLong(RedisModuleCtx *ctx
                                                        long long ll);

/* Create a new module string from a double */
RedisModuleString *RedisModule_CreateStringFromDouble(RedisModuleCtx *ctx,
                                                      double d);

/* Create a new module string from a long double */
RedisModuleString *RedisModule_CreateStringFromLongDouble(RedisModuleCtx *ctx,
                   long double ld, int humanfriendly);

/* Create a new module string from another RedisModuleString */
RedisModuleString *RedisModule_CreateStringFromString(RedisModuleCtx *ctx,
                   const RedisModuleString *str);
  • 在未启用自动内存管理功能时,上述接口所返回的对象必须调用 RedisModule_FreeString() 进行释放
  • 当你想在上下文范围之外创建一个 RedisModuleString 对象时,ctx 可设置为 NULL,此时,自动内存管理功能不可用,你必须手动进行释放操作

3.2. Free

1
2
3
/* Free a module string object obtained with one of the
 * Redis Module API calls that return new string objects. */
void RedisModule_FreeString(RedisModuleCtx *ctx, RedisModuleString *str);
  • 在启用自动内存管理的情况下,我们仍然可以调用该函数,会立即释放传入的 RedisModuleString,并从字符串待释放列表中移除该对象
  • 如果在创建 RedisModuleString 时上下文为 NULL,则在释放时上下文也可为 NULL,不过此时上下文不为 NULL 也没啥问题。但如果创建时上下文不为空,则在释放时也不可为空。

3.3. Retain

1
void RedisModule_RetainString(RedisModuleCtx *ctx, RedisModuleString *str);
  • 通常而言,只有在以下条件都满足时,才会调用这个函数:

    • You have automatic memory management enabled
    • You want to create string objects
    • Those string objects you create need to live after the callback function creating them returns
  • 每次调用该函数,必须额外调用一次 RedisModule_FreeString() 以真正释放内存。

  • 在该函数中,上下文也可以为空。

1
RedisModuleString *RedisModule_HoldString(RedisModuleCtx *ctx, RedisModuleString *str);
  • RedisModule_RetainString() 中只有在无法从字符串待释放列表中移除该字符串时,才会增加引用次数;而调用 RedisModule_HoldString() 总是会成功达到目的。
  • 调用该函数时,在传入字符串的引用次数达到上限 INT_MAX - 1 时,调用 RedisModule_CreateStringFromString() 复制,返回副本的指针;否则,增加引用次数,返回传入字符串指针。所以该函数比 RedisModule_CreateStringFromString() 效率更高。
  • 在未开启自动内存管理时,需要手动调用 RedisModule_FreeString()
  • 在该函数中,上下文也可以为空。

3.4. Convert

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
/* Return the string pointer and length of the given module string.
 * Return value should only be used for read only access and never modified. */
const char *RedisModule_StringPtrLen(const RedisModuleString *str, size_t *len);

/* Convert the string into a long long integer, storing it at *ll.
 * Return REDISMODULE_OK on success. if the string can't be parsed as a valid,
 * strict long long(no sapces before/after), REDISMODULE_ERR is returned. */
int RedisModule_StringToLongLong(const RedisModuleString *str, long long *ll);

/* Convert the string into a double, storing it at *d.
 * Return REDISMODULE_OK on success or REDISMODULE_ERR
 * if the string is not a valid string representation of a double value. */
int RedisModule_StringToDouble(const RedisModuleString *str, double *d);

/* Convert the string into a long double, storing it at *ld.
 * Return REDISMODULE_OK on success or REDISMODULE_ERR if the
 * string is not a valid string representation of a long double value. */
int RedisModule_StringToLongDouble(const RedisModuleString *str, long double *ld);

3.5. Operators

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
/* Compare two string objects, returning -1, 0 or 1 respectively if a < b,
 * a == b or a > b. String are compared byte by byte as two binary blobs
 * without any encoding care/collation attempt. */
int RedisModule_StringCompare(RedisModuleString *a, RedisModuleString *b);

/* Append the specified buffer to the string str. The string must be
 * a string created by the user that is referenced only a single time,
 * otherwise REDISMOUDLE_ERR is returned and the operation is not performed. */
int RedisModule_StringAppendBuffer(
    RedisModuleCtx *ctx,
    RedisModuleString *str,
    const char *buf, size_t len);

4. Reply APIs

4.1. Reply with numeral

以下函数返回值固定为 REDISMODULE_OK.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
/* Send an integer reply to the client, with the specified long long value. */
int RedisModule_ReplyWithLongLong(RedisModuleCtx *ctx, long long ll);

/* Send a string reply obtained converting the double 'd' into a bulk string.
 * This function is basically equivalent to converting a double into a string
 * into a C buffer, and then calling the function
 * RedisModule_ReplyWithStringBuffer() with the buffer and length. */
int RedisModule_ReplyWithDouble(RedisModuleCtx *ctx, double d);

/* Send a string reply obtained converting the long double 'ld' into a bulk
 * string. This function is basically equivalent to converting a long double
 * into a string into a C buffer, and then calling the function
 * RedisModule_ReplyWithStringBuffer() with the buffer and length.
 * The double string uses human readable formatting
 * (see addReplyHumanLongDouble in networking.c). */
int RedisModule_ReplyWithLongDouble(RedisModuleCtx *ctx, long double ld);

4.2. Reply with string

以下函数返回值固定为 REDISMODULE_OK.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* Reply with a simple string(+...\r\n in RESP protocol). This replies are
 * suitable only when sending a small non-binary string with small overhead,
 * like "OK" or similar replies. */
int RedisModule_ReplyWithSimpleString(RedisModuleCtx *ctx, const char *msg);

/* Reply with a bulk string, taking in input a C buffer pointer
 * that is assumed to be null-terminated. */
int RedisModule_ReplyWithCString(RedisModuleCtx *ctx, const char *buf);

/* Reply with a bulk string, taking in input a C buffer pointer and length. */
int RedisModule_ReplyWithStringBuffer(RedisModuleCtx *ctx,
                                      const char *buf, size_t len);

/* Reply with a bulk string, taking in input a RedisModuleString object. */
int RedisModule_ReplyWithString(RedisModuleCtx *ctx, RedisModuleString *str);

/* Reply with an empty string */
int RedisModule_ReplyWithEmptyString(RedisModuleCtx *ctx);

/* Reply with a binary safe string, which should not be escaped or filtered
 * taking in input a C buffer pointer and length. */
int RedisModule_ReplyWithVerbatimString(RedisModuleCtx *ctx,
                                        const char *buf, size_t len);

4.3. Reply with array

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* Reply with an array type of 'len' elements.
 * However 'len' other calls to ReplyWith* style functions must follow in order to
 * emit the elements of the array. The function always returns REDISMODULE_OK. */
int RedisModule_ReplyWithArray(RedisModuleCtx *ctx, long len);

/* When RedisModule_ReplyWithArray() is used with the argument
 * REDISMODULE_POSTPONED_ARRAY_LEN, because we don't know beforehand the number
 * of items we are going to output as elements of the array, this function will
 * take care to set the array length.
 *
 * Since it is possible to have multiple array replies pending with unknown
 * length, this function guarantees to always set the latest array length
 * that was created in a postponed way. */
void RedisModule_ReplySetArrayLength(RedisModuleCtx *ctx, long len);

/* Reply to the client with a null array, simple null in RESP3
 * null array in RESP2. The function always returns REDISMODULE_OK. */
int RedisModule_ReplyWithNullArray(RedisModuleCtx *ctx);

/* Reply to client with an empty array.
 * The function always returns REDISMODULE_OK. */
int RedisModule_ReplyWithEmptyArray(RedisModuleCtx *ctx);

4.4. Others

以下函数返回值固定为 REDISMODULE_OK.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
/* Send an error about the number of arguments given to the command,
 * citing the command name in the error message. */
int RedisModule_WrongArity(RedisModuleCtx *ctx);

/* Reply to client with a NULL. */
int RedisModule_ReplyWithNull(RedisModuleCtx *ctx);

/* Reply with the error 'err'.
 *
 * Note that 'err' must contain all the error, including
 * the initial error code. */
int RedisModule_ReplyWithError(RedisModuleCtx *ctx, const char *err);

/* Reply exactly what a Redis command returned us with RedisModule_Call().
 * This function is useful when we use RedisModule_Call() in order to execute
 * some command, as we want to reply to the client exactly the same reply
 * we obtained by the command. */
int RedisModule_ReplyWithCallReply(RedisModuleCtx *ctx,
                                   RedisModuleCallReply *reply);

5. Commands replication APIs

1
2
3
4
/* Replicate the specified command and arguments to slaves and AOF, as effect
 * of execution of the calling command implementation. */
int RedisModule_Replicate(RedisModuleCtx *ctx,
                          const char *cmdname, const char *fmt, ...);
  • 复制的命令在 MULTI/EXEC 块中,其包含在该 module command 执行期间调用的所有命令,在 MULTI/EXEC 块中,通过 RedisModule_Call() 复制的命令在前,RedisModule_Replicate 复制的命令在后。Modules 应该只使用两个接口其中一个。
  • 该函数的接口和 RedisModule_Call() 相同,可查看 RedisModule_Call() 了解详情
  • 格式说明符非法或不存在该 cmdname 时,函数返回 REDISMODULE_ERR

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, Redis will accumulate all the calls to this function in the context of the callback, and will propagate all the commands wrapped in a MULTI/EXEC transaction. However when calling this function from a threaded safe context that can live an undefined amount of time, and can be locked/unlocked in at will, the behavior is different: MULTI/EXEC wrapper is not emitted and the command specified is inserted in the AOF and replication stream immediately.

1
2
/* Replicate the command exactly as it was invoked by the client. */
int RedisModule_ReplicateVerbatim(RedisModuleCtx *ctx);
  • 复制的命令不会包含在 MULTI/EXEC 块中,所以不要和其他命令复制的接口混用
  • 常用于想将客户端请求的命令直接复制转发至 slaveAOF 的场景
  • 返回值固定为 REDISMODULE_OK
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
/* Returns true if some client sent the CLIENT PAUSE command to the server or
 * if Redis Cluster is doing a manual failover, and paused tue clients.
 * This is needed when we have a master with replicas, and want to write,
 * without adding further data to the replication channel, that the replicas
 * replication offset, match the one of the master. When this happens, it is
 * safe to failover the master without data loss.
 *
 * However modules may generate traffic by calling RedisModule_Call() with
 * the "!" flag, or by calling RedisModule_Replicate(), in a context outside
 * commands execution, for instance in timeout callbacks, threads safe
 * contexts, and so forth. When modules will generate too much traffic, it
 * will be hard for the master and replicas offset to match, because there
 * is more data to send in the replication channel.
 *
 * So modules may want to try to avoid very heavy background work that has
 * the effect of creating data to the replication channel, when this function
 * returns true. This is mostly useful for modules that have background
 * garbage collection tasks, or that do writes and replicate such writes
 * periodically in timer callbacks or other periodic callbacks. */
int RedisModule_AvoidReplicaTraffic();

6. DB and Key APIs – Generic APIs

GetClientId

1
2
/* Return th ID of the current client calling the currently active module cmd. */
unsigned long long RedisModule_GetClientId(RedisModuleCtx *ctx);
  • 每个客户端 ID 是唯一的,单调递增的,取值范围为 $[1, 2^{64} - 1]$
  • 返回 0 则意味着在当前上下文中无法获取客户端 ID
  • 获取客户端 ID 后,可通过调用 RedisModule_IsAOFClient(id) 等判断当前客户端是否为指定类型

GetClientInfoById

1
2
3
/* Return information about the client with the specified ID. If the client
 * exists, REDISMODULE_OK is returned, otherwise REDISMODULE_ERR is returned. */
int RedisModule_GetClientInfoById(void *ci, uint64_t id);

当对应的客户端存在且传入的 ci 指向一个 RedisModuleClientInfo 结构体时,会使用客户端信息填充该结构体,其包含以下成员:

1
2
3
4
5
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

该结构体可被 REDISMODULE_CLIENTINFO_INITIALIZER 初始化,其中 flags 的种类如下:

flag meaning
REDISMODULE_CLIENTINFO_FLAG_SSL Client using SSL connection.
REDISMODULE_CLIENTINFO_FLAG_PUBSUB Client in Pub/Sub mode.
REDISMODULE_CLIENTINFO_FLAG_BLOCKED Client blocked in command.
REDISMODULE_CLIENTINFO_FLAG_TRACKING Client with keys tracking on.
REDISMODULE_CLIENTINFO_FLAG_UNIXSOCKET Client using unix domain socket.
REDISMODULE_CLIENTINFO_FLAG_MULTI Client in MULTI state.

当传入的 ciNULL 时,会直接返回 REDISMODULE_OKREDISMODULE_ERR 表明对应的客户端存在/不存在。

PublishMessage

1
2
3
/* Publish a message to subscribers (see PUBLISH command). */
int RedisModule_PublishMessage(RedisModuleCtx *ctx,
    RedisModuleString *channel, RedisModuleString *message);

GetSelectedDb

1
2
/* Return the currently selected DB. */
int RedisModule_GetSelectedDb(RedisModuleCtx *ctx);

SelectDb

1
2
/* Change the currently selected DB. Returns an error if the id is out of range. */
int RedisModule_SelectDb(RedisModuleCtx *ctx, int newid);

若调用该函数,直接返回响应后,客户端会一直保留该函数执行后所选择的 DB。因此,如想切换回之前的 DB,可调用 RedisModule_GetSelectedDb() 保存之前的 DB,并在返回响应前,切换回原先的 DB

DbSize

1
2
/* Returns the number of keys in the current db. */
unsigned long long RedisModule_DbSize(RedisModuleCtx *ctx);

ResetDataset

1
void RedisModule_ResetDataset(int restart_aof, int async)
  • 执行与 FLUSHALL 类似的操作
  • 在启用 AOF 的情况下,如果 restart_aoftrue,则会启动一个新的 AOF 文件,必须确保该命令不会传播到新的 AOF 文件
  • 如果 async 设置为 true,则释放数据占用内存的操作会在后台线程中执行

GetContextFlags

1
2
/* Return the current context's flags. */
int RedisModule_GetContextFlags(RedisModuleCtx *ctx);
  • 上下文标志会包含当前请求信息和实例信息
  • 也可以对 NULL 上下文调用该函数

其中 flags 的种类如下:

flag meaning
REDISMODULECTXFLAGS_LUA The command is running in a Lua script
REDISMODULECTXFLAGS_MULTI The command is running inside a transaction
REDISMODULECTXFLAGS_REPLICATED The command was sent over the replication link by the MASTER
REDISMODULECTXFLAGS_MASTER The Redis instance is a master
REDISMODULECTXFLAGS_SLAVE The Redis instance is a slave
REDISMODULECTXFLAGS_READONLY The Redis instance is read-only
REDISMODULECTXFLAGS_CLUSTER The Redis instance is in cluster mode
REDISMODULECTXFLAGS_AOF The Redis instance has AOF enabled
REDISMODULECTXFLAGS_RDB The instance has RDB enabled
REDISMODULECTXFLAGS_MAXMEMORY The instance has Maxmemory set
REDISMODULECTXFLAGS_EVICT Maxmemory is set and has an eviction policy that may delete keys
REDISMODULECTXFLAGS_OOM Redis is out of memory according to the maxmemory setting.
REDISMODULECTXFLAGS_OOM_WARNING Less than 25% of memory remains before reaching the maxmemory level.
REDISMODULECTXFLAGS_LOADING Server is loading RDB/AOF
REDISMODULECTXFLAGS_REPLICA_IS_STALE No active link with the master.
REDISMODULECTXFLAGS_REPLICA_IS_CONNECTING The replica is trying to connect with the master.
REDISMODULECTXFLAGS_REPLICA_IS_TRANSFERRING Master -> Replica RDB transfer is in progress.
REDISMODULECTXFLAGS_REPLICA_IS_ONLINE The replica has an active link with its master. This is the contrary of STALE state.
REDISMODULECTXFLAGS_ACTIVE_CHILD There is currently some background process active (RDB, AUX or module).

7. Low level Key APIs

7.1. Base

1
2
3
/* Return an handle representing a Redis key, so that it is possible to call
 * other APIs with the key handle as argument to perform operations on the key. */
void *RedisModule_OpenKey(RedisModuleCtx *ctx, robj *keyname, int mode);

通过 RedisModule_OpenKey() 返回的 RedisModuleKey,在对其访问结束之前,必须调用 RedisModule_CloseKey() 以释放句柄。

调用 RedisModule_OpenKey() 传入的 key 不存在时:

  • 如果是 REDISMODULE_WRITE 模式,仍会返回句柄
  • 如果仅是 REDISMODULE_READ 模式,则会返回 NULL
1
2
3
4
5
6
/* Close a key handle. */
void RedisModule_CloseKey(RedisModuleKey *key);

/* Return the type of the key. If the key pointer is NULL then
 * REDISMODULE_KEYTYPE_EMPTY is returned. */
int RedisModule_KeyType(RedisModuleKey *key);

调用 RedisModule_CloseKey()RedisModule_KeyType() 时,传入的实参可以为 NULL

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/* Return the length of the value associated with the key.
 * For strings this is the length of the string. For all the other types
 * is the number of elements (just counting keys for hashes).
 *
 * If the key pointer is NULL or the key is empty, zero is returned. */
size_t RedisModule_ValueLength(RedisModuleKey *key);

/* If the key is open for writing, remove it, and setup the key to
 * accept new writes as an empty key (that will be created on demand).
 * On success REDISMODULE_OK is returned. If the key is not open for
 * writing REDISMODULE_ERR is returned. */
int RedisModule_DeleteKey(RedisModuleKey *key);

/* If the key is open for writing, unlink it (that is delete it in a
 * non-blocking way, not reclaiming memory immediately) and setup the key to
 * accept new writes as an empty key (that will be created on demand).
 * On success REDISMODULE_OK is returned. If the key is not open for
 * writing REDISMODULE_ERR is returned. */
int RedisModule_UnlinkKey(RedisModuleKey *key);

/* Return the key expire value, as milliseconds of remaining TTL.
 * If no TTL is associated with the key or if the key is empty,
 * REDISMODULE_NO_EXPIRE is returned. */
mstime_t RedisModule_GetExpire(RedisModuleKey *key);

/* 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 number of milliseconds of TTL 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 RedisModule_SetExpire(RedisModuleKey *key, mstime_t expire);

/* Returns a name of a random key, or NULL if current db is empty. */
RedisModuleString *RedisModule_RandomKey(RedisModuleCtx *ctx);

7.2. String type

Set

1
2
3
4
5
/* If the key is open for writing, set the specified string 'str' as the
 * value of the key, deleting the old value if any.
 * On success REDISMODULE_OK is returned. If the key is not open for
 * writing or there is an active iterator, REDISMODULE_ERR is returned. */
int RedisModule_StringSet(RedisModuleKey *key, RedisModuleString *str);

DMA access

1
2
3
4
/* Prepare the key associated string value for DMA access, and returns
 * a pointer and size (by reference), that the user can use to read or
 * modify the string in-place accessing it directly via pointer. */
char *RedisModule_StringDMA(RedisModuleKey *key, size_t *len, int mode);
  • 如果 key 所关联的对象不是 String 类型,则会返回 NULL
  • 如果 mode 仅为可读,则不可修改返回值
  • DMA access rules:
    • 因为是就地访问,所以应避免并发问题。对于同一个 key,只存在一写或多读的情况。
    • 每次调用 RedisModule_StringTruncate() 之后, 需重新调用 RedisModule_StringDMA() 以获取新的字符串指针和长度
    • 若获取的指针不为 NULL,但长度为 0 时 (key 为空或字符串为空),可使用 RedisModule_StringTruncate() 调整字符串大小后再次调用 RedisModule_StringDMA()

Truncate

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
/* If the string is open for writing and is of string type, resize it, padding
 * with zero bytes if the new length is greater than the old one.
 *
 * After this call, RedisModule_StringDMA() must be called again to continue
 * DMA access with the new pointer.
 *
 * The function returns REDISMODULE_OK on success, and REDISMODULE_ERR on
 * error, that is, the key is not open for writing, is not a string
 * or resizing for more than 512 MB is requested.
 *
 * If the key is empty, a string key is created with the new string value
 * unless the new length value requested is zero. */
int RedisModule_StringTruncate(RedisModuleKey *key, size_t newlen);

7.3. List type

Push

1
2
/* Push an element into a list, on head or tail depending on 'where' argument. */
int RedisModule_ListPush(RedisModuleKey *key, int where, RedisModuleString *ele);
  • 如果传入的 key 为空,模式为写模式的话,则会创建新 key
  • 只读模式或类型错误时,返回 REDISMODULE_ERR,否则返回 REDISMODULE_OK

Pop

1
2
/* Pop an element from list, from head or tail depending on 'where' argument. */
RedisModuleString *RedisModule_ListPop(RedisModuleKey *key, int where);
  • 在未启用自动内存管理的情况下,返回值必须调用 RedisModule_FreeString() 进行释放
  • 返回值为 NULL 时,可能由以下原因造成:
    • 列表为空
    • 只读模式
    • 类型错误
1
2
3
/* tail and head */
REDISMODULE_LIST_HEAD
REDISMODULE_LIST_TAIL

7.4. Sorted Set type

1
2
3
4
/* Conversion from/to public flags of the Modules API and our private flags,
 * so that we have everything decoupled. */
int RedisModule_ZsetAddFlagsToCoreFlags(int flags);
int RedisModule_ZsetAddFlagsFromCoreFlags(int flags);

ZADD

1
2
3
4
5
6
7
/* Add a new element into a sorted set, with the specified 'score'.
 * If the element already exists, the score is updated. */
int RedisModule_ZsetAdd(
    RedisModuleKey *key,
    double score,
    RedisModuleString *ele,
    int *flagsptr);

在写模式下,如果 key 为空,则会新建一个 ZSET

可以注意到,此处 flags 按指针传递,其用途有二:

  • 作为 input flags, 决定 RedisModule_ZsetAdd() 的行为模式
  • 作为 output flags,返回 RedisModule_ZsetAdd() 的执行结果

其中,input flags 可以为

  • REDISMODULE_ZADD_XX: Element must already exist. Do nothing otherwise.
  • REDISMODULE_ZADD_NX: Element must not exist. Do nothing otherwise.

output flags 可以为:

  • REDISMODULE_ZADD_ADDED: The new element was added to the sorted set.
  • REDISMODULE_ZADD_UPDATED: The score of the element was updated.
  • REDISMODULE_ZADD_NOP: No operation was performed because XX or NX flags.

当然,flagsptr 也可为 NULL

函数执行成功时返回 REDISMODULE_OK,但遇到以下错误时会返回 REDISMODULE_ERR:

  • 只读模式
  • 类型错误
  • 传入的 score 不是数值类型

ZINCRBY

1
2
3
4
5
6
7
8
/* Increase the score of the existing element, of if the element does not
 * already exist, it is added assuming the old score was zero. */
int RedisModule_ZsetIncrby(
    RedisModuleKey *key,
    double score,
    RedisModuleString *ele,
    int *flagsptr,
    double *newscore);

inpout/output flags 和返回值和 RedisModule_ZsetAdd() 一样,唯一的区别是,当 score 不是数值类型时,该函数除了返回 REDISMODULE_ERR 之外,还会使得现有 elementsocre 变成非数值类型。

传入的 newscore 实参不为 NULL 时,会在函数执行完毕之后使用 element 新的 score 进行填充,在无错误的情况下,会被返回。

ZREM

1
2
/* Remove the specified element from the sorted set. */
int RedisModule_ZsetRem(RedisModuleKey *key, RedisModuleString *ele, int *deleted);

函数执行成功时返回 REDISMODULE_OK,但遇到以下错误时会返回 REDISMODULE_ERR:

  • 只读模式
  • 类型错误

通过返回值不能确定该 element 是否真的被移除,如果想知道 element 是否被移除的话,则需要传入非空的 deleted 指针,其在返回时会根据函数执行情况填充 0 或 1

ZSCORE

1
2
/* Retrieve the double score associated at the sorted set element 'ele'. */
int RedisModule_ZsetScore(RedisModuleKey *key, RedisModuleString *ele, double *score);

函数执行成功时返回 REDISMODULE_OK,但遇到以下错误时会返回 REDISMODULE_ERR:

  • ZSET 中不存在该 element
  • key 为空
  • 类型错误

Sorted Set iterator

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
/* Stop a sorted set iteration. */
void RedisModule_ZsetRangeStop(RedisModuleKey *key);

/* Return the "End of range" flag value to signal the end of the iteration. */
int RedisModule_ZsetRangeEndReached(RedisModuleKey *key);

/* Return the current sorted set element of an active sorted set iterator or
 * NULL if the range specified in the iterator does not include any element. */
RedisModuleString *RedisModule_ZsetRangeCurrentElement(RedisModuleKey *key, double *score);

/* Go to the next element of the sorted set iterator.
 * Returns 1 if there was a next element, 0 if we are already
 * at the latest element or the range does not include any item at all. */
int RedisModule_ZsetRangeNext(RedisModuleKey *key);

/* Go to the previous element of the sorted set iterator.
 * Returns 1 if there was a previous element, 0 if we are already
 * at the first element or the range does not include any item at all. */
int RedisModule_ZsetRangePrev(RedisModuleKey *key);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
/* Setup a sorted set iterator seeking the first element in the specified range. */
int RedisModule_ZsetFirstInScoreRange(
    RedisModuleKey *key,
    double min, double max,
    int minex, int maxex);

/* Exactly like RedisModule_ZsetFirstInScoreRange() but the last element of
 * the range is selected for the start of the iteration instead. */
int RedisModule_ZsetLastInScoreRange(
    RedisModuleKey *key,
    double min, double max,
    int minex, int maxex);

上述两函数在迭代器被正确初始化时,返回 REDISMODULE_OK;在 key 为空或对应的数据类型不是 ZSET 时,返回 REDISMODULE_ERR

迭代区间由 minmax 控制,当 minex(maxex) 设置为 true 时,区间不包含 min(max)。可以使用以下宏赋值给 minmax:

  • REDISMODULE_POSITIVE_INFINITE: positive infinite value
  • REDISMODULE_NEGATIVE_INFINITE: negative infinite value
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
/* Setup a sorted set iterator seeking the first element in the
 * specified lexicographical range. */
int RedisModule_ZsetFirstInLexRange(
    RedisModuleKey *key,
    RedisModuleString *min, RedisModuleString *max);

/* Exactly like RedisModule_ZsetFirstInLexRange() but the last element
 * of the range is selected for the start of the iteration instead. */
int RedisModule_ZsetLastInLexRange(
    RedisModuleKey *key,
    RedisModuleString *min, RedisModuleString *max);

上述两函数在迭代器被正确初始化时,返回 REDISMODULE_OK,在以下情况下返回 REDISMODULE_ERR:

  • key 对应的数据类型不为 ZSET
  • key 为空
  • 确定字典范围的 minmax 格式错误

传入的 minmax 格式和 ZRANGEBYLEX 命令参数的格式相同。minmax 对象可在迭代器被正确赋值之后立即释放。

7.5. Hash type

HashSet

1
2
/* Set the field of the specified hash field to the specified value. */
int RedisModule_HashSet(RedisModuleKey *key, int flags, ...);

如果 key 为空且处于写模式,则会新建一个 Hash

控制函数行为模式的 flags 列表如下:

flags meaning
REDISMODULE_HASH_NONE No special behavior is needed.
REDISMODULE_HASH_NX The operation is performed only if the field was not already existing in the hash.
REDISMODULE_HASH_XX The operation is performed only if the field was already existing, so that a new value could be associated to an existing filed, but no new fields are created.
REDISMODULE_HASH_CFIELDS The field names passed are null terminated C strings instead of RedisModuleString objects.

可变参数列表由 flied/value-ptr pairsNULL 组成:

  • flied/value-ptr pairs 中传入的数据类型为 RedisModuleString* (flags 不为 CFIELDS)
  • NULL 为最后一个实参的标识
  • value == REDISMODULE_HASH_DELETE 时,意为删除对应的 field

该函数的返回值为更新的 field 个数,故 key 为只读模式或对应的数据类型不为 Hash 时,返回值一直为 0。

1
2
3
4
5
6
7
8
/* Example to set the hash argv[1] to the value argv[2]: */
RedisModule_HashSet(key,REDISMODULE_HASH_NONE,argv[1],argv[2],NULL);

/* Example of delete fileds */
RedisModule_HashSet(key,REDISMODULE_HASH_NONE,argv[1],REDISMODULE_HASH_DELETE,NULL);

/* Example of REDISMODULE_HASH_CFIELDS */
RedisModule_HashSet(key,REDISMODULE_HASH_CFIELDS,"foo",REDISMODULE_HASH_DELETE,NULL);

HashGet

1
2
/* Get fields value */
int RedisModule_HashGet(RedisModuleKey *key, int flags, ...);

控制函数行为模式的 flags 列表如下:

flags meaning
REDISMODULE_HASH_NONE No special behavior is needed.
REDISMODULE_HASH_CFIELD field names as null terminated C strings.
REDISMODULE_HASH_EXISTS instead of setting the value of the field expecting a RedisModuleString pointer to pointer, the function just reports if the field exists or not and expects an integer pointer as the second element of each pair.

可变参数列表由 flied/value-ptr pairsNULL 组成:

  • flied/value-ptr pairs 中传入的数据类型为 RedisModuleString* (flags 不为 CFIELDS)
  • NULL 为最后一个实参的标识
  • 返回时,value 指针为空表示对应的 field 不存在
  • 不要忘记释放 RedisModuleString 对象

在函数执行成功时返回 REDISMODULE_OK,如 key 对应的数据类型不为 Hash,则返回 REDISMODULE_ERR

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
/* An example usage. */
RedisModuleString *first, *second;
RedisModule_HashGet(mykey,REDISMODULE_HASH_NONE,argv[1],&first,argv[2],&second,NULL);

/* Example of REDISMODULE_HASH_CFIELD */
RedisModuleString *username, *hashedpass;
RedisModule_HashGet(mykey,"username",&username,"hp",&hashedpass, NULL);

/* Example of REDISMODULE_HASH_EXISTS */
int exists;
RedisModule_HashGet(mykey,argv[1],&exists,NULL);

8. High level Key APIs – Call

Call

1
2
3
/* Exported API to call any Redis command from modules. */
RedisModuleCallReply *RedisModule_Call(RedisModuleCtx *ctx,
    const char *cmdname, const char *fmt, ...);

格式说明符列表如下:

  • c – Null terminated C string pointer
  • b – C buffer, two arguments needed: C string pointer and size_t length
  • s – RedisModuleString as received in argv or by other Redis module APIs returning a RedisModuleString object
  • l – Long long integer
  • v – Array of RedisModuleString objects
  • ! – This modifier just tells the function to replicate the command to replicas and AOF. it is ignored from the point of view of arguments parsing
  • A – This modifier, when ! is given, tells to suppress AOF propagation: the command will propagated only to replicas
  • R – This modifier, when ! is given, tells to suppress replicas propagation: the command will be propagated only to the AOF if enabled

函数执行成功时返回 RedisModuleCallReply 对象,失败时返回 NULL 并将 errno 设置为以下值:

errno meaning
EBADF wrong format specifier.
EINVAL wrong command arity.
ENOENT command does not exist.
EPERM operation in Cluster instance with key in non local slot.
EROFS operation in Cluster instance when a write command is sent in a readonly state. ENETDOWN

Access CallReply

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* Return the reply type. */
int RedisModule_CallReplyType(RedisModuleCallReply *reply);

/* Return the reply type length, where applicable. */
size_t RedisModule_CallReplyLength(RedisModuleCallReply *reply);

/* Return the 'idx'-th nested call reply element of an array reply,
 * or NULL if the reply type is wrong or the index is out of range. */
RedisModuleCallReply *RedisModule_CallReplyArrayElement(
    RedisModuleCallReply *reply, size_t idx);

/* Return the long long of an integer reply. */
long long RedisModule_CallReplyInteger(RedisModuleCallReply *reply);

/* Return the pointer and length of a string or error reply. */
const char *RedisModule_CallReplyStringPtr(RedisModuleCallReply *reply, size_t *len);

/* Return a new string object from a call reply of type string,
 * error or integer. Otherwise (wrong reply type) return NULL. */
RedisModuleString *RedisModule_CreateStringFromCallReply(RedisModuleCallReply *reply);

/* Return a pointer, and a length, to the protocol
 * returned by the command that returned the reply object. */
const char *RedisModule_CallReplyProto(RedisModuleCallReply *reply, size_t *len);

Free CallReply

1
2
3
4
5
6
7
/* Free a Call reply and all the nested replies it contains if it's an array. */
void RedisModule_FreeCallReply_Rec(RedisModuleCallReply *reply, int freenested);

/* Wrapper for the recursive free reply function.
 * This is needed in order to have the first level function to return
 * on nested replies, but only if called by the module API. */
void RedisModule_FreeCallReply(RedisModuleCallReply *reply);

9. 自定义数据类型

9.1. Create Data Type

1
2
3
4
5
6
/*  Register a new data type exported by the module. */
moduleType *RedisModule_CreateDataType(
    RedisModuleCtx *ctx,
    const char *name,
    int encver,
    void *typemethods_ptr);

name 已存在,nameencver 非法的情况下,该函数返回 NULL

各个参数说明如下:

  • name: 类型名,由 9 个字符组成,可用字符集合为 {A-Z, a-z, 0-9, -, _}

    “AAAAAAAAA” is reserved and produces an error

  • encver: 序列化数据的 Encoding version,用于向前兼容,取值范围为 $[0, 1023]$

  • typemethods_ptr: 指向 RedisModuleTypeMethods 结构体的指针,示例如下:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    RedisModuleTypeMethods tm =
    {
        .version = REDISMODULE_TYPE_METHOD_VERSION,
        .rdb_load = myType_RDBLoadCallBack,
        .rdb_save = myType_RDBSaveCallBack,
        .aof_rewrite = myType_AOFRewriteCallBack,
        .free = myType_FreeCallBack,
    
        /* Optional fields */
        .digest = myType_DigestCallBack,
        .mem_usage = myType_MemUsageCallBack,
        .aux_save = myType_AuxRDBSaveCallBack,
        .aux_load = myType_AuxRDBLoadCallBack,
    };
    

    结构体成员说明如下:

    • rdb_load: A callback function pointer that loads data from RDB files.
    • rdb_save: A callback function pointer that saves data to RDB files.
    • aof_rewrite: A callback function pointer that rewrites data as commands.
    • free: A callback function pointer that can free a type value.
    • digest: A callback function pointer that is used for DEBUG DIGEST.
    • mem_usage: A callback function pointer that is used for MEMORY
    • aux_save: A callback function pointer that saves out of keyspace data to RDB files. when argument is either REDISMODULE_AUX_BEFORE_RDB or REDISMODULE_AUX_AFTER_RDB.
    • aux_load: A callback function pointer that loads out of keyspace data from RDB files. Similar to aux_save, returns REDISMODULE_OK on success, and REDISMODULE_ERR otherwise.

    The digest and mem_usage methods should currently be omitted since they are not yet implemented inside the Redis modules core.

9.2. Access to moduleType

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/* If the key is open for writing, set the specified module type object
 * as the value of the key, deleting the old value if any.
 *
 * On success REDISMODULE_OK is returned. If the key is not open for writing
 * or there is an active iterator, REDISMODULE_ERR is returned. */
int RedisModule_ModuleTypeSetValue(RedisModuleKey *key, moduleType *mt, void *value);

/* Replace the value assigned to a module type.
 *
 * The key must be open for writing, have an existing value, and have a moduleType
 * that matches the one specified by the caller.
 *
 * Unlike RM_ModuleTypeSetValue() which will free the old value, this function
 * simply swaps the old value with the new value.
 *
 * The function returns REDISMODULE_OK on success, REDISMODULE_ERR on errors
 * such as:
 *
 * 1. Key is not opened for writing.
 * 2. Key is not a module data type key.
 * 3. Key is a module datatype other than 'mt'.
 *
 * If old_value is non-NULL, the old value is returned by reference. */
int RedisModule_ModuleTypeReplaceValue(
    RedisModuleKey *key, moduleType *mt, void *new_value, void **old_value);

/* Assuming RedisModule_KeyType() returned REDISMODULE_KEYTYPE_MODULE on
 * the key, returns the module type pointer of the value stored at key.
 *
 * If the key is NULL, is not associated with a module type, or is empty,
 * then NULL is returned instead. */
moduleType *RedisModule_ModuleTypeGetType(RedisModuleKey *key);

/* Assuming RedisModule_KeyType() returned REDISMODULE_KEYTYPE_MODULE on
 * the key, returns the module type low-level value stored at key, as
 * it was set by the user via RedisModule_ModuleTypeSetValue().
 *
 * If the key is NULL, is not associated with a module type, or is empty,
 * then NULL is returned instead. */
void *RedisModule_ModuleTypeGetValue(RedisModuleKey *key);

9.3. RDB loading and saving functions

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/* Returns true if any previous IO API failed.
 * for Load* APIs the REDISMODULE_OPTIONS_HANDLE_IO_ERRORS flag must be set with
 * RediModule_SetModuleOptions first. */
int RedisModule_IsIOError(RedisModuleIO *io);

/* Save an unsigned 64 bit value into the RDB file. This function should only
 * be called in the context of the rdb_save method of modules implementing new
 * data types. */
void RedisModule_SaveUnsigned(RedisModuleIO *io, uint64_t value);

/* Load an unsigned 64 bit value from the RDB file. This function should only
 * be called in the context of the rdb_load method of modules implementing
 * new data types. */
uint64_t RedisModule_LoadUnsigned(RedisModuleIO *io);

/* Like RedisModule_SaveUnsigned() but for signed 64 bit values. */
void RedisModule_SaveSigned(RedisModuleIO *io, int64_t value);

/* Like RedisModule_LoadUnsigned() but for signed 64 bit values. */
int64_t RedisModule_LoadSigned(RedisModuleIO *io);

/* In the context of the rdb_save method of a module type, saves a
 * string into the RDB file taking as input a RedisModuleString.
 *
 * The string can be later loaded with RedisModule_LoadString() or
 * other Load family functions expecting a serialized string inside
 * the RDB file. */
void RedisModule_SaveString(RedisModuleIO *io, RedisModuleString *s);

/* In the context of the rdb_load method of a module data type, loads a string
 * from the RDB file, that was previously saved with RedisModule_SaveString()
 * functions family.
 *
 * The returned string is a newly allocated RedisModuleString object, and
 * the user should at some point free it with a call to RedisModule_FreeString().
 *
 * If the data structure does not store strings as RedisModuleString objects,
 * the similar function RedisModule_LoadStringBuffer() could be used instead. */
RedisModuleString *RedisModule_LoadString(RedisModuleIO *io);

/* Like RedisModule_SaveString() but takes a raw C pointer and length as input. */
void RedisModule_SaveStringBuffer(RedisModuleIO *io, const char *str, size_t len);

/* Like RedisModule_LoadString() but returns an heap allocated string that
 * was allocated with RedisModule_Alloc(), and can be resized or freed with
 * RedisModule_Realloc() or RedisModule_Free().
 *
 * The size of the string is stored at '*lenptr' if not NULL.
 * The returned string is not automatically NULL terminated, it is loaded
 * exactly as it was stored inisde the RDB file. */
char *RedisModule_LoadStringBuffer(RedisModuleIO *io, size_t *lenptr);

/* In the context of the rdb_save method of a module data type, saves a double
 * value to the RDB file. The double can be a valid number, a NaN or infinity.
 * It is possible to load back the value with RedisModule_LoadDouble(). */
void RedisModule_SaveDouble(RedisModuleIO *io, double value);

/* In the context of the rdb_save method of a module data type, loads back the
 * double value saved by RedisModule_SaveDouble(). */
double RedisModule_LoadDouble(RedisModuleIO *io);

/* In the context of the rdb_save method of a module data type, saves a float
 * value to the RDB file. The float can be a valid number, a NaN or infinity.
 * It is possible to load back the value with RedisModule_LoadFloat(). */
void RedisModule_SaveFloat(RedisModuleIO *io, float value);

/* In the context of the rdb_save method of a module data type, loads back the
 * float value saved by RedisModule_SaveFloat(). */
float RedisModule_LoadFloat(RedisModuleIO *io);

/* In the context of the rdb_save method of a module data type, saves a long double
 * value to the RDB file. The double can be a valid number, a NaN or infinity.
 * It is possible to load back the value with RedisModule_LoadLongDouble(). */
void RedisModule_SaveLongDouble(RedisModuleIO *io, long double value);

/* In the context of the rdb_save method of a module data type, loads back the
 * long double value saved by RedisModule_SaveLongDouble(). */
long double RedisModule_LoadLongDouble(RedisModuleIO *io);

9.4. Key digest API

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/* Add a new element to the digest. This function can be called multiple times
 * one element after the other, for all the elements that constitute a given
 * data structure. The function call must be followed by the call to
 * `RedisModule_DigestEndSequence` eventually, when all the elements that are
 * always in a given order are added. See the Redis Modules data types
 * documentation for more info. However this is a quick example that uses Redis
 * data types as an example.
 *
 * To add a sequence of unordered elements (for example in the case of a Redis
 * Set), the pattern to use is:
 *
 *     foreach element {
 *         AddElement(element);
 *         EndSequence();
 *     }
 *
 * Because Sets are not ordered, so every element added has a position that
 * does not depend from the other. However if instead our elements are
 * ordered in pairs, like field-value pairs of an Hash, then one should
 * use:
 *
 *     foreach key,value {
 *         AddElement(key);
 *         AddElement(value);
 *         EndSquence();
 *     }
 *
 * Because the key and value will be always in the above order, while instead
 * the single key-value pairs, can appear in any position into a Redis hash.
 *
 * A list of ordered elements would be implemented with:
 *
 *     foreach element {
 *         AddElement(element);
 *     }
 *     EndSequence();
 *
 */
void RedisModule_DigestAddStringBuffer(RedisModuleDigest *md,
    unsigned char *ele, size_t len);

/* Like `RedisModule_DigestAddStringBuffer()` but takes a long long as input
 * that gets converted into a string before adding it to the digest. */
void RedisModule_DigestAddLongLong(RedisModuleDigest *md, long long ll);

void RedisModule_DigestEndSequence(RedisModuleDigest *md);

/* Decode a serialized representation of a module data type 'mt' from string
 * 'str' and return a newly allocated value, or NULL if decoding failed.
 *
 * This call basically reuses the 'rdb_load' callback which module data types
 * implement in order to allow a module to arbitrarily serialize/de-serialize
 * keys, similar to how the Redis 'DUMP' and 'RESTORE' commands are implemented.
 *
 * Modules should generally use the REDISMODULE_OPTIONS_HANDLE_IO_ERRORS flag and
 * make sure the de-serialization code properly checks and handles IO errors
 * (freeing allocated buffers and returning a NULL).
 *
 * If this is NOT done, Redis will handle corrupted (or just truncated) serialized
 * data by producing an error message and terminating the process.
 */
void *RedisModule_LoadDataTypeFromString(
    const RedisModuleString *str, const moduleType *mt);

/* Encode a module data type 'mt' value 'data' into serialized form, and return it
 * as a newly allocated RedisModuleString.
 *
 * This call basically reuses the 'rdb_save' callback which module data types
 * implement in order to allow a module to arbitrarily serialize/de-serialize
 * keys, similar to how the Redis 'DUMP' and 'RESTORE' commands are implemented.
 */
RedisModuleString *RedisModule_SaveDataTypeToString(
    RedisModuleCtx *ctx, void *data, const moduleType *md);

9.5. AOF API for modules data types

1
2
/* Emits a command into the AOF during the AOF rewriting process. */
void RedisModule_EmitAOF(RedisModuleIO *io, const char *cmdname, const char *fmt, ...);
  • 该函数只在 aof_rewrite 函数中调用
  • 执行模式和 RedisModule_Call() 相同,但是无返回值,异常情况由 Redis 自身处理

9.6. IO context handling

1
2
3
4
5
6
7
8
9
RedisModuleCtx *RedisModule_GetContextFromIO(RedisModuleIO *io);

/* Returns a RedisModuleString with the name of the key currently saving or
 * loading, when an IO data type callback is called.  There is no guarantee
 * that the key name is always available, so this may return NULL. */
const RedisModuleString *RedisModule_GetKeyNameFromIO(RedisModuleIO *io);

/* Returns a RedisModuleString with the name of the key from RedisModuleKey */
const RedisModuleString *RedisModule_GetKeyNameFromModuleKey(RedisModuleKey *key);

10. 输出日志

1
2
3
4
/* Produces a log message to the standard Redis log, the format accepts
 * printf-alike specifiers. */
void RedisModule_Log(RedisModuleCtx *ctx,
                     const char *levelstr, const char *fmt, ...);

levelstr 描述了指定的日志级别,日志级别可为以下四种之一:

  • debug
  • verbose
  • notice
  • warning

当传入的 levelstr 内容不为以上四种之一时,日志级别默认设置为 verbose。传入的上下文指针可以为 NULL,此时在日志输出时,会以 module 代替 module name。

日志长度有上限。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
/* Log errors from RDB / AOF serialization callbacks.
 *
 * This function should be used when a callback is returning a critical
 * error to the caller since cannot load or save the data for some
 * critical reason. */
void RedisModule_LogIOError(RedisModuleIO *io,
                            const char *levelstr, const char *fmt, ...);

/* Redis-like assert function.
 *
 * A failed assertion will shut down the server and produce logging information
 * that looks identical to information generated by Redis itself. */
void RedisModule__Assert(const char *estr, const char *file, int line);

/* Allows adding event to the latency monitor to be observed by the LATENCY
 * command. The call is skipped if the latency is smaller than the configured
 * latency-monitor-threshold. */
void RedisModule_LatencyAddSample(const char *event, mstime_t latency);

11. Blocking clients from modules

11.1. Block Client

1
2
3
4
5
6
7
/* Block a client in the context of a blocking command. */
RedisModuleBlockedClient *RedisModule_BlockClient(
    RedisModuleCtx *ctx,
    RedisModuleCmdFunc reply_callback,
    RedisModuleCmdFunc timeout_callback,
    void (*free_privdata)(RedisModuleCtx*,void*),
    long long timeout_ms);

参数说明:

  • reply_callback: called after a successful RedisModule_UnblockClient() call in order to reply to the client and unblock it.
  • timeout_callback: called when the timeout is reached in order to send an error to the client.
  • free_privdata: called in order to free the private data that is passed by RedisModule_UnblockClient() call.
  • timeout_ms: a timeout after which the client is unblocked.

在以下情况下不该调用该函数,如在这些情况下,调用该函数并不会阻塞客户端,会返回错误回复。

  • If the client is a Lua script.
  • If the client is executing a MULTI block.

函数执行成功会会返回 RedisModuleBlockedClient 对象,用于之后调用 RedisModule_UnblockClient() 以解除对客户端的阻塞及释放该对象占用的内存。因此,无论该客户端被 killed, time-out 或 disconnected,都应该调用 RedisModule_UnblockClient(),否则会有内存泄漏。

1
2
/* Unblock a client blocked by `RedisModule_BlockedClient`. */
int RedisModule_UnblockClient(RedisModuleBlockedClient *bc, void *privdata);
  • 调用该函数,会触发 reply_callback,按需将数据通过 privdata 指针传递至 reply_callback
  • 可在模块派生出的线程中调用此函数
  • 当对 RedisModule_BlockClientOnKeys 的返回值调用该函数时,privdata 指针无用,客户端被视为超时,触发 timeout_callback

11.2. Block Client On Keys

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
/* This call is similar to RedisModule_BlockClient(), however in this case we
 * don't just block the client, but also ask Redis to unblock it automatically
 * once certain keys become "ready", that is, contain more data. */
RedisModuleBlockedClient *RedisModule_BlockClientOnKeys(
    RedisModuleCtx *ctx,
    RedisModuleCmdFunc reply_callback,
    RedisModuleCmdFunc timeout_callback,
    void (*free_privdata)(RedisModuleCtx*,void*),
    long long timeout_ms,
    RedisModuleString **keys,
    int numkeys,
    void *privdata);

该函数的运行模式和 BLPOPBZPOPMAX 等可阻塞客户端的命令类似 —— 当不能立马获取数据时,客户端阻塞,随后当数据可获取时,客户端解除阻塞并返回数据。调用该函数后,解除阻塞条件分为两种:

  1. If you block on a key of a type that has blocking operations associated, like a list, a sorted set, a stream, and so forth, the client may be unblocked once the relevant key is targeted by an operation that normally unblocks the native blocking operations for that type. So if we block on a list key, an RPUSH command may unblock our client and so forth.
  2. If you are implementing your native data type, or if you want to add new unblocking conditions in addition to 1, you can call the modules API RedisModule_SignalKeyAsReady().

我们不能在收到 key 已准备好的信号后,立马解除对客户端的阻塞,因为在此期间 key 可能又被修改。因此在调用 RedisModule_BlockClientOnKeys() 时,reply_callback 并不是在调用 RedisModule_UnblockClient() 之后调用,而是在每次收到 key 已准备好的信号后调用:如果此时 reply_callback 可以返回客户端期待的数据时,其返回 REDISMODULE_OK 并解除对客户端的阻塞;否则其返回 REDISMODULE_ERR 并在之后重试。reply_callback 可以在收到 key 已准备的信号后,通过调用 RedisModule_GetBlockedClientReadyKey() 访问该 key

RedisModule_BlockClient() 中,prvidataRedisModule_UnblockClient() 提供,由于 RedisModule_BlockClientOnKeys() 中解除阻塞无须调用 RedisModule_UnblockClient(),所以我们直接传递了 prvidata 指针,供 reply_callback/timeout_callback 访问。

在 Block client on keys 的情况下,通常无需调用 RedisModule_UnblockClient(),若因某些原因而调用 RedisModule_UnblockClient() 时,此时客户端视为超时,因此此时必须实现 timeout_callback

1
2
3
4
5
6
7
8
9
/* This function is used in order to potentially unblock a client blocked
 * on keys with RedisModule_BlockClientOnKeys(). When this function is called,
 * all the clients blocked for this key will get their reply callback called,
 * and if the callback returns REDISMODULE_OK the client will be unblocked. */
void RedisModule_SignalKeyAsReady(RedisModuleCtx *ctx, RedisModuleString *key);

/* Get the key that is ready when the reply callback is called in the context
 * of a client blocked by RedisModule_BlockClientOnKeys(). */
RedisModuleString *RedisModule_GetBlockedClientReadyKey(RedisModuleCtx *ctx);

11.3. Utils

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/* Abort a blocked client blocking operation: the client will be unblocked
 * without firing any callback. */
int RedisModule_AbortBlock(RedisModuleBlockedClient *bc);

/* Set a callback that will be called if a blocked client disconnects
 * before the module has a chance to call RedisModule_UnblockClient()
 *
 * Usually what you want to do there, is to cleanup your module state
 * so that you can call RedisModule_UnblockClient() safely, otherwise
 * the client will remain blocked forever if the timeout is large.
 *
 * Notes:
 *
 * 1. It is not safe to call Reply* family functions here, it is also
 *    useless since the client is gone.
 *
 * 2. This callback is not called if the client disconnects because of
 *    a timeout. In such a case, the client is unblocked automatically
 *    and the timeout callback is called.
 */
void RedisModule_SetDisconnectCallback(RedisModuleBlockedClient *bc,
                                       RedisModuleDisconnectFunc callback);

/* Return non-zero if a module command was called in order to fill the
 * reply for a blocked client. */
int RedisModule_IsBlockedReplyRequest(RedisModuleCtx *ctx);

/* Return non-zero if a module command was called in order to fill the
 * reply for a blocked client that timed out. */
int RedisModule_IsBlockedTimeoutRequest(RedisModuleCtx *ctx);

/* Get the private data set by RedisModule_UnblockClient() */
void *RedisModule_GetBlockedClientPrivateData(RedisModuleCtx *ctx);

/* Get the blocked client associated with a given context.
 * This is useful in the reply and timeout callbacks of blocked clients,
 * before sometimes the module has the blocked client handle references
 * around, and wants to cleanup it. */
RedisModuleBlockedClient *RedisModule_GetBlockedClientHandle(RedisModuleCtx *ctx);

/* Return true if when the free callback of a blocked client is called,
 * the reason for the client to be unblocked is that it disconnected
 * while it was blocked. */
int RedisModule_BlockedClientDisconnected(RedisModuleCtx *ctx);

12. Thread Safe Contexts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/* Return a context which can be used inside threads to make Redis context
 * calls with certain modules APIs. If 'bc' is not NULL then the module will
 * be bound to a blocked client, and it will be possible to use the
 * `RedisModule_Reply*` family of functions to accumulate a reply for when the
 * client will be unblocked. Otherwise the thread safe context will be
 * detached by a specific client.
 *
 * To call non-reply APIs, the thread safe context must be prepared with:
 *
 *     RedisModule_ThreadSafeContextLock(ctx);
 *     ... make your call here ...
 *     RedisModule_ThreadSafeContextUnlock(ctx);
 *
 * This is not needed when using `RedisModule_Reply*` functions, assuming
 * that a blocked client was used when the context was created, otherwise
 * no RedisModule_Reply* call should be made at all.
 *
 * TODO: thread safe contexts do not inherit the blocked client
 * selected database. */
RedisModuleCtx *RedisModule_GetThreadSafeContext(RedisModuleBlockedClient *bc);

/* Release a thread safe context. */
void RedisModule_FreeThreadSafeContext(RedisModuleCtx *ctx);

/* Acquire the server lock before executing a thread safe API call.
 * This is not needed for `RedisModule_Reply*` calls when there is
 * a blocked client connected to the thread safe context. */
void RedisModule_ThreadSafeContextLock(RedisModuleCtx *ctx);

/* Release the server lock after a thread safe API call was executed. */
void RedisModule_ThreadSafeContextUnlock(RedisModuleCtx *ctx);

13. Module Keyspace Notifications API

1
2
3
4
5
/* Subscribe to keyspace notifications. This is a low-level version of the
 * keyspace-notifications API. A module can register callbacks to be notified
 * when keyspce events occur. */
int RedisModule_SubscribeToKeyspaceEvents(RedisModuleCtx *ctx,
    int types, RedisModuleNotificationFunc callback);

types 表示我们想订阅的 events 类型,其种类如下:

types meaning
REDISMODULE_NOTIFY_GENERIC Generic commands like DEL, EXPIRE, RENAME
REDISMODULE_NOTIFY_STRING String events
REDISMODULE_NOTIFY_LIST List events
REDISMODULE_NOTIFY_SET Set events
REDISMODULE_NOTIFY_HASH Hash events
REDISMODULE_NOTIFY_ZSET Sorted Set events
REDISMODULE_NOTIFY_EXPIRED Expiration events
REDISMODULE_NOTIFY_EVICTED Eviction events
REDISMODULE_NOTIFY_STREAM Stream 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, indicates that the key was loaded from persistence. Notice, when this event fires, the given key can not be retained, use RM_CreateStringFromString instead.

回调函数 RedisModuleNotificationFuncsignature 如下:

1
2
3
4
int (*RedisModuleNotificationFunc) (RedisModuleCtx *ctx,
                                    int type,
                                    const char *event,
                                    RedisModuleString *key);

其中:

  • type 必须和注册时相同
  • event 指的是正在执行的 command
  • key 为相关的 redis key

需要注意以下几点:

  • 不可再 RedisModuleNotificationFunc 中向客户端发送信息
  • 为了使 module notifications 工作无需在 redis.conf 中启用 notifications
  • 由于 RedisModuleNotificationFunc 是同步运行的,因此要保证该函数的运行够快
1
2
3
4
5
6
7
/* Get the configured bitmap of notify-keyspace-events (Could be used
 * for additional filtering in RedisModuleNotificationFunc) */
int RedisModule_GetNotifyKeyspaceEvents();

/* Expose notifyKeyspaceEvent to modules */
int RedisModule_NotifyKeyspaceEvent(RedisModuleCtx *ctx,
    int type, const char *event, RedisModuleString *key);

14. Modules Cluster API

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/* Register a callback receiver for cluster messages of type 'type'. If there
 * was already a registered callback, this will replace the callback function
 * with the one provided, otherwise if the callback is set to NULL and there
 * is already a callback for this function, the callback is unregistered
 * (so this API call is also used in order to delete the receiver). */
void RedisModule_RegisterClusterMessageReceiver(RedisModuleCtx *ctx,
    uint8_t type, RedisModuleClusterMessageReceiver callback);

/* Send a message to all the nodes in the cluster if `target` is NULL, otherwise
 * at the specified target, which is a REDISMODULE_NODE_ID_LEN bytes node ID, as
 * returned by the receiver callback or by the nodes iteration functions.
 *
 * The function returns REDISMODULE_OK if the message was successfully sent,
 * otherwise if the node is not connected or such node ID does not map to any
 * known cluster node, REDISMODULE_ERR is returned. */
int RedisModule_SendClusterMessage(RedisModuleCtx *ctx,
    char *target_id, uint8_t type, unsigned char *msg, uint32_t len);

/* Return an array of string pointers, each string pointer points to a cluster
 * node ID of exactly REDISMODULE_NODE_ID_SIZE bytes (without any null term).
 * The number of returned node IDs is stored into `*numnodes`.
 * However if this function is called by a module not running an a Redis
 * instance with Redis Cluster enabled, NULL is returned instead.
 *
 * The IDs returned can be used with RedisModule_GetClusterNodeInfo() in order
 * to get more information about single nodes.
 *
 * The array returned by this function must be freed using the function
 * RedisModule_FreeClusterNodesList().
 *
 * Example:
 *
 *     size_t count, j;
 *     char **ids = RedisModule_GetClusterNodesList(ctx,&count);
 *     for (j = 0; j < count; j++) {
 *         RedisModule_Log("notice","Node %.*s",
 *             REDISMODULE_NODE_ID_LEN,ids[j]);
 *     }
 *     RedisModule_FreeClusterNodesList(ids);
 */
char **RedisModule_GetClusterNodesList(RedisModuleCtx *ctx, size_t *numnodes);

/* Free the node list obtained with RedisModule_GetClusterNodesList. */
void RedisModule_FreeClusterNodesList(char **ids);

/* Return this node ID (REDISMODULE_CLUSTER_ID_LEN bytes) or NULL if the cluster
 * is disabled. */
const char *RedisModule_GetMyClusterID(void);

/* Return the number of nodes in the cluster, regardless of their state
 * (handshake, noaddress, ...) so that the number of active nodes may actually
 * be smaller, but not greater than this number. If the instance is not in
 * cluster mode, zero is returned. */
size_t RedisModule_GetClusterSize(void);

/* Populate the specified info for the node having as ID the specified 'id',
 * then returns REDISMODULE_OK. Otherwise if the node ID does not exist from
 * the POV of this local node, REDISMODULE_ERR is returned.
 *
 * The arguments ip, master_id, port and flags can be NULL in case we don't
 * need to populate back certain info. If an ip and master_id (only populated
 * if the instance is a slave) are specified, they point to buffers holding
 * at least REDISMODULE_NODE_ID_LEN bytes. The strings written back as ip
 * and master_id are not null terminated.
 *
 * The list of flags reported is the following:
 *
 * * REDISMODULE_NODE_MYSELF        This node
 * * REDISMODULE_NODE_MASTER        The node is a master
 * * REDISMODULE_NODE_SLAVE         The node is a replica
 * * REDISMODULE_NODE_PFAIL         We see the node as failing
 * * REDISMODULE_NODE_FAIL          The cluster agrees the node is failing
 * * REDISMODULE_NODE_NOFAILOVER    The slave is configured to never failover
 */
int RedisModule_GetClusterNodeInfo(RedisModuleCtx *ctx,
    const char *id, char *ip, char *master_id, int *port, int *flags);

/* Set Redis Cluster flags in order to change the normal behavior of
 * Redis Cluster, especially with the goal of disabling certain functions.
 * This is useful for modules that use the Cluster API in order to create
 * a different distributed system, but still want to use the Redis Cluster
 * message bus. Flags that can be set:
 *
 *  CLUSTER_MODULE_FLAG_NO_FAILOVER
 *  CLUSTER_MODULE_FLAG_NO_REDIRECTION
 *
 * With the following effects:
 *
 *  NO_FAILOVER: prevent Redis Cluster slaves to failover a failing master.
 *               Also disables the replica migration feature.
 *
 *  NO_REDIRECTION: Every node will accept any key, without trying to perform
 *                  partitioning according to the user Redis Cluster algorithm.
 *                  Slots informations will still be propagated across the
 *                  cluster, but without effects. */
void RedisModule_SetClusterFlags(RedisModuleCtx *ctx, uint64_t flags);

15. 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 the actual event loop will just have a single timer that is used to awake the module timers subsystem in order to process the next event.

All the timers are stored into a radix tree, ordered by expire time, when the main Redis event loop timer callback is called, we try to process all the timers already expired one after the other. Then we re-enter the event loop registering a timer that will expire when the next to process module timer will expire.

Every time the list of active timers drops to zero, we unregister the main event loop timer, so that there is no overhead when such feature is not used.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
/* Create a new timer that will fire after `period` milliseconds, and will call
 * the specified function using `data` as argument. The returned timer ID can be
 * used to get information from the timer or to stop it before it fires. */
RedisModuleTimerID RedisModule_CreateTimer(RedisModuleCtx *ctx,
    mstime_t period, RedisModuleTimerProc callback, void *data);

/* Stop a timer, returns REDISMODULE_OK if the timer was found, belonged to the
 * calling module, and was stopped, otherwise REDISMODULE_ERR is returned.
 * If not NULL, the data pointer is set to the value of the data argument when
 * the timer was created. */
int RedisModule_StopTimer(RedisModuleCtx *ctx, RedisModuleTimerID id, void **data);

/* Obtain information about a timer: its remaining time before firing
 * (in milliseconds), and the private data pointer associated with the timer.
 * If the timer specified does not exist or belongs to a different module
 * no information is returned and the function returns REDISMODULE_ERR, otherwise
 * REDISMODULE_OK is returned. The arguments remaining or data can be NULL if
 * the caller does not need certain information. */
int RedisModule_GetTimerInfo(RedisModuleCtx *ctx,
    RedisModuleTimerID id, uint64_t *remaining, void **data);

16. Modules ACL API

Implements a hook into the authentication and authorization within Redis.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/* Creates a Redis ACL user that the module can use to authenticate a client.
 * After obtaining the user, the module should set what such user can do
 * using the RM_SetUserACL() function. Once configured, the user
 * can be used in order to authenticate a connection, with the specified
 * ACL rules, using the RedisModule_AuthClientWithUser() function.
 *
 * Note that:
 *
 * * Users created here are not listed by the ACL command.
 * * Users created here are not checked for duplicated name, so it's up to
 *   the module calling this function to take care of not creating users
 *   with the same name.
 * * The created user can be used to authenticate multiple Redis connections.
 *
 * The caller can later free the user using the function
 * RM_FreeModuleUser(). When this function is called, if there are
 * still clients authenticated with this user, they are disconnected.
 * The function to free the user should only be used when the caller really
 * wants to invalidate the user to define a new one with different
 * capabilities. */
RedisModuleUser *RedisModule_CreateModuleUser(const char *name);

/* Frees a given user and disconnects all of the clients that have been
 * authenticated with it. See RM_CreateModuleUser for detailed usage.*/
int RedisModule_FreeModuleUser(RedisModuleUser *user);

/* Sets the permissions of a user created through the redis module
 * interface. The syntax is the same as ACL SETUSER, so refer to the
 * documentation in acl.c for more information. See RM_CreateModuleUser
 * for detailed usage.
 *
 * Returns REDISMODULE_OK on success and REDISMODULE_ERR on failure
 * and will set an errno describing why the operation failed. */
int RedisModule_SetModuleUserACL(RedisModuleUser *user, const char* acl);

/* Authenticate the current context's user with the provided redis acl user.
 * Returns REDISMODULE_ERR if the user is disabled.
 *
 * See authenticateClientWithUser for information about callback, client_id,
 * and general usage for authentication. */
int RedisModule_AuthenticateClientWithUser(
    RedisModuleCtx *ctx,
    RedisModuleUser *module_user,
    RedisModuleUserChangedFunc callback,
    void *privdata,
    uint64_t *client_id);

/* Authenticate the current context's user with the provided redis acl user.
 * Returns REDISMODULE_ERR if the user is disabled or the user does not exist.
 *
 * See authenticateClientWithUser for information about callback, client_id,
 * and general usage for authentication. */
int RedisModule_AuthenticateClientWithACLUser(
    RedisModuleCtx *ctx,
    const char *name,
    size_t len,
    RedisModuleUserChangedFunc callback,
    void *privdata,
    uint64_t *client_id);

/* Deauthenticate and close the client. The client resources will not be
 * be immediately freed, but will be cleaned up in a background job. This is
 * the recommended way to deauthenicate a client since most clients can't
 * handle users becomming deauthenticated. Returns REDISMODULE_ERR when the
 * client doesn't exist and REDISMODULE_OK when the operation was successful.
 *
 * The client ID is returned from the RM_AuthenticateClientWithUser and
 * RM_AuthenticateClientWithACLUser APIs, but can be obtained through
 * the CLIENT api or through server events.
 *
 * This function is not thread safe, and must be executed within the context
 * of a command or thread safe context. */
int RedisModule_DeauthenticateAndCloseClient(RedisModuleCtx *ctx, uint64_t client_id);

17. 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 capable of going back and forth.

17.1. Base

1
2
/* Create a new dictionary. */
RedisModuleDict *RedisModule_CreateDict(RedisModuleCtx *ctx);

新建一个 Dictionary,传入的 ctx 可以为 NULL

1
2
3
4
5
6
7
/* Free a dictionary created with RM_CreateDict(). You need to pass the
 * context pointer 'ctx' only if the dictionary was created using the
 * context instead of passing NULL. */
void RedisModule_FreeDict(RedisModuleCtx *ctx, RedisModuleDict *d);

/* Return the size of the dictionary (number of keys). */
uint64_t RedisModule_DictSize(RedisModuleDict *d);

17.2. Set/Get/Del

Take key as a cstring

1
2
3
/* Store the specified key into the dictionary, setting its value to the
 * pointer 'ptr'. */
int RedisModule_DictSetC(RedisModuleDict *d, void *key, size_t keylen, void *ptr);

key 已存在时,返回 REDISMODULE_ERR,否则返回 REDISMODULE_OK

1
2
3
4
5
6
/* Like RedisModule_DictSetC() but will replace the key with the new
 * value if the key already exists. */
int RedisModule_DictReplaceC(RedisModuleDict *d, void *key, size_t keylen, void *ptr);

/* Return the value stored at the specified key. */
void *RedisModule_DictGetC(RedisModuleDict *d, void *key, size_t keylen, int *nokey);

RedisModule_DictGetCkey 不存在或 key 对应的 valueNULL 时,返回 NULL。因此,若想确定 key 是否存在时,可传入非空的 nokey,当 key 不存在时,其会被设置为 1,否则为 0。

1
2
/* Remove the specified key from the dictionary. */
int RedisModule_DictDelC(RedisModuleDict *d, void *key, size_t keylen, void *oldval);

key 存在并被删除时,返回 REDISMODULE_OK,若 key 不存在时,返回 REDISMODULE_ERR。返回 REDISMODULE_OK 时,若传入的 oldval 不为空,其会保存 key 被删除前对应的 value

Take key as a RedisModuleString

key 的数据类型为 RedisModuleString,功能和上述四个接口相同。

1
2
3
4
5
6
7
int RedisModule_DictSet(RedisModuleDict *d, RedisModuleString *key, void *ptr);

int RedisModule_DictReplace(RedisModuleDict *d, RedisModuleString *key, void *ptr);

void *RedisModule_DictGet(RedisModuleDict *d, RedisModuleString *key, int *nokey);

int RedisModule_DictDel(RedisModuleDict *d, RedisModuleString *key, void *oldval);

17.3. Iterator

Take key as a cstring

1
2
RedisModuleDictIter *RedisModule_DictIteratorStartC(
    RedisModuleDict *d, const char *op, void *key, size_t keylen);

基于传入的 keyop,返回 Dictionary 的迭代器,其中 op 的种类如下:

  • ^ – Seek the first (lexicographically smaller) key.
  • $ – Seek the last (lexicographically biffer) key.
  • > – Seek the first element greter than the specified key.
  • >= – Seek the first element greater or equal than the specified key.
  • < – Seek the first element smaller than the specified key.
  • <= – Seek the first element smaller or equal than the specified key.
  • == – Seek the first element matching exactly the specified key.

op^$ 时,传入的 key 不会被使用,此时传入 key 可为 NULLkeylen 为 0.

如果根据 keyop 无法获取迭代器,那么初次调用 RedisModule_DictNextC() / PrevC() 时会返回 REDISMODULE_ERR

1
2
int RedisModule_DictIteratorReseekC(
    RedisModuleDictIter *di, const char *op, void *key, size_t keylen);
  • 在调用 RedisModule_DictIteratorStartC() 获得迭代器之后,可以通过调用该函数改变迭代器当前所指向的元素
  • opkey 的含义和 RedisModule_DictIteratorStartC() 相同
  • 查找到指定的元素后,返回 REDISMODULE_OK,无法查找指定的元素时,返回 REDISMODULE_ERR
1
2
3
4
5
6
7
8
/* Return the current item of the dictionary iterator 'di' and steps to the
 * next element. */
void *RedisModule_DictNextC(RedisModuleDictIter *di, size_t *keylen, void **dataptr);

/* This function is exactly like RedisModule_DictNext() but after returning
 * the currently selected element in the iterator, it selects the previous
 * element (laxicographically smaller) instead of the next one. */
void *RedisModule_DictPrevC(RedisModuleDictIter *di, size_t *keylen, void **dataptr);

这两函数执行模式类似,下述仅提及 RedisModule_DictNextC()

如果迭代器已指向最后一个元素,返回 NULL,否则返回指向 key 的指针,此时:

  • keylen 不为 NULL,则会被赋值为 key 的长度
  • *dataptr 不为 NULL,则会被赋值为指向 value 的指针

由于返回的值都是指针,因此返回值仅在 di 未被释放且未调用 RedisModule_DictNextC()RedisModule_DictPrevC() 之前有效。

使用示例:

1
2
3
4
5
6
7
/* ... create the iteator here ... */
char *key;
size_t keylen;
void *data;
while((key = RedisModule_DictNextC(iter, &keylen, &data)) != NULL) {
    printf("%.*s %p\n", (int)keylen, key, data);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/* Compare the element currently pointed by the iterator to the specified
 * element given by key/keylen, according to the operator 'op' (the set of
 * valid operators are the same valid for RedisModule_DictIteratorStart).
 * If the comparision is successful the command returns REDISMODULE_OK
 * otherwise REDISMODULE_ERR is returned.
 *
 * This is useful when we want to just emit a lexicographical range, so
 * in the loop, as we iterate elements, we can also check if we are still
 * on range.
 *
 * The function returne REDISMODULE_ERR if the iterator reached the
 * end of elements condition as well. */
int RedisModule_DictCompareC(
    RedisModuleDictIter *di, const char *op, void *key, size_t keylen);

Take key as a RedisModuleString

key 的数据类型为 RedisModuleString,功能和上述五个接口相同。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
RedisModuleDictIter *RedisModule_DictIteratorStart(
    RedisModuleDict *d, const char *op, RedisModuleString *key);

int RedisModule_DictIteratorReseek(
    RedisModuleDictIter *di, const char *op, RedisModuleString *key);

RedisModuleString *RedisModule_DictNext(
    RedisModuleCtx *ctx, RedisModuleDictIter *di, void **dataptr);

RedisModuleString *RedisModule_DictPrev(
    RedisModuleCtx *ctx, RedisModuleDictIter *di, void **dataptr);

int RedisModule_DictCompare(
    RedisModuleDictIter *di, const char *op, RedisModuleString *key);

Stop

1
2
/* Release the iterator created with RedisModule_DictIteratorStart(). */
void RedisModule_DictIteratorStop(RedisModuleDictIter *di);

必须调用该函数以释放 RedisModuleDictIter 对象。

18. Modules Info fields

可以使用以下接口在 INFO 命令中显示我们想展示的信息。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/* Used to start a new section, before adding any fields. the section name will
 * be prefixed by "<modulename>_" and must only include A-Z,a-z,0-9.
 * NULL or empty string indicates the default section (only <modulename>) is used.
 * When return value is REDISMODULE_ERR, the section should and will be skipped. */
int RedisModule_InfoAddSection(RedisModuleInfoCtx *ctx, char *name);

/* Starts a dict field, similar to the ones in INFO KEYSPACE. Use normal
 * RedisModule_InfoAddField* functions to add the items to this field, and
 * terminate with RedisModule_InfoEndDictField. */
int RedisModule_InfoBeginDictField(RedisModuleInfoCtx *ctx, char *name);

/* Ends a dict field, see RedisModule_InfoBeginDictField */
int RedisModule_InfoEndDictField(RedisModuleInfoCtx *ctx);

/* Used by RedisModuleInfoFunc to add info fields.
 * Each field will be automatically prefixed by "<modulename>_".
 * Field names or values must not include \r\n of ":" */
int RedisModule_InfoAddFieldString(
    RedisModuleInfoCtx *ctx, char *field, RedisModuleString *value);

int RedisModule_InfoAddFieldCString(
    RedisModuleInfoCtx *ctx, char *field, char *value);

int RedisModule_InfoAddFieldDouble(
    RedisModuleInfoCtx *ctx, char *field, double value);

int RedisModule_InfoAddFieldLongLong(
    RedisModuleInfoCtx *ctx, char *field, long long value);

int RedisModule_InfoAddFieldULongLong(
    RedisModuleInfoCtx *ctx, char *field, unsigned long long value);

int RedisModule_RegisterInfoFunc(RedisModuleCtx *ctx, RedisModuleInfoFunc cb);

/* Get information about the server similar to the one that returns from the
 * INFO command. This function takes an optional 'section' argument that may
 * be NULL. The return value holds the output and can be used with
 * RedisModule_ServerInfoGetField and alike to get the individual fields.
 * When done, it needs to be freed with RedisModule_FreeServerInfo or with the
 * automatic memory management mechanism if enabled. */
RedisModuleServerInfoData *RedisModule_GetServerInfo(
    RedisModuleCtx *ctx, const char *section);

/* Free data created with RM_GetServerInfo(). You need to pass the
 * context pointer 'ctx' only if the dictionary was created using the
 * context instead of passing NULL. */
void RedisModule_FreeServerInfo(RedisModuleCtx *ctx, RedisModuleServerInfoData *data);

/* Get the value of a field from data collected with RM_GetServerInfo(). You
 * need to pass the context pointer 'ctx' only if you want to use auto memory
 * mechanism to release the returned string. Return value will be NULL if the
 * field was not found. */
RedisModuleString *RedisModule_ServerInfoGetField(
    RedisModuleCtx *ctx, RedisModuleServerInfoData *data, const char* field);

/* Similar to RM_ServerInfoGetField, but returns a char* which should not be 
 * freed but the caller. */
const char *RedisModule_ServerInfoGetFieldC(
    RedisModuleServerInfoData *data, const char* field);

/* Get the value of a field from data collected with RM_GetServerInfo(). If the
 * field is not found, or is not numerical or out of range, return value will be
 * 0, and the optional out_err argument will be set to REDISMODULE_ERR. */
long long RedisModule_ServerInfoGetFieldSigned(
    RedisModuleServerInfoData *data, const char* field, int *out_err);

/* Get the value of a field from data collected with RM_GetServerInfo(). If the
 * field is not found, or is not numerical or out of range, return value will be
 * 0, and the optional out_err argument will be set to REDISMODULE_ERR. */
unsigned long long RedisModule_ServerInfoGetFieldUnsigned(
    RedisModuleServerInfoData *data, const char* field, int *out_err);

/* Get the value of a field from data collected with RM_GetServerInfo(). If the
 * field is not found, or is not a double, return value will be 0, and the
 * optional out_err argument will be set to REDISMODULE_ERR. */
double RedisModule_ServerInfoGetFieldDouble(
    RedisModuleServerInfoData *data, const char* field, int *out_err);

19. Modules utility APIs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/* Return random bytes using SHA1 in counter mode with a /dev/urandom
 * initialized seed. This function is fast so can be used to generate
 * many bytes without any effect on the operating system entropy pool.
 * Currently this function is not thread safe. */
void RedisModule_GetRandomBytes(unsigned char *dst, size_t len);

/* Like RedisModule_GetRandomBytes() but instead of setting the string to
 * random bytes the string is set to random characters in the in the
 * hex charset [0-9a-f]. */
void RedisModule_GetRandomHexChars(char *dst, size_t len);

20. Modules API exporting / importing

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/* This function is called by a module in order to export some API with a
 * given name. Other modules will be able to use this API by calling the
 * symmetrical function RM_GetSharedAPI() and casting the return value to
 * the right function pointer.
 *
 * The function will return REDISMODULE_OK if the name is not already taken,
 * otherwise REDISMODULE_ERR will be returned and no operation will be
 * performed.
 *
 * IMPORTANT: the apiname argument should be a string literal with static
 * lifetime. The API relies on the fact that it will always be valid in
 * the future. */
int RedisModule_ExportSharedAPI(RedisModuleCtx *ctx, const char *apiname, void *func);

/* Request an exported API pointer. The return value is just a void pointer
 * that the caller of this function will be required to cast to the right
 * function pointer, so this is a private contract between modules.
 *
 * If the requested API is not available then NULL is returned. Because
 * modules can be loaded at different times with different order, this
 * function calls should be put inside some module generic API registering
 * step, that is called every time a module attempts to execute a
 * command that requires external APIs: if some API cannot be resolved, the
 * command should return an error.
 *
 * Here is an exmaple:
 *
 *     int ... myCommandImplementation() {
 *        if (getExternalAPIs() == 0) {
 *             reply with an error here if we cannot have the APIs
 *        }
 *        // Use the API:
 *        myFunctionPointer(foo);
 *     }
 *
 * And the function registerAPI() is:
 *
 *     int getExternalAPIs(void) {
 *         static int api_loaded = 0;
 *         if (api_loaded != 0) return 1; // APIs already resolved.
 *
 *         myFunctionPointer = RedisModule_GetOtherModuleAPI("...");
 *         if (myFunctionPointer == NULL) return 0;
 *
 *         return 1;
 *     }
 */
void *RedisModule_GetSharedAPI(RedisModuleCtx *ctx, const char *apiname);

21. Module Command Filter API

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/* Register a new command filter function.
 *
 * Command filtering makes it possible for modules to extend Redis by plugging
 * into the execution flow of all commands.
 *
 * A registered filter gets called before Redis executes *any* command.  This
 * includes both core Redis commands and commands registered by any module.  The
 * filter applies in all execution paths including:
 *
 * 1. Invocation by a client.
 * 2. Invocation through `RedisModule_Call()` by any module.
 * 3. Invocation through Lua 'redis.call()`.
 * 4. Replication of a command from a master.
 *
 * The filter executes in a special filter context, which is different and more
 * limited than a RedisModuleCtx.  Because the filter affects any command, it
 * must be implemented in a very efficient way to reduce the performance impact
 * on Redis.  All Redis Module API calls that require a valid context (such as
 * `RedisModule_Call()`, `RedisModule_OpenKey()`, etc.) are not supported in a
 * filter context.
 *
 * The `RedisModuleCommandFilterCtx` can be used to inspect or modify the
 * executed command and its arguments.  As the filter executes before Redis
 * begins processing the command, any change will affect the way the command is
 * processed.  For example, a module can override Redis commands this way:
 *
 * 1. Register a `MODULE.SET` command which implements an extended version of
 *    the Redis `SET` command.
 * 2. Register a command filter which detects invocation of `SET` on a specific
 *    pattern of keys.  Once detected, the filter will replace the first
 *    argument from `SET` to `MODULE.SET`.
 * 3. When filter execution is complete, Redis considers the new command name
 *    and therefore executes the module's own command.
 *
 * Note that in the above use case, if `MODULE.SET` itself uses
 * `RedisModule_Call()` the filter will be applied on that call as well.  If
 * that is not desired, the `REDISMODULE_CMDFILTER_NOSELF` flag can be set when
 * registering the filter.
 *
 * The `REDISMODULE_CMDFILTER_NOSELF` flag prevents execution flows that
 * originate from the module's own `RM_Call()` from reaching the filter.  This
 * flag is effective for all execution flows, including nested ones, as long as
 * the execution begins from the module's command context or a thread-safe
 * context that is associated with a blocking command.
 *
 * Detached thread-safe contexts are *not* associated with the module and cannot
 * be protected by this flag.
 *
 * If multiple filters are registered (by the same or different modules), they
 * are executed in the order of registration. */
RedisModuleCommandFilter *RedisModule_RegisterCommandFilter(
    RedisModuleCtx *ctx, RedisModuleCommandFilterFunc callback, int flags);

/* Unregister a command filter. */
int RedisModule_UnregisterCommandFilter(
    RedisModuleCtx *ctx, RedisModuleCommandFilter *filter);

/* Return the number of arguments a filtered command has.  The number of
 * arguments include the command itself. */
int RedisModule_CommandFilterArgsCount(RedisModuleCommandFilterCtx *fctx);

/* Return the specified command argument.  The first argument (position 0) is
 * the command itself, and the rest are user-provided args. */
const RedisModuleString *RedisModule_CommandFilterArgGet(
    RedisModuleCommandFilterCtx *fctx, int pos);

/* Modify the filtered command by inserting a new argument at the specified
 * position.  The specified RedisModuleString argument may be used by Redis
 * after the filter context is destroyed, so it must not be auto-memory
 * allocated, freed or used elsewhere. */
int RedisModule_CommandFilterArgInsert(
    RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg);

/* Modify the filtered command by replacing an existing argument with a new one.
 * The specified RedisModuleString argument may be used by Redis after the
 * filter context is destroyed, so it must not be auto-memory allocated, freed
 * or used elsewhere. */
int RedisModule_CommandFilterArgReplace(
    RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg);

/* Modify the filtered command by deleting an argument at the specified position. */
int RedisModule_CommandFilterArgDelete(RedisModuleCommandFilterCtx *fctx, int pos);

22. Scanning keyspace and hashes

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/* Create a new cursor to be used with RedisModule_Scan */
RedisModuleScanCursor *RedisModule_ScanCursorCreate();

/* Restart an existing cursor. The keys will be rescanned. */
void RedisModule_ScanCursorRestart(RedisModuleScanCursor *cursor);

/* Destroy the cursor struct. */
void RedisModule_ScanCursorDestroy(RedisModuleScanCursor *cursor);

/* Scan API that allows a module to scan all the keys and value in
 * the selected db.
 *
 * Callback for scan implementation.
 * void scan_callback(RedisModuleCtx *ctx, RedisModuleString *keyname,
 *                    RedisModuleKey *key, void *privdata);
 * ctx - the redis module context provided to for the scan.
 * keyname - owned by the caller and need to be retained if used after this
 * function.
 *
 * key - holds info on the key and value, it is provided as best effort, in
 * some cases it might be NULL, in which case the user should (can) use
 * RedisModule_OpenKey (and CloseKey too).
 * when it is provided, it is owned by the caller and will be free when the
 * callback returns.
 *
 * privdata - the user data provided to RedisModule_Scan.
 *
 * The way it should be used:
 *      RedisModuleCursor *c = RedisModule_ScanCursorCreate();
 *      while(RedisModule_Scan(ctx, c, callback, privateData));
 *      RedisModule_ScanCursorDestroy(c);
 *
 * It is also possible to use this API from another thread while the lock
 * is acquired durring the actuall call to RM_Scan:
 *
 *      RedisModuleCursor *c = RedisModule_ScanCursorCreate();
 *      RedisModule_ThreadSafeContextLock(ctx);
 *      while(RedisModule_Scan(ctx, c, callback, privateData)){
 *          RedisModule_ThreadSafeContextUnlock(ctx);
 *          // do some background job
 *          RedisModule_ThreadSafeContextLock(ctx);
 *      }
 *      RedisModule_ScanCursorDestroy(c);
 *
 * The function will return 1 if there are more elements to scan and
 * 0 otherwise, possibly setting errno if the call failed.
 *
 * It is also possible to restart an existing cursor using RM_ScanCursorRestart.
 *
 * IMPORTANT: This API is very similar to the Redis SCAN command from the
 * point of view of the guarantees it provides. This means that the API
 * may report duplicated keys, but guarantees to report at least one time
 * every key that was there from the start to the end of the scanning process.
 *
 * NOTE: If you do database changes within the callback, you should be aware
 * that the internal state of the database may change. For instance it is safe
 * to delete or modify the current key, but may not be safe to delete any
 * other key.
 * Moreover playing with the Redis keyspace while iterating may have the
 * effect of returning more duplicates. A safe pattern is to store the keys
 * names you want to modify elsewhere, and perform the actions on the keys
 * later when the iteration is complete. Howerver this can cost a lot of
 * memory, so it may make sense to just operate on the current key when
 * possible during the iteration, given that this is safe. */
int RedisModule_Scan(RedisModuleCtx *ctx,
    RedisModuleScanCursor *cursor, RedisModuleScanCB fn, void *privdata);

/* Scan api that allows a module to scan the elements in a hash, set or sorted set key
 *
 * Callback for scan implementation.
 * void scan_callback(RedisModuleKey *key, RedisModuleString* field,
 *                    RedisModuleString* value, void *privdata);
 * - key - the redis key context provided to for the scan.
 * - field - field name, owned by the caller and need to be retained if used
 *   after this function.
 * - value - value string or NULL for set type, owned by the caller and need to
 *   be retained if used after this function.
 * - privdata - the user data provided to RedisModule_ScanKey.
 *
 * The way it should be used:
 *      RedisModuleCursor *c = RedisModule_ScanCursorCreate();
 *      RedisModuleKey *key = RedisModule_OpenKey(...)
 *      while(RedisModule_ScanKey(key, c, callback, privateData));
 *      RedisModule_CloseKey(key);
 *      RedisModule_ScanCursorDestroy(c);
 *
 * It is also possible to use this API from another thread while the lock is acquired
 * during the actuall call to RM_ScanKey, and re-opening the key each time:
 *      RedisModuleCursor *c = RedisModule_ScanCursorCreate();
 *      RedisModule_ThreadSafeContextLock(ctx);
 *      RedisModuleKey *key = RedisModule_OpenKey(...)
 *      while(RedisModule_ScanKey(ctx, c, callback, privateData)){
 *          RedisModule_CloseKey(key);
 *          RedisModule_ThreadSafeContextUnlock(ctx);
 *          // do some background job
 *          RedisModule_ThreadSafeContextLock(ctx);
 *          RedisModuleKey *key = RedisModule_OpenKey(...)
 *      }
 *      RedisModule_CloseKey(key);
 *      RedisModule_ScanCursorDestroy(c);
 *
 * The function will return 1 if there are more elements to scan and 0 otherwise,
 * possibly setting errno if the call failed.
 * It is also possible to restart an existing cursor using RM_ScanCursorRestart.
 *
 * NOTE: Certain operations are unsafe while iterating the object. For instance
 * while the API guarantees to return at least one time all the elements that
 * are present in the data structure consistently from the start to the end
 * of the iteration (see HSCAN and similar commands documentation), the more
 * you play with the elements, the more duplicates you may get. In general
 * deleting the current element of the data structure is safe, while removing
 * the key you are iterating is not safe. */
int RedisModule_ScanKey(RedisModuleKey *key,
    RedisModuleScanCursor *cursor, RedisModuleScanKeyCB fn, void *privdata);

23. Module fork API

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
/* Create a background child process with the current frozen snaphost of the
 * main process where you can do some processing in the background without
 * affecting / freezing the traffic and no need for threads and GIL locking.
 * Note that Redis allows for only one concurrent fork.
 * When the child wants to exit, it should call RedisModule_ExitFromChild.
 * If the parent wants to kill the child it should call RedisModule_KillForkChild
 * The done handler callback will be executed on the parent process when the
 * child existed (but not when killed)
 * Return: -1 on failure, on success the parent process will get a positive PID
 * of the child, and the child process will get 0. */
int RedisModule_Fork(RedisModuleForkDoneHandler cb, void *user_data);

/* Call from the child process when you want to terminate it.
 * retcode will be provided to the done handler executed on the parent process. */
int RedisModule_ExitFromChild(int retcode);

/* Can be used to kill the forked child process from the parent process.
 * child_pid whould be the return value of RedisModule_Fork. */
int RedisModule_KillForkChild(int child_pid);

24. Server hooks implementation

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/* 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 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.
 *
 * Finally the 'data' pointer may be populated, only for certain events, with
 * more relevant data.
 *
 * Here is a list of events you can use as 'eid' and related sub events:
 *
 *      RedisModuleEvent_ReplicationRoleChanged
 *
 *          This event is called when the instance switches from master
 *          to replica or the other way around, however the event is
 *          also called when the replica remains a replica but starts to
 *          replicate with a different master.
 *
 *          The following sub events are available:
 *
 *              REDISMODULE_SUBEVENT_REPLROLECHANGED_NOW_MASTER
 *              REDISMODULE_SUBEVENT_REPLROLECHANGED_NOW_REPLICA
 *
 *          The 'data' field can be casted by the callback to a
 *          RedisModuleReplicationInfo structure with the following fields:
 *
 *              int master; // true if master, false if replica
 *              char *masterhost; // master instance hostname for NOW_REPLICA
 *              int masterport; // master instance port for NOW_REPLICA
 *              char *replid1; // Main replication ID
 *              char *replid2; // Secondary replication ID
 *              uint64_t repl1_offset; // Main replication offset
 *              uint64_t repl2_offset; // Offset of replid2 validity
 *
 *      RedisModuleEvent_Persistence
 *
 *          This event is called when RDB saving or AOF rewriting starts
 *          and ends. The following sub events are available:
 *
 *              REDISMODULE_SUBEVENT_PERSISTENCE_RDB_START
 *              REDISMODULE_SUBEVENT_PERSISTENCE_AOF_START
 *              REDISMODULE_SUBEVENT_PERSISTENCE_SYNC_RDB_START
 *              REDISMODULE_SUBEVENT_PERSISTENCE_ENDED
 *              REDISMODULE_SUBEVENT_PERSISTENCE_FAILED
 *
 *          The above events are triggered not just when the user calls the
 *          relevant commands like BGSAVE, but also when a saving operation
 *          or AOF rewriting occurs because of internal server triggers.
 *          The SYNC_RDB_START sub events are happening in the forground due to
 *          SAVE command, FLUSHALL, or server shutdown, and the other RDB and
 *          AOF sub events are executed in a background fork child, so any
 *          action the module takes can only affect the generated AOF or RDB,
 *          but will not be reflected in the parent process and affect connected
 *          clients and commands. Also note that the AOF_START sub event may end
 *          up saving RDB content in case of an AOF with rdb-preamble.
 *
 *      RedisModuleEvent_FlushDB
 *
 *          The FLUSHALL, FLUSHDB or an internal flush (for instance
 *          because of replication, after the replica synchronization)
 *          happened. The following sub events are available:
 *
 *              REDISMODULE_SUBEVENT_FLUSHDB_START
 *              REDISMODULE_SUBEVENT_FLUSHDB_END
 *
 *          The data pointer can be casted to a RedisModuleFlushInfo
 *          structure with the following fields:
 *
 *              int32_t async;  // True if the flush is done in a thread.
 *                                 See for instance FLUSHALL ASYNC.
 *                                 In this case the END callback is invoked
 *                                 immediately after the database is put
 *                                 in the free list of the thread.
 *              int32_t dbnum;  // Flushed database number, -1 for all the DBs
 *                                 in the case of the FLUSHALL operation.
 *
 *          The start event is called *before* the operation is initated, thus
 *          allowing the callback to call DBSIZE or other operation on the
 *          yet-to-free keyspace.
 *
 *      RedisModuleEvent_Loading
 *
 *          Called on loading operations: at startup when the server is
 *          started, but also after a first synchronization when the
 *          replica is loading the RDB file from the master.
 *          The following sub events are available:
 *
 *              REDISMODULE_SUBEVENT_LOADING_RDB_START
 *              REDISMODULE_SUBEVENT_LOADING_AOF_START
 *              REDISMODULE_SUBEVENT_LOADING_REPL_START
 *              REDISMODULE_SUBEVENT_LOADING_ENDED
 *              REDISMODULE_SUBEVENT_LOADING_FAILED
 *
 *          Note that AOF loading may start with an RDB data in case of
 *          rdb-preamble, in which case you'll only recieve an AOF_START event.
 *
 *
 *      RedisModuleEvent_ClientChange
 *
 *          Called when a client connects or disconnects.
 *          The data pointer can be casted to a RedisModuleClientInfo
 *          structure, documented in RedisModule_GetClientInfoById().
 *          The following sub events are available:
 *
 *              REDISMODULE_SUBEVENT_CLIENT_CHANGE_CONNECTED
 *              REDISMODULE_SUBEVENT_CLIENT_CHANGE_DISCONNECTED
 *
 *      RedisModuleEvent_Shutdown
 *
 *          The server is shutting down. No subevents are available.
 *
 *  RedisModuleEvent_ReplicaChange
 *
 *          This event is called when the instance (that can be both a
 *          master or a replica) get a new online replica, or lose a
 *          replica since it gets disconnected.
 *          The following sub events are availble:
 *
 *              REDISMODULE_SUBEVENT_REPLICA_CHANGE_ONLINE
 *              REDISMODULE_SUBEVENT_REPLICA_CHANGE_OFFLINE
 *
 *          No additional information is available so far: future versions
 *          of Redis will have an API in order to enumerate the replicas
 *          connected and their state.
 *
 *  RedisModuleEvent_CronLoop
 *
 *          This event is called every time Redis calls the serverCron()
 *          function in order to do certain bookkeeping. Modules that are
 *          required to do operations from time to time may use this callback.
 *          Normally Redis calls this function 10 times per second, but
 *          this changes depending on the "hz" configuration.
 *          No sub events are available.
 *
 *          The data pointer can be casted to a RedisModuleCronLoop
 *          structure with the following fields:
 *
 *              int32_t hz;  // Approximate number of events per second.
 *
 *  RedisModuleEvent_MasterLinkChange
 *
 *          This is called for replicas in order to notify when the
 *          replication link becomes functional (up) with our master,
 *          or when it goes down. Note that the link is not considered
 *          up when we just connected to the master, but only if the
 *          replication is happening correctly.
 *          The following sub events are available:
 *
 *              REDISMODULE_SUBEVENT_MASTER_LINK_UP
 *              REDISMODULE_SUBEVENT_MASTER_LINK_DOWN
 *
 *  RedisModuleEvent_ModuleChange
 *
 *          This event is called when a new module is loaded or one is unloaded.
 *          The following sub events are availble:
 *
 *              REDISMODULE_SUBEVENT_MODULE_LOADED
 *              REDISMODULE_SUBEVENT_MODULE_UNLOADED
 *
 *          The data pointer can be casted to a RedisModuleModuleChange
 *          structure with the following fields:
 *
 *              const char* module_name;  // Name of module loaded or unloaded.
 *              int32_t module_version;  // Module version.
 *
 *  RedisModuleEvent_LoadingProgress
 *
 *          This event is called repeatedly called while an RDB or AOF file
 *          is being loaded.
 *          The following sub events are availble:
 *
 *              REDISMODULE_SUBEVENT_LOADING_PROGRESS_RDB
 *              REDISMODULE_SUBEVENT_LOADING_PROGRESS_AOF
 *
 *          The data pointer can be casted to a RedisModuleLoadingProgress
 *          structure with the following fields:
 *
 *              int32_t hz;  // Approximate number of events per second.
 *              int32_t progress;  // Approximate progress between 0 and 1024,
 *                                    or -1 if unknown.
 *
 * 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);

25. Modules API internals

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* Set the key last access time for LRU based eviction. not relevent 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. */
int RedisModule_SetLRU(RedisModuleKey *key, mstime_t lru_idle);

/* Gets the key last access time.
 * Value is idletime in milliseconds or -1 if the server's eviction policy is
 * LFU based.
 * returns REDISMODULE_OK if when key is valid. */
int RedisModule_GetLRU(RedisModuleKey *key, mstime_t *lru_idle);

/* Set the key access frequency. only relevant if the server's maxmemory policy
 * is LFU based.
 * The frequency is a logarithmic counter that provides an indication of
 * the access frequencyonly (must be <= 255).
 * returns REDISMODULE_OK if the LFU was updated, REDISMODULE_ERR otherwise. */
int RedisModule_SetLFU(RedisModuleKey *key, long long lfu_freq);

/* Gets the key access frequency or -1 if the server's eviction policy is not
 * LFU based.
 * returns REDISMODULE_OK if when key is valid. */
int RedisModule_GetLFU(RedisModuleKey *key, long long *lfu_freq);