VALKEY-MODULES-API-REF(7)
๐ VALKEY-MODULES-API-REF ๐
okay so bestie, welcome to the manual page for valkey-modules-api-ref โ it's part of the Valkey manual (section 7, no cap) and it is literally the entire Modules API reference. it's long. it's iconic. grab a snack ๐ and let's go.
๐ธ NAME ๐ธ
valkey-modules-api-ref โ Modules API reference
๐ DESCRIPTION ๐
โจ Sections โจ
here's the whole menu, bestie. every single one of these is in here somewhere ๐
- Heap allocation raw functions
- Commands API
- Module information and time measurement
- Automatic memory management for modules
- String objects APIs
- Reply APIs
- Commands replication API
- DB and Key APIs โ Generic API
- Key API for String type
- Key API for List type
- Key API for Sorted Set type
- Key API for Sorted Set iterator
- Key API for Hash type
- Key API for Stream type
- Calling commands from modules
- Modules data types
- RDB loading and saving functions
- Key digest API (DEBUG DIGEST interface for modules types)
- AOF API for modules data types
- IO context handling
- Logging
- Blocking clients from modules
- Thread Safe Contexts
- Module Keyspace Notifications API
- Modules Cluster API
- Modules Timers API
- Modules EventLoop API
- Modules ACL API
- Modules Dictionary API
- Modules Info fields
- Modules utility APIs
- Modules API exporting / importing
- Module Command Filter API
- Scanning keyspace and hashes
- Module fork API
- Server hooks implementation
- Module Configurations API
- RDB load/save API
- Key eviction API
- Miscellaneous APIs
- Defrag API
- Function index
๐ง Heap allocation raw functions ๐ง
the vibe here: anything you allocate with these functions gets counted by the key eviction algorithms and shows up in memory usage info. so the server actually knows about it. it's giving "responsible memory citizen". โจ
ValkeyModule_Alloc
void *ValkeyModule_Alloc(size_t bytes);
Available since: 4.0.0
use it like malloc(), bestie. memory you grab with this one gets reported in INFO memory, it gets used for keys eviction according to maxmemory settings, and like, generally the server counts it as memory it allocated. so pls avoid using plain malloc(). also heads up: this function panics if it can't allocate enough memory. ๐ญ
ValkeyModule_TryAlloc
void *ValkeyModule_TryAlloc(size_t bytes);
Available since: 7.0.0
same energy as ValkeyModule_Alloc, except it returns NULL when allocation fails instead of panicking. so much chiller. ๐ซถ
ValkeyModule_Calloc
void *ValkeyModule_Calloc(size_t nmemb, size_t size);
Available since: 4.0.0
use it like calloc(). memory from this one is reported in INFO memory, is used for keys eviction according to maxmemory settings, and in general is taken into account as memory allocated by the server. pls don't use calloc() directly. ๐
ValkeyModule_TryCalloc
void *ValkeyModule_TryCalloc(size_t nmemb, size_t size);
Available since: 8.0.0
like ValkeyModule_Calloc but it hands you NULL on allocation failure instead of panicking. โจ
ValkeyModule_Realloc
void *ValkeyModule_Realloc(void *ptr, size_t bytes);
Available since: 4.0.0
use it like realloc(), but for memory you got from ValkeyModule_Alloc(). ๐
ValkeyModule_TryRealloc
void *ValkeyModule_TryRealloc(void *ptr, size_t bytes);
Available since: 8.0.0
like ValkeyModule_Realloc but returns NULL on allocation failure instead of panicking. ๐
ValkeyModule_Free
void ValkeyModule_Free(void *ptr);
Available since: 4.0.0
use it like free(), for memory obtained by ValkeyModule_Alloc() and ValkeyModule_Realloc().
โ ๏ธ but bestie, never ever try to free memory that you allocated withmalloc()inside your module usingValkeyModule_Free(). that's a whole mess. not the move. ๐
ValkeyModule_Strdup
char *ValkeyModule_Strdup(const char *str);
Available since: 4.0.0
like strdup() but the memory it returns was allocated with ValkeyModule_Alloc(). cute and consistent. ๐
ValkeyModule_PoolAlloc
void *ValkeyModule_PoolAlloc(ValkeyModuleCtx *ctx, size_t bytes);
Available since: 4.0.0
returns heap allocated memory that gets freed automatically when the module callback function returns. ๐ฆ mostly for small, short-living allocations that have to be released when the callback returns anyway. it's giving "set it and forget it".
the returned memory is aligned to the architecture word size if at least word size bytes are requested, otherwise it's just aligned to the next power of two. so for example a 3 bytes request is 4 bytes aligned, while a 2 bytes request is 2 bytes aligned. numbers don't lie. ๐ข
there's no realloc style function, because when you need realloc the pool allocator is not a good idea. periodt.
the function returns NULL if bytes is 0.
๐ Commands API ๐
these functions are used to implement custom commands. for examples, go peek at valkey-modules-intro(7). ๐
ValkeyModule_IsKeysPositionRequest
int ValkeyModule_IsKeysPositionRequest(ValkeyModuleCtx *ctx);
Available since: 4.0.0
returns non-zero if a module command that was declared with the flag โgetkeys-apiโ is called in a special way to get the keys positions and not to get executed. otherwise zero comes back. ๐ฏ
ValkeyModule_KeyAtPosWithFlags
void ValkeyModule_KeyAtPosWithFlags(ValkeyModuleCtx *ctx, int pos, int flags);
Available since: 7.0.0
so when a module command is called in order to obtain the position of keys โ because it was flagged as โgetkeys-apiโ during registration โ the command implementation checks for this special call using the ValkeyModule_IsKeysPositionRequest() API and then uses this function to report keys. ๐
the supported flags are the ones used by ValkeyModule_SetCommandInfo, see VALKEYMODULE_CMD_KEY_*.
here's how it could be used, bestie:
if (ValkeyModule_IsKeysPositionRequest(ctx)) {
ValkeyModule_KeyAtPosWithFlags(ctx, 2, VALKEYMODULE_CMD_KEY_RO | VALKEYMODULE_CMD_KEY_ACCESS);
ValkeyModule_KeyAtPosWithFlags(ctx, 1, VALKEYMODULE_CMD_KEY_RW | VALKEYMODULE_CMD_KEY_UPDATE |
VALKEYMODULE_CMD_KEY_ACCESS); }
๐ note: in the example above, the get keys API could have been handled by key-specs (preferred). implementing the getkeys-api is required only when it is not possible to declare key-specs that cover all keys.
ValkeyModule_KeyAtPos
void ValkeyModule_KeyAtPos(ValkeyModuleCtx *ctx, int pos);
Available since: 4.0.0
this API existed before ValkeyModule_KeyAtPosWithFlags was added. it's now deprecated and can be used for compatibility with older versions โ you know, back before key-specs and flags were introduced. a little vintage moment. ๐ฐ๏ธ
ValkeyModule_IsChannelsPositionRequest
int ValkeyModule_IsChannelsPositionRequest(ValkeyModuleCtx *ctx);
Available since: 7.0.0
returns non-zero if a module command that was declared with the flag โgetchannels-apiโ is called in a special way to get the channel positions and not to get executed. otherwise zero. ๐ก
ValkeyModule_ChannelAtPosWithFlags
void ValkeyModule_ChannelAtPosWithFlags(ValkeyModuleCtx *ctx,
int pos,
int flags);
Available since: 7.0.0
when a module command is called in order to obtain the position of channels โ since it was flagged as โgetchannels-apiโ during registration โ the command implementation checks for this special call using the ValkeyModule_IsChannelsPositionRequest() API and uses this function to report the channels. ๐ข
the supported flags are:
VALKEYMODULE_CMD_CHANNEL_SUBSCRIBE: this command will subscribe to the channel.VALKEYMODULE_CMD_CHANNEL_UNSUBSCRIBE: this command will unsubscribe from this channel.VALKEYMODULE_CMD_CHANNEL_PUBLISH: this command will publish to this channel.VALKEYMODULE_CMD_CHANNEL_PATTERN: instead of acting on a specific channel, it'll act on any channel specified by the pattern. this is the same access used by thePSUBSCRIBEandPUNSUBSCRIBEcommands. not intended to be used withPUBLISHpermissions.
here's an example of how it could be used:
if (ValkeyModule_IsChannelsPositionRequest(ctx)) {
ValkeyModule_ChannelAtPosWithFlags(ctx, 1, VALKEYMODULE_CMD_CHANNEL_SUBSCRIBE | VALKEYMODULE_CMD_CHANNEL_PATTERN);
ValkeyModule_ChannelAtPosWithFlags(ctx, 1, VALKEYMODULE_CMD_CHANNEL_PUBLISH);
}
๐ note: one usage of declaring channels is for evaluating ACL permissions. in this context, unsubscribing is always allowed, so commands will only be checked against subscribe and publish permissions. this is preferred over using ValkeyModule_ACLCheckChannelPermissions, since it allows the ACLs to be checked before the command is executed. โจ
ValkeyModule_CreateCommand
int ValkeyModule_CreateCommand(ValkeyModuleCtx *ctx,
const char *name,
ValkeyModuleCmdFunc cmdfunc,
const char *strflags,
int firstkey,
int lastkey,
int keystep);
Available since: 4.0.0
registers a new command in the server, which will be handled by calling the function pointer cmdfunc using the ValkeyModule calling convention. ๐ ๏ธ
the function returns VALKEYMODULE_ERR in these cases:
- if creation of module command is called outside the
ValkeyModule_OnLoad. - the specified command is already busy.
- the command name contains some chars that are not allowed.
- a set of invalid flags were passed.
otherwise VALKEYMODULE_OK is returned and the new command is registered. ๐
this function must be called during the initialization of the module inside the ValkeyModule_OnLoad() function. calling this function outside of the initialization function is not defined. ๐
the command function type is the following:
int MyCommand_ValkeyCommand(ValkeyModuleCtx *ctx, ValkeyModuleString **argv, int argc);
and it's supposed to always return VALKEYMODULE_OK.
the set of flags strflags specify the behavior of the command, and should be passed as a C string composed of space separated words, like for example โwrite deny-oomโ. the set of flags are:
- โ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 might give you a totally different result even when you feed it the exact same args and keys. Starting from Redis OSS 7.0 this flag is deprecated (rip). If you wanna declare a command asโrandomโnow, do it with command tips, k? โ https://valkey.io/topics/command-tips โจโallow-staleโโ lets the command run on replicas that donโt serve stale data. ๐ซถ Donโt use if you donโt know what this means. like fr, if you have to ask, donโt touch it.โno-monitorโโ donโt propagate the command onmonitor. Use this when the command has sensitive data hiding among its arguments. ๐โno-slowlogโโ Deprecated, please useโno-commandlogโinstead. itโs giving old news ๐โno-commandlogโโ donโt log this command in the commandlog. Same energy as above: use it when thereโs sensitive data among the arguments. ๐โfastโโ the commandโs time complexity is not greater thanO(log(N)), where N is the size of the collection (or literally anything else that represents the normal scalability issue with the command). โกโgetkeys-apiโโ the command implements the interface to return the arguments that are keys. Use this when start/stop/step just isnโt enough because of the command syntax. ๐๏ธโno-clusterโโ the command should NOT register in Cluster, since itโs not designed to work with it. why? maybe it canโt report the position of the keys, or it programmatically creates key names, or any other reason honestly. ๐ซโno-authโโ this command can be run by an un-authenticated client. Normally this is what a command that authenticates a client would use. ๐โmay-replicateโโ this command may generate replication traffic, even though itโs not a write command. ๐โno-mandatory-keysโโ all the keys this command may take are optional. no pressure bestie ๐โblockingโโ the command has the potential to block the client. ๐ดโallow-busyโโ permit the command while the server is blocked, either by a script or by a slow module command, seeValkeyModule_Yield. ๐ซถโgetchannels-apiโโ the command implements the interface to return the arguments that are channels. ๐ก
The last three parameters specify which arguments of the new command are keys. See command(3valkey) for more info. ๐
firstkeyโ one-based index of the first argument thatโs a key. Position0is always the command name itself.0for commands with no keys.lastkeyโ one-based index of the last argument thatโs a key. Negative numbers refer to counting backwards from the last argument (-1means the last argument provided).0for commands with no keys.keystepโ step between first and last key indexes.0for commands with no keys.
This information is used by ACL, Cluster and the COMMAND command. โจ
NOTE: The scheme described above serves a limited purpose and can only be used to find keys that exist at constant indices. For non-trivial key arguments, you may pass0,0,0and useValkeyModule_SetCommandInfoto set key specs using a more advanced scheme, and useValkeyModule_SetCommandACLCategoriesto set ACL categories of the commands. ๐ง
โจ ValkeyModule_GetCommand โจ
ValkeyModuleCommand *ValkeyModule_GetCommand(ValkeyModuleCtx *ctx,
const char *name);
Available since: 7.0.0
Get an opaque structure, representing a module command, by command name. This structure is used in some of the command-related APIs. ๐
NULL is returned in case of the following errors:
- Command not found
- The command is not a module command
- The command doesnโt belong to the calling module
๐ ValkeyModule_CreateSubcommand ๐
int ValkeyModule_CreateSubcommand(ValkeyModuleCommand *parent,
const char *name,
ValkeyModuleCmdFunc cmdfunc,
const char *strflags,
int firstkey,
int lastkey,
int keystep);
Available since: 7.0.0
Very similar to ValkeyModule_CreateCommand, except that itโs used to create a subcommand, associated with another, container, command. ๐ช
Example: If a module has a configuration command, MODULE.CONFIG, then GET and SET should be individual subcommands, while MODULE.CONFIG is a command, but should not be registered with a valid funcptr:
if (ValkeyModule_CreateCommand(ctx,"module.config",NULL,"",0,0,0) == VALKEYMODULE_ERR)
return VALKEYMODULE_ERR;
ValkeyModuleCommand *parent = ValkeyModule_GetCommand(ctx,,"module.config");
if (ValkeyModule_CreateSubcommand(parent,"set",cmd_config_set,"",0,0,0) == VALKEYMODULE_ERR)
return VALKEYMODULE_ERR;
if (ValkeyModule_CreateSubcommand(parent,"get",cmd_config_get,"",0,0,0) == VALKEYMODULE_ERR)
return VALKEYMODULE_ERR;
Returns VALKEYMODULE_OK on success and VALKEYMODULE_ERR in case of the following errors:
- Error while parsing
strflags - Command is marked as
no-clusterbut cluster mode is enabled parentis already a subcommand (we do not allow more than one level of command nesting)parentis a command with an implementation (ValkeyModuleCmdFunc) (a parent command should be a pure container of subcommands)parentalready has a subcommand calledname- Creating a subcommand is called outside of
ValkeyModule_OnLoad
๐ ValkeyModule_AddACLCategory ๐
int ValkeyModule_AddACLCategory(ValkeyModuleCtx *ctx, const char *name);
Available since: 8.0.0
ValkeyModule_AddACLCategory can be used to add new ACL command categories. Category names can only contain alphanumeric characters, underscores, or dashes. Categories can only be added during the ValkeyModule_OnLoad function. Once a category has been added, it can not be removed. Any module can register a command to any added categories using ValkeyModule_SetCommandACLCategories. ๐๏ธ
Returns:
VALKEYMODULE_OKon successfully adding the new ACL category.VALKEYMODULE_ERRon failure.
On error the errno is set to:
EINVALif the name contains invalid characters.EBUSYif the category name already exists.ENOMEMif the number of categories reached the max limit of64categories.
๐ ValkeyModule_SetCommandACLCategories ๐
int ValkeyModule_SetCommandACLCategories(ValkeyModuleCommand *command,
const char *aclflags);
Available since: 7.2.0
ValkeyModule_SetCommandACLCategories can be used to set ACL categories to module commands and subcommands. The set of ACL categories should be passed as a space separated C string aclflags. ๐ธ
Example, the acl flags write slow marks the command as part of the write and slow ACL categories. ๐
On success VALKEYMODULE_OK is returned. On error VALKEYMODULE_ERR is returned. ๐ฅ
This function can only be called during the ValkeyModule_OnLoad function. If called outside of this function, an error is returned. โ ๏ธ
๐ ValkeyModule_SetCommandInfo ๐
int ValkeyModule_SetCommandInfo(ValkeyModuleCommand *command,
const ValkeyModuleCommandInfo *info);
Available since: 7.0.0
Set additional command information. ๐
Affects the output of COMMAND, COMMAND INFO and COMMAND DOCS, Cluster, ACL, and is used to filter commands with the wrong number of arguments before the call reaches the module code. ๐คฏ
This function can be called after creating a command using ValkeyModule_CreateCommand and fetching the command pointer using ValkeyModule_GetCommand. The information can only be set once for each command and has the following structure:
typedef struct ValkeyModuleCommandInfo {
const ValkeyModuleCommandInfoVersion *version;
const char *summary;
const char *complexity;
const char *since;
ValkeyModuleCommandHistoryEntry *history;
const char *tips;
int arity;
ValkeyModuleCommandKeySpec *key_specs;
ValkeyModuleCommandArg *args;
} ValkeyModuleCommandInfo;
All fields except version are optional. Explanation of the fields:
versionโ This field enables compatibility with different server versions. Always set this field toVALKEYMODULE_COMMAND_INFO_VERSION. ๐ซถsummaryโ A short description of the command (optional).complexityโ Complexity description (optional).sinceโ The version where the command was introduced (optional). Note: The version specified should be the moduleโs, not the server version. ๐historyโ An array ofValkeyModuleCommandHistoryEntry(optional), which is a struct with the following fields:
const char *since;
const char *changes;
since is a version string and changes is a string describing the changes. The array is terminated by a zeroed entry, i.e. an entry with both strings set to NULL. โ๏ธ
tipsโ A string of space-separated tips regarding this command, meant for clients and proxies. Seevalkey-command-tips(7). ๐กarityโ Number of arguments, including the command name itself. A positive number specifies an exact number of arguments and a negative number specifies a minimum number of arguments, so use-Nto say>= N. The server validates a call before passing it to a module, so this can replace an arity check inside the module command implementation. A value of0(or an omittedarityfield) is equivalent to-2if the command has sub commands and-1otherwise. ๐ขkey_specsโ An array ofValkeyModuleCommandKeySpec, terminated by an element memset to zero. This is a scheme that tries to describe the positions of key arguments better than the oldValkeyModule_CreateCommandargumentsfirstkey,lastkey,keystepand is needed if those three are not enough to describe the key positions. There are two steps to retrieve key positions: begin search (BS) in which index should find the first key and find keys (FK) which, relative to the output of BS, describes how can we will which arguments are keys. Additionally, there are key specific flags. ๐บ๏ธ
Key-specs cause the triplet (firstkey, lastkey, keystep) given in ValkeyModule_CreateCommand to be recomputed, but it is still useful to provide these three parameters in ValkeyModule_CreateCommand, to better support old server versions where ValkeyModule_SetCommandInfo is not available. ๐ฐ๏ธ
Note that key-specs donโt fully replace the โgetkeys-apiโ (see ValkeyModule_CreateCommand, ValkeyModule_IsKeysPositionRequest and ValkeyModule_KeyAtPosWithFlags) so it may be a good idea to supply both key-specs and implement the getkeys-api. ๐ซถ
A key-spec has the following structure:
typedef struct ValkeyModuleCommandKeySpec {
const char *notes;
uint64_t flags;
ValkeyModuleKeySpecBeginSearchType begin_search_type;
union {
struct {
int pos;
} index;
struct {
const char *keyword;
int startfrom;
} keyword;
} bs;
ValkeyModuleKeySpecFindKeysType find_keys_type;
union {
struct {
int lastkey;
int keystep;
int limit;
} range;
struct {
int keynumidx;
int firstkey;
int keystep;
} keynum;
} fk;
} ValkeyModuleCommandKeySpec;
okay so here's the tea on every single field of ValkeyModuleCommandKeySpec bestie, buckle up ๐ซถ
notesโ optional lil notes or clarifications about this key spec. cute, extra, optional ๐flagsโ a bitwise or of the key-spec flags we're about to spill below.begin_search_typeโ this one describes how the first key gets discovered. there are two ways to figure out where the first key is:VALKEYMODULE_KSPEC_BS_UNKNOWNโ there's literally no way to tell where the key args start. mystery. unknown. we simply do not know ๐ญVALKEYMODULE_KSPEC_BS_INDEXโ key args start at a constant index.VALKEYMODULE_KSPEC_BS_KEYWORDโ key args start right after a specific keyword.
bsโ this is a union, and which branch you use (index or keyword) depends on the value of thebegin_search_typefield.bs.index.posโ the index from which we start the search for keys. (VALKEYMODULE_KSPEC_BS_INDEXonly!)bs.keyword.keywordโ the keyword (a string) that indicates the beginning of the key arguments. (VALKEYMODULE_KSPEC_BS_KEYWORDonly!)bs.keyword.startfromโ an index inargvfrom which to start searching. it can be negative, which means start the search from the end, in reverse. example:-2means start in reverse from the penultimate argument. (VALKEYMODULE_KSPEC_BS_KEYWORDonly!)
find_keys_typeโ after the "begin search" step, this describes which arguments are actually keys. the strategies are:VALKEYMODULE_KSPEC_BS_UNKNOWNโ no way to tell where the key args are located. again, mystery ๐ถโ๐ซ๏ธVALKEYMODULE_KSPEC_FK_RANGEโ keys end at a specific index (or relative to the last argument).VALKEYMODULE_KSPEC_FK_KEYNUMโ there's an argument that contains the number of key args, and it lives somewhere before the keys themselves.
heads up bestie:find_keys_typeandfkcan be totally omitted if this keyspec describes exactly one key. one key? skip the whole thing ๐
fkโ another union! the range or keynum branch gets used depending on the value of thefind_keys_typefield.fk.range(forVALKEYMODULE_KSPEC_FK_RANGE): a struct with these fields:lastkeyโ index of the last key relative to the result of the begin search step. can be negative, in which case it's not relative.-1indicates the last argument,-2one before the last, and so on.keystepโ how many arguments should we skip after finding a key, so we can find the next one?limitโ iflastkeyis-1, we uselimitto stop the search by a factor.0and1mean no limit.2means 1/2 of the remaining args,3means 1/3, and so on.
fk.keynum(forVALKEYMODULE_KSPEC_FK_KEYNUM): a struct with these fields:keynumidxโ index of the argument containing the number of keys to come, relative to the result of the begin search step.firstkeyโ index of the fist key relative to the result of the begin search step. (usually it's just afterkeynumidx, in which case it should be set tokeynumidx + 1.)keystepโ how many arguments should we skip after finding a key, in order to find the next one?
โจ Key-spec flags โจ
The first four flags are about what the command actually does with the value or metadata of the key, and not necessarily the user data or how it affects it. each key-spec may must have exactly one of these. any operation that's not distinctly deletion, overwrite or read-only gets marked as RW. periodt ๐
VALKEYMODULE_CMD_KEY_ROโ Read-Only. reads the value of the key, but doesn't necessarily return it.VALKEYMODULE_CMD_KEY_RWโ Read-Write. modifies the data stored in the value of the key, or its metadata.VALKEYMODULE_CMD_KEY_OWโ Overwrite. overwrites the data stored in the value of the key.VALKEYMODULE_CMD_KEY_RMโ Deletes the key. gone. poof. ๐
The next four are about the user data inside the value of the key, not the metadata like LRU, type, or cardinality. they refer to the logical operation on the user's data (actual input strings or TTL), being used/returned/copied/changed. they do not refer to modification or returning of metadata (like type, count, presence of data). ACCESS can be combined with one of the write operations INSERT, DELETE or UPDATE. any write that's not an INSERT or a DELETE would be UPDATE. fr fr ๐ธ
VALKEYMODULE_CMD_KEY_ACCESSโ returns, copies or uses the user data from the value of the key.VALKEYMODULE_CMD_KEY_UPDATEโ updates data to the value; the new value may depend on the old value.VALKEYMODULE_CMD_KEY_INSERTโ adds data to the value with no chance of modification or deletion of existing data.VALKEYMODULE_CMD_KEY_DELETEโ explicitly deletes some content from the value of the key.
Other flags, the leftovers, still iconic ๐
VALKEYMODULE_CMD_KEY_NOT_KEYโ the key is not actually a key, but it should be routed in cluster mode as if it was a key.VALKEYMODULE_CMD_KEY_INCOMPLETEโ the keyspec might not point out all the keys it should cover.VALKEYMODULE_CMD_KEY_VARIABLE_FLAGSโ some keys might have different flags depending on arguments.
argsโ an array ofValkeyModuleCommandArg, terminated by an element memset to zero.ValkeyModuleCommandArgis a structure with the fields described below ๐
typedef struct ValkeyModuleCommandArg {
const char *name;
ValkeyModuleCommandArgType type;
int key_spec_index;
const char *token;
const char *summary;
const char *since;
int flags;
struct ValkeyModuleCommandArg *subargs;
} ValkeyModuleCommandArg;
Explanation of the fields, bestie:
nameโ name of the argument.typeโ the type of the argument. see below for the deets. the typesVALKEYMODULE_ARG_TYPE_ONEOFandVALKEYMODULE_ARG_TYPE_BLOCKrequire an argument to have sub-arguments, akasubargs.key_spec_indexโ if the type isVALKEYMODULE_ARG_TYPE_KEYyou must provide the index of the key-spec associated with this argument. seekey_specsabove. if the argument is not a key, you may specify-1.tokenโ the token preceding the argument (optional). example: the argumentsecondsinSEThas a tokenEX. if the argument consists of only a token (for exampleNXinSET) the type should beVALKEYMODULE_ARG_TYPE_PURE_TOKENandvalueshould beNULL.summaryโ a short description of the argument (optional).sinceโ the first version which included this argument (optional).flagsโ a bitwise or of the macrosVALKEYMODULE_CMD_ARG_*. see below.valueโ the display-value of the argument. this string is what should be displayed when creating the command syntax from the output ofCOMMAND. iftokenis notNULL, it should also be displayed.
Explanation of ValkeyModuleCommandArgType ๐ซ
VALKEYMODULE_ARG_TYPE_STRINGโ string argument.VALKEYMODULE_ARG_TYPE_INTEGERโ integer argument.VALKEYMODULE_ARG_TYPE_DOUBLEโ double-precision float argument.VALKEYMODULE_ARG_TYPE_KEYโ string argument representing a keyname.VALKEYMODULE_ARG_TYPE_PATTERNโ string, but regex pattern.VALKEYMODULE_ARG_TYPE_UNIX_TIMEโ integer, but Unix timestamp.VALKEYMODULE_ARG_TYPE_PURE_TOKENโ argument doesn't have a placeholder. it's just a token without a value. example: theKEEPTTLoption of theSETcommand.VALKEYMODULE_ARG_TYPE_ONEOFโ used when the user can choose only one of a few sub-arguments. requiressubargs. example: theNXandXXoptions ofSET.VALKEYMODULE_ARG_TYPE_BLOCKโ used when one wants to group together several sub-arguments, usually to apply something on all of them, like making the entire group "optional". requiressubargs. example: theLIMIT offset countparameters inZRANGE.
Explanation of the command argument flags ๐ฅ
VALKEYMODULE_CMD_ARG_OPTIONALโ the argument is optional (likeGETin theSETcommand).VALKEYMODULE_CMD_ARG_MULTIPLEโ the argument may repeat itself (likekeyinDEL).VALKEYMODULE_CMD_ARG_MULTIPLE_TOKENโ the argument may repeat itself, and so does its token (likeGET patterninSORT).
On success VALKEYMODULE_OK is returned. on error VALKEYMODULE_ERR is returned and errno is set to EINVAL if invalid info was provided, or EEXIST if info has already been set. if the info is invalid, a warning is logged explaining which part of the info is invalid and why. so if something's off, go read the log, bestie, it's telling on you ๐
ValkeyModule_UpdateRuntimeArgs ๐ฆ
int ValkeyModule_UpdateRuntimeArgs(ValkeyModuleCtx *ctx,
ValkeyModuleString **argv,
int argc);
Available since: 8.1.0
ValkeyModule_UpdateRuntimeArgs can be used to update the module argument values. the function parameter argc indicates the number of updated arguments, and argv represents the values of the updated arguments. once CONFIG REWRITE command is called, the updated argument values can be saved into conf file. so like, update now, persist later ๐
the function always returns VALKEYMODULE_OK.
โฐ Module information and time measurement โฐ
ValkeyModule_IsModuleNameBusy ๐
int ValkeyModule_IsModuleNameBusy(const char *name);
Available since: 4.0.3
Return non-zero if the module name is busy. otherwise zero is returned. simple as that ๐ซถ
ValkeyModule_Milliseconds โญ
mstime_t ValkeyModule_Milliseconds(void);
Available since: 4.0.0
Return the current UNIX time in milliseconds. ticking away, main character energy โจ
ValkeyModule_MonotonicMicroseconds ๐
uint64_t ValkeyModule_MonotonicMicroseconds(void);
Available since: 7.0.0
Return counter of micro-seconds relative to an arbitrary point in time. not a real clock, just vibes (but consistent vibes) ๐
ValkeyModule_Microseconds โฐ
ustime_t ValkeyModule_Microseconds(void);
Available since: 7.2.0
Okay so this one hands you back the current UNIX time in microseconds. That's it, that's the whole vibe. No cap.
ValkeyModule_CachedMicroseconds ๐ฐ๏ธ
ustime_t ValkeyModule_CachedMicroseconds(void);
Available since: 7.2.0
This returns the cached UNIX time in microseconds, bestie. It gets updated in the server cron job and also right before a command executes. Why do you care? Because when your call stack goes absolutely feral โ like a command triggering a key space notification, which makes a module run ValkeyModule_Call, which triggers another notification, and so on forever โ it just makes sense that all those callbacks read from the same clock. Consistency, queen. ๐
ValkeyModule_BlockedClientMeasureTimeStart โฑ๏ธ
int ValkeyModule_BlockedClientMeasureTimeStart(ValkeyModuleBlockedClient *bc);
Available since: 6.2.0
Marks a point in time that's gonna be used as the start time for calculating the elapsed execution time when ValkeyModule_BlockedClientMeasureTimeEnd() gets called. Inside the same command you can call ValkeyModule_BlockedClientMeasureTimeStart() and ValkeyModule_BlockedClientMeasureTimeEnd() multiple times to accumulate independent time intervals into the background duration. And this method always returns VALKEYMODULE_OK. Always. ๐
๐ซถ Heads up: this function is not thread safe. If you're using it in a module thread and a blocked callback (possibly the main thread) at the same time, it's recommended to protect them with a lock owned by the caller instead of the GIL.
ValkeyModule_BlockedClientMeasureTimeEnd ๐
int ValkeyModule_BlockedClientMeasureTimeEnd(ValkeyModuleBlockedClient *bc);
Available since: 6.2.0
Marks a point in time used as the end time for calculating the elapsed execution time. On success you get VALKEYMODULE_OK back. It only ever returns VALKEYMODULE_ERR if no start time was previously defined โ meaning ValkeyModule_BlockedClientMeasureTimeStart was never called. ๐ญ
๐ซถ Same warning as above, fr fr: this function is not thread safe. If it's used in a module thread and a blocked callback (possibly the main thread) simultaneously, protect them with a lock owned by the caller instead of the GIL.
ValkeyModule_Yield ๐ฌ๏ธ
void ValkeyModule_Yield(ValkeyModuleCtx *ctx,
int flags,
const char *busy_reply);
Available since: 7.0.0
This API lets modules give the server a lil breather โ it allows the server to process background tasks, and some commands, during a long blocking execution of a module command. Your module can just call this API periodically. The flags is a bit mask of these:
VALKEYMODULE_YIELD_FLAG_NONEโ no special flags, it can perform some background operations, but it will not process client commands.VALKEYMODULE_YIELD_FLAG_CLIENTSโ the server can also process client commands. ๐
The busy_reply argument is optional, and it's how you control the verbose error string that shows up after the -BUSY error code.
So when VALKEYMODULE_YIELD_FLAG_CLIENTS is used, the server will only start processing client commands after the time defined by the busy-reply-threshold config โ and in that case the server starts rejecting most commands with the -BUSY error, but it allows the ones marked with the allow-busy flag to be executed. This API can also be used in thread safe context (while locked), and during loading โ in the rdb_load callback, in which case it'll reject commands with the -LOADING error. โจ
ValkeyModule_SetModuleOptions ๐๏ธ
void ValkeyModule_SetModuleOptions(ValkeyModuleCtx *ctx, int options);
Available since: 6.0.0
Sets flags that define capabilities or behavior โ bit flags, bestie.
VALKEYMODULE_OPTIONS_HANDLE_IO_ERRORSโ generally, modules don't need to bother with this, since the process will just terminate if a read error happens. But setting this flag would allowrepl-diskless-loadto work if it's enabled. The module should useValkeyModule_IsIOErrorafter reads, before using the data that was read, and in case of error it should propagate it upwards, and also be able to release the partially populated value and all its allocations.VALKEYMODULE_OPTION_NO_IMPLICIT_SIGNAL_MODIFIEDโ seeValkeyModule_SignalModifiedKey().VALKEYMODULE_OPTIONS_HANDLE_REPL_ASYNC_LOADโ setting this flag indicates module awareness of diskless async replication (repl-diskless-load=swapdb) and that the server could be serving reads during replication instead of blocking withLOADINGstatus.VALKEYMODULE_OPTIONS_ALLOW_NESTED_KEYSPACE_NOTIFICATIONSโ declares that the module wants to get nested key-space notifications. By default, the server will not fire key-space notifications that happened inside a key-space notification callback. This flag changes that behavior and fires nested key-space notifications. > โ ๏ธ Notice: if enabled, the module should protect itself from infinite recursion.VALKEYMODULE_OPTIONS_SKIP_COMMAND_VALIDATIONโ when set, this option allows the module to skip command validation. Useful in scenarios where the module needs to bypass command validation for specific operations to reduce overhead or handle trusted custom command logic.ValkeyModule_ReplicateandValkeyModule_EmitAOFare affected by this option, allowing them to operate without the command validation check.VALKEYMODULE_OPTIONS_HANDLE_ATOMIC_SLOT_MIGRATIONโ when set, this option indicates that the module is capable of handling atomic slot migration. If it's not set, the module is assumed to not be aware of atomic slot migration andCLUSTER MIGRATESLOTSwill return an error. Modules should set this flag if they understand keys may be loaded during the migration but before ownership is transferred.
ValkeyModule_SignalModifiedKey ๐ฃ
int ValkeyModule_SignalModifiedKey(ValkeyModuleCtx *ctx,
ValkeyModuleString *keyname);
Available since: 6.0.0
Signals that the key is modified from the user's perspective โ aka invalidate WATCH and client side caching. ๐
This is done automatically when a key opened for writing is closed, unless the option VALKEYMODULE_OPTION_NO_IMPLICIT_SIGNAL_MODIFIED has been set using ValkeyModule_SetModuleOptions().
๐ง Automatic memory management for modules ๐ง
ValkeyModule_AutoMemory ๐งน
void ValkeyModule_AutoMemory(ValkeyModuleCtx *ctx);
Available since: 4.0.0
Enables automatic memory management. Slay.
The function must be called as the first function of a command implementation that wants to use automatic memory.
When it's enabled, automatic memory management tracks and automatically frees keys, call replies and ValkeyModuleString objects once the command returns. In most cases this eliminates the need to call the following functions:
ValkeyModule_CloseKey()ValkeyModule_FreeCallReply()ValkeyModule_FreeString()
These functions can still be used with automatic memory management enabled, to optimize loops that make numerous allocations, for example. ๐
๐ String objects APIs ๐
ValkeyModule_CreateString ๐งต
ValkeyModuleString *ValkeyModule_CreateString(ValkeyModuleCtx *ctx,
const char *ptr,
size_t len);
Available since: 4.0.0
Creates a brand new module string object. The returned string must be freed with ValkeyModule_FreeString(), unless automatic memory is enabled.
The string is created by copying the len bytes starting at ptr. No reference is retained to the passed buffer โ it copies and moves on, iconic behavior honestly.
The module context ctx is optional and may be NULL if you want to create a string out of the context scope. But in that case, automatic memory management will not be available, and the string memory must be managed manually. ๐ญ
ValkeyModule_CreateStringPrintf โ๏ธ
ValkeyModuleString *ValkeyModule_CreateStringPrintf(ValkeyModuleCtx *ctx,
const char *fmt,
...);
Available since: 4.0.0
Creates a new module string object from a printf format and arguments. The returned string must be freed with ValkeyModule_FreeString(), unless automatic memory is enabled.
The string is created using the sds formatter function sdscatvprintf().
The passed context ctx may be NULL if necessary โ see the ValkeyModule_CreateString() documentation for more info.
ValkeyModule_CreateStringFromLongLong ๐ข
ValkeyModuleString *ValkeyModule_CreateStringFromLongLong(ValkeyModuleCtx *ctx,
long long ll);
Available since: 4.0.0
Like ValkeyModule_CreateString(), but it creates a string starting from a long long integer instead of taking a buffer and its length. Same energy, different input. โจ
The returned string must be released with ValkeyModule_FreeString() or by enabling automatic memory management.
The passed context ctx may be NULL if necessary โ see the ValkeyModule_CreateString() documentation for more info.
ValkeyModule_CreateStringFromULongLong ๐ข
ValkeyModuleString *ValkeyModule_CreateStringFromULongLong(ValkeyModuleCtx *ctx,
unsigned long long ull);
Available since: 7.0.3
Like ValkeyModule_CreateString(), but it creates a string starting from an unsigned long long integer instead of taking a buffer and its length. Unsigned girlies, this one's for you. ๐ซถ
The returned string must be released with ValkeyModule_FreeString() or by enabling automatic memory management.
The passed context ctx may be NULL if necessary โ see the ValkeyModule_CreateString() documentation for more info.
ValkeyModule_CreateStringFromDouble ๐ข
ValkeyModuleString *ValkeyModule_CreateStringFromDouble(ValkeyModuleCtx *ctx,
double d);
Available since: 6.0.0
Like ValkeyModule_CreateString(), but it creates a string starting from a double instead of taking a buffer and its length.
The returned string must be released with ValkeyModule_FreeString() or by enabling automatic memory management.
ValkeyModule_CreateStringFromLongDouble ๐ข
ValkeyModuleString *ValkeyModule_CreateStringFromLongDouble(ValkeyModuleCtx *ctx,
long double ld,
int humanfriendly);
Available since: 6.0.0
Like ValkeyModule_CreateString(), but it creates a string starting from a long double. Extra precision, extra slay. ๐
The returned string must be released with ValkeyModule_FreeString() or by enabling automatic memory management.
The passed context ctx may be NULL if necessary โ see the ValkeyModule_CreateString() documentation for more info.
ValkeyModule_CreateStringFromString ๐ช
ValkeyModuleString *ValkeyModule_CreateStringFromString(ValkeyModuleCtx *ctx,
const ValkeyModuleString *str);
Available since: 4.0.0
It's literally ValkeyModule_CreateString(), bestie, but it makes the string from another ValkeyModuleString instead of from raw chars. โจ
And yeah โ that returned string? You gotta release it with ValkeyModule_FreeString() or just vibe with automatic memory management on. And that ctx you're passing? It can be NULL if you need it to be โ go peek at the ValkeyModule_CreateString() docs for the whole tea. ๐ซ
ValkeyModule_CreateStringFromStreamID
ValkeyModuleString *ValkeyModule_CreateStringFromStreamID(ValkeyModuleCtx *ctx,
const ValkeyModuleStreamID *id);
Available since: 6.2.0 ๐ธ
Makes a string out of a stream ID. That string you get back? Free it with ValkeyModule_FreeString() โ unless automatic memory is turned on, in which case the automation handles it for you. ๐
And the ctx you pass can be NULL if necessary. Same drill as always: check the ValkeyModule_CreateString() docs for more deets. ๐
ValkeyModule_FreeString
void ValkeyModule_FreeString(ValkeyModuleCtx *ctx, ValkeyModuleString *str);
Available since: 4.0.0 โญ
This one frees a module string object that you got from any of the module API calls that hand back brand-new string objects. It's giving cleanup era. ๐งน
Fun fact: you can still call this even when automatic memory management is enabled! In that case the string gets released ASAP and gets yeeted off the pool of strings-to-release-at-the-end. ๐
If the string was created with a NULL context ctx, then you can also pass ctx as NULL when releasing it (though passing a context won't cause any issue either). But listen: strings created with a context should be freed also passing the context โ so if you're planning to free a string out-of-context later, make sure you created it using a NULL context in the first place. Plan ahead, queen. ๐
โ ๏ธ This API is not thread safe. If these retained strings originated from a client command's arguments, accessing them must be done with the GIL locked.
ValkeyModule_RetainString
void ValkeyModule_RetainString(ValkeyModuleCtx *ctx, ValkeyModuleString *str);
Available since: 4.0.0 ๐
Every single call to this function makes the string str require one additional call to ValkeyModule_FreeString() before it's actually, truly, fully freed. And heads up: the automatic freeing you get from enabling modules' automatic memory management counts as one ValkeyModule_FreeString() call โ it's just executed automatically for you. ๐ช
So like, when do you actually want this? You want it when all three of these are true at the same time:
- You have automatic memory management enabled.
- You want to create string objects.
- Those string objects you create need to live on after the callback function that created them (a command implementation, say) returns. ๐
Usually you're doing this so you can stash the created string object inside your own data structure โ like when you're implementing a whole new data type. Main character data type behavior, honestly. ๐
And note: when memory management is turned off, you don't need any RetainString() call at all โ because creating a string will always give you a string that lives past the callback's return, as long as no FreeString() call happens. So there's nothing to retain, bestie. ๐ฆ
Oh and you can call this with a NULL context too. ๐
When strings are gonna be retained for a long-ass time, it's good practice to also call ValkeyModule_TrimStringAllocation() to optimize memory usage. ๐ธ
Threaded modules that reference retained strings from other threads must explicitly trim the allocation as soon as the string is retained. Not doing so may result in automatic trimming โ and that is not thread safe. ๐ญ
โ ๏ธ This API is not thread safe. Access to these retained strings (if they came from client command arguments) must be done with the GIL locked.
ValkeyModule_HoldString
ValkeyModuleString *ValkeyModule_HoldString(ValkeyModuleCtx *ctx,
ValkeyModuleString *str);
Available since: 6.0.7 โจ
You can use this instead of ValkeyModule_RetainString(). The big glow-up difference between the two? This one always succeeds, whereas ValkeyModule_RetainString() may fail because of an assertion. ๐ฌ No cap.
It returns a pointer to ValkeyModuleString that's owned by you, the caller. You need a call to ValkeyModule_FreeString() to free the string when automatic memory management is disabled for the context. And when automatic memory management is enabled? You can either call ValkeyModule_FreeString() or just let the automation free it. Your call, bestie. ๐ซถ
This function is more efficient than ValkeyModule_CreateStringFromString() because whenever possible it avoids copying the underlying ValkeyModuleString. Ate. ๐
The trade-off (there's always one ๐) is that it might not be possible to use ValkeyModule_StringAppendBuffer() on the ValkeyModuleString it returns.
You can call this with a NULL context. ๐
When strings are gonna be held for an extended duration, it's good practice to also call ValkeyModule_TrimStringAllocation() to optimize memory usage. ๐ธ
Threaded modules that reference held strings from other threads must explicitly trim the allocation as soon as the string is held. Not doing so may result in automatic trimming, which is not thread safe. ๐ญ
โ ๏ธ This API is not thread safe. Access to these retained strings (if they originated from client command arguments) must be done with the GIL locked.
ValkeyModule_StringPtrLen
const char *ValkeyModule_StringPtrLen(const ValkeyModuleString *str,
size_t *len);
Available since: 4.0.0 ๐
Hand it a string module object and it gives you back the string pointer and the length of the string. Important though: that returned pointer and length are for read only accesses only โ never, ever modify them. Look, don't touch. ๐
ValkeyModule_StringToLongLong
int ValkeyModule_StringToLongLong(const ValkeyModuleString *str, long long *ll);
Available since: 4.0.0 โญ
Converts the string into a long long integer and stores it at *ll. Returns VALKEYMODULE_OK on success. And if the string can't be parsed as a valid, strict long long (meaning no spaces before or after, bestie โ zero tolerance ๐), then VALKEYMODULE_ERR is returned.
ValkeyModule_StringToULongLong
int ValkeyModule_StringToULongLong(const ValkeyModuleString *str,
unsigned long long *ull);
Available since: 7.0.3 โจ
Converts the string into an unsigned long long integer and stores it at *ull. Returns VALKEYMODULE_OK on success. If the string can't be parsed as a valid, strict unsigned long long (again: no spaces before/after, periodt), VALKEYMODULE_ERR comes back at you.
ValkeyModule_StringToDouble
int ValkeyModule_StringToDouble(const ValkeyModuleString *str, double *d);
Available since: 4.0.0 ๐ฆ
Converts the string into a double and stores it at *d. Returns VALKEYMODULE_OK on success, or VALKEYMODULE_ERR if the string isn't a valid string representation of a double value. Simple as. ๐
ValkeyModule_StringToLongDouble
int ValkeyModule_StringToLongDouble(const ValkeyModuleString *str,
long double *ld);
Available since: 6.0.0 ๐
Converts the string into a long double and stores it at *ld. Returns VALKEYMODULE_OK on success, or VALKEYMODULE_ERR if the string isn't a valid string representation of a double value. (Yes, that's what it says โ quoting the source exactly, bestie. ๐)
ValkeyModule_StringToStreamID
int ValkeyModule_StringToStreamID(const ValkeyModuleString *str,
ValkeyModuleStreamID *id);
Available since: 6.2.0 ๐ธ
Converts the string into a stream ID and stores it at *id. Returns VALKEYMODULE_OK on success, and returns VALKEYMODULE_ERR if the string isn't a valid string representation of a stream ID. Oh and also: the special IDs "+" and "-" are allowed. Iconic little exceptions, love that for them. ๐
ValkeyModule_StringCompare
int ValkeyModule_StringCompare(const ValkeyModuleString *a,
const ValkeyModuleString *b);
Available since: 4.0.0 ๐ฅ
Compares two string objects and returns -1, 0 or 1 โ respectively for a < b, a == b, and a > b. The comparison is byte by byte, treating the two as binary blobs, with absolutely no encoding care and no collation attempt whatsoever. Raw bytes only, no thoughts head empty. ๐
ValkeyModule_StringAppendBuffer
int ValkeyModule_StringAppendBuffer(ValkeyModuleCtx *ctx,
ValkeyModuleString *str,
const char *buf,
size_t len);
Available since: 4.0.0 โญ
Appends the specified buffer to the string str. BUT โ the string has to be one you created, and it must be referenced only a single time. Otherwise VALKEYMODULE_ERR is returned and the operation doesn't happen at all. No half-appends, no drama. ๐ซ
ValkeyModule_TrimStringAllocation
void ValkeyModule_TrimStringAllocation(ValkeyModuleString *str);
Available since: 7.0.0 ๐ง
Trims any possible excess memory allocated for a ValkeyModuleString. โ๏ธ
Sometimes a ValkeyModuleString has more memory allocated to it than it actually needs โ typically for argv arguments that were constructed from network buffers. This function optimizes those strings by reallocating their memory, which is genuinely useful for strings that aren't short lived but instead get retained for an extended duration. ๐
โ ๏ธ This operation is not thread safe and should only be called when no concurrent access to the string is guaranteed. Using it on an argv string in a module command, before the string is potentially available to other threads, is generally safe.
Currently, the server may also automatically trim retained strings when a module command returns. But doing it explicitly should still be your preferred option, and here's why:
- Future versions of the server may abandon auto-trimming entirely. ๐
- Auto-trimming, as currently implemented, is not thread safe. A background thread manipulating a recently retained string may end up in a race condition with the auto-trim, which could result in data corruption. ๐ญ
Reply APIs
These functions are the ones you use for sending replies back to the client. ๐ฌ
Most of them always return VALKEYMODULE_OK, which means you can use them with return to bail out of your command implementation like this:
if (... some condition ...)
return ValkeyModule_ReplyWithLongLong(ctx,mycount);
Reply with collection functions
Once you've started a collection reply, the module has to make calls to other ReplyWith*-style functions in order to emit the elements of that collection. The collection types include: Array, Map, Set and Attribute. ๐
And when you're producing collections with a number of elements you don't know beforehand? You can call the function with the special flag VALKEYMODULE_POSTPONED_LEN (VALKEYMODULE_POSTPONED_ARRAY_LEN in the past, iykyk), and then set the actual number of elements later with a ValkeyModule_ReplySet*Length() call. Fun detail: that call sets the latest "open" count if there are multiple ones open at once. ๐
ValkeyModule_WrongArity
int ValkeyModule_WrongArity(ValkeyModuleCtx *ctx);
Available since: 4.0.0 ๐
Sends an error about the number of arguments given to the command, citing the command name right there in the error message. Returns VALKEYMODULE_OK. So when someone forgets their args, you can roast them automatically. ๐
Example:
if (argc != 3) return ValkeyModule_WrongArity(ctx);
๐ฌ ValkeyModule_ReplyWithLongLong
int ValkeyModule_ReplyWithLongLong(ValkeyModuleCtx *ctx, long long ll);
Available since: 4.0.0
Send an integer reply to the client, with the exact long long value you pass in. That's it, that's the whole vibe bestie ๐
The function always returns VALKEYMODULE_OK.
๐จ ValkeyModule_ReplyWithError
int ValkeyModule_ReplyWithError(ValkeyModuleCtx *ctx, const char *err);
Available since: 4.0.0
Reply with the error err. Yelling at the client, but make it protocol โจ
Heads up:errhas to contain the entire error, including the initial error code. The function literally only supplies the leading-for you, so the move is like this:
ValkeyModule_ReplyWithError(ctx,"ERR Wrong Type");
and not just:
ValkeyModule_ReplyWithError(ctx,"Wrong Type");
The function always returns VALKEYMODULE_OK.
๐จโจ ValkeyModule_ReplyWithErrorFormat
int ValkeyModule_ReplyWithErrorFormat(ValkeyModuleCtx *ctx,
const char *fmt,
...);
Available since: 7.2.0
Reply with an error built from a printf format plus its arguments. Same energy as above, just fancier because you get formatting ๐
Same rule though:fmtmust contain all of the error, initial error code included. The function only hands you the leading-, so:
ValkeyModule_ReplyWithErrorFormat(ctx,"ERR Wrong Type: %s",type);
and not just:
ValkeyModule_ReplyWithErrorFormat(ctx,"Wrong Type: %s",type);
The function always returns VALKEYMODULE_OK.
โจ ValkeyModule_ReplyWithSimpleString
int ValkeyModule_ReplyWithSimpleString(ValkeyModuleCtx *ctx, const char *msg);
Available since: 4.0.0
Reply with a simple string (+... \r\n in RESP protocol). These replies are only the move when you're sending a small, non-binary string with minimal overhead โ think "OK" or little replies like that. Don't be putting your whole novel in here bestie ๐
The function always returns VALKEYMODULE_OK.
๐ ValkeyModule_ReplyWithArray
int ValkeyModule_ReplyWithArray(ValkeyModuleCtx *ctx, long len);
Available since: 4.0.0
Reply with an array type of len elements.
Once you've started an array reply, the module has to make len calls to the other ReplyWith*-style functions to actually emit the elements of the array. Peep the Reply APIs section for the full deets.
Use ValkeyModule_ReplySetArrayLength() to set a deferred length (iykyk, we get to that below ๐
).
The function always returns VALKEYMODULE_OK.
๐บ๏ธ ValkeyModule_ReplyWithMap
int ValkeyModule_ReplyWithMap(ValkeyModuleCtx *ctx, long len);
Available since: 7.0.0
Reply with a RESP3 Map type of len pairs. Go visit valkey-protocol(7) for more on RESP3 ๐ฆ
Once you've kicked off a map reply, the module must make len*2 calls to other ReplyWith*-style functions to emit the elements of the map. See the Reply APIs section for more.
If the connected client is on RESP2, the reply gets converted into a flat array. No map for them, sorry ๐
Use ValkeyModule_ReplySetMapLength() to set a deferred length.
The function always returns VALKEYMODULE_OK.
๐งบ ValkeyModule_ReplyWithSet
int ValkeyModule_ReplyWithSet(ValkeyModuleCtx *ctx, long len);
Available since: 7.0.0
Reply with a RESP3 Set type of len elements. Again, valkey-protocol(7) has the RESP3 lore โจ
After starting a set reply, the module must make len calls to other ReplyWith*-style functions to emit the elements of the set. Check the Reply APIs section for more.
If the connected client is on RESP2, the reply gets converted to an array type.
Use ValkeyModule_ReplySetSetLength() to set a deferred length.
The function always returns VALKEYMODULE_OK.
๐ท๏ธ ValkeyModule_ReplyWithAttribute
int ValkeyModule_ReplyWithAttribute(ValkeyModuleCtx *ctx, long len);
Available since: 7.0.0
Add attributes (metadata) to the reply. This has to happen before you add the actual reply, okay? See valkey-protocol(7) ๐
After starting an attribute's reply, the module must make len*2 calls to other ReplyWith*-style functions to emit the elements of the attribute map. Reply APIs section, as always, has more.
Use ValkeyModule_ReplySetAttributeLength() to set a deferred length.
Not supported by RESP2 โ it'll return VALKEYMODULE_ERR. Otherwise the function always returns VALKEYMODULE_OK.
๐ณ๏ธ ValkeyModule_ReplyWithNullArray
int ValkeyModule_ReplyWithNullArray(ValkeyModuleCtx *ctx);
Available since: 6.0.0
Reply to the client with a null array โ simply null in RESP3, and a null array in RESP2. Two vibes, one function ๐
Note: in RESP3 there's no difference between a Null reply and a NullArray reply, so to dodge any ambiguity it's better to skip this API entirely and use ValkeyModule_ReplyWithNull instead. Not me telling you to use the other one ๐ญ
The function always returns VALKEYMODULE_OK.
๐ซ ValkeyModule_ReplyWithEmptyArray
int ValkeyModule_ReplyWithEmptyArray(ValkeyModuleCtx *ctx);
Available since: 6.0.0
Reply to the client with an empty array. Nothing in it, and honestly? Iconic minimalism ๐
The function always returns VALKEYMODULE_OK.
๐ ValkeyModule_ReplySetArrayLength
void ValkeyModule_ReplySetArrayLength(ValkeyModuleCtx *ctx, long len);
Available since: 4.0.0
So when ValkeyModule_ReplyWithArray() gets called with the argument VALKEYMODULE_POSTPONED_LEN โ because we don't know in advance how many items we're gonna output as elements of the array โ this function rolls in and sets that array length for you.
Since you can totally have multiple array replies pending with unknown length, this function guarantees it always sets the latest array length that was created in a postponed way. Last one wins, periodt ๐
For example, to output an array like [1,[10,20,30]] we could write:
ValkeyModule_ReplyWithArray(ctx,VALKEYMODULE_POSTPONED_LEN);
ValkeyModule_ReplyWithLongLong(ctx,1);
ValkeyModule_ReplyWithArray(ctx,VALKEYMODULE_POSTPONED_LEN);
ValkeyModule_ReplyWithLongLong(ctx,10);
ValkeyModule_ReplyWithLongLong(ctx,20);
ValkeyModule_ReplyWithLongLong(ctx,30);
ValkeyModule_ReplySetArrayLength(ctx,3); // Set len of 10,20,30 array.
ValkeyModule_ReplySetArrayLength(ctx,2); // Set len of top array
Ngl, in the example above there's genuinely no reason to postpone the array length โ we produce a fixed number of elements. But in real life your code might use an iterator or some other way of building the output, so calculating the number of elements in advance just isn't easy. That's when this slays ๐ฆ
๐ ValkeyModule_ReplySetMapLength
void ValkeyModule_ReplySetMapLength(ValkeyModuleCtx *ctx, long len);
Available since: 7.0.0
Very similar to ValkeyModule_ReplySetArrayLength, except len should be exactly half the number of ReplyWith* functions you called in the context of the map. Half, bestie, don't fumble it ๐ Visit valkey-protocol(7) for more info about RESP3.
๐ ValkeyModule_ReplySetSetLength
void ValkeyModule_ReplySetSetLength(ValkeyModuleCtx *ctx, long len);
Available since: 7.0.0
Very similar to ValkeyModule_ReplySetArrayLength. Visit valkey-protocol(7) for more info about RESP3 โจ
๐ท๏ธ๐ ValkeyModule_ReplySetAttributeLength
void ValkeyModule_ReplySetAttributeLength(ValkeyModuleCtx *ctx, long len);
Available since: 7.0.0
Very similar to ValkeyModule_ReplySetMapLength. Visit valkey-protocol(7) for more info about RESP3.
Must not be called if ValkeyModule_ReplyWithAttribute returned an error. Do not do it. I'm serious ๐ซถ
๐งต ValkeyModule_ReplyWithStringBuffer
int ValkeyModule_ReplyWithStringBuffer(ValkeyModuleCtx *ctx,
const char *buf,
size_t len);
Available since: 4.0.0
Reply with a bulk string, taking as input a C buffer pointer and a length. Give it the bytes, give it the size, done ๐ธ
The function always returns VALKEYMODULE_OK.
๐ค ValkeyModule_ReplyWithCString
int ValkeyModule_ReplyWithCString(ValkeyModuleCtx *ctx, const char *buf);
Available since: 5.0.6
Reply with a bulk string, taking as input a C buffer pointer that's assumed to be null-terminated. Trusting that \0 to show up fr fr ๐
The function always returns VALKEYMODULE_OK.
๐ ValkeyModule_ReplyWithString
int ValkeyModule_ReplyWithString(ValkeyModuleCtx *ctx, ValkeyModuleString *str);
Available since: 4.0.0
Reply with a bulk string, taking as input a ValkeyModuleString object. The fancy struct girlies get their moment ๐
The function always returns VALKEYMODULE_OK.
๐ซง ValkeyModule_ReplyWithEmptyString
int ValkeyModule_ReplyWithEmptyString(ValkeyModuleCtx *ctx);
Available since: 6.0.0
Reply with an empty string. Zero characters, maximum poise ๐
The function always returns VALKEYMODULE_OK.
๐ก ValkeyModule_ReplyWithVerbatimStringType
int ValkeyModule_ReplyWithVerbatimStringType(ValkeyModuleCtx *ctx,
const char *buf,
size_t len,
const char *ext);
Available since: 7.0.0
Reply with a binary safe string, which should not be escaped or filtered, taking as input a C buffer pointer, a length, and a 3 character type/extension. Three characters exactly, don't get creative โจ
The function always returns VALKEYMODULE_OK.
๐ ValkeyModule_ReplyWithVerbatimString
int ValkeyModule_ReplyWithVerbatimString(ValkeyModuleCtx *ctx,
const char *buf,
size_t len);
Available since: 6.0.0
Reply with a binary safe string, which should not be escaped or filtered, taking as input a C buffer pointer and a length. Raw bytes, no notes ๐ฆ
The function always returns VALKEYMODULE_OK.
๐ ValkeyModule_ReplyWithNull
int ValkeyModule_ReplyWithNull(ValkeyModuleCtx *ctx);
Available since: 4.0.0
Reply to the client with a NULL. The void. The absence. Very main character energy ๐
The function always returns VALKEYMODULE_OK.
โ
ValkeyModule_ReplyWithBool
int ValkeyModule_ReplyWithBool(ValkeyModuleCtx *ctx, int b);
Available since: 7.0.0
Reply with a RESP3 Boolean type. Visit valkey-protocol(7) for more info about RESP3.
In RESP3, this is a boolean type. In RESP2, it's a string response of "1" and "0" for true and false respectively. Same answer, different outfit ๐
The function always returns VALKEYMODULE_OK.
๐ ValkeyModule_ReplyWithCallReply
int ValkeyModule_ReplyWithCallReply(ValkeyModuleCtx *ctx,
ValkeyModuleCallReply *reply);
Available since: 4.0.0
Reply with exactly what a command returned to us via ValkeyModule_Call(). This function is the moment when you use ValkeyModule_Call() to execute some command and you just wanna hand the client the exact same reply you got back from that command. Copy, paste, slay ๐
Return: ๐
VALKEYMODULE_OKโ it worked, we slayed. โจVALKEYMODULE_ERRโ the reply you handed over is in RESP3 format but the client is out here expecting RESP2. ๐ญ And bestie, if that happens, it is the module writer's responsibility to translate the reply to RESP2 (or handle it differently by returning an error). No passing the buck, okay?
๐ซถ Tiny convenience note: you can pass0as the parameter to thefmtargument ofValkeyModule_Callso that theValkeyModuleCallReplycomes back in the same protocol (RESP2 or RESP3) as whatever is set in the current client's context. Easy peasy.
๐ธ ValkeyModule_ReplyWithDouble
int ValkeyModule_ReplyWithDouble(ValkeyModuleCtx *ctx, double d);
Available since: 4.0.0 โญ
Reply with a RESP3 Double type, bestie. Go visit valkey-protocol(7) for more info about RESP3. ๐
So what it does: it sends a string reply obtained by converting the double d into a bulk string. Basically it's equivalent to converting a double into a string into a C buffer, and then calling ValkeyModule_ReplyWithStringBuffer() with the buffer and length. Same energy, two steps. ๐
In RESP3 the string gets tagged as a double, while in RESP2 it's just a plain string that the user is gonna have to parse themselves. Not our problem, respectfully. ๐
The function always returns VALKEYMODULE_OK. Always. No drama. ๐
๐ฅ ValkeyModule_ReplyWithBigNumber
int ValkeyModule_ReplyWithBigNumber(ValkeyModuleCtx *ctx,
const char *bignum,
size_t len);
Available since: 7.0.0 ๐
Reply with a RESP3 BigNumber type. Again, valkey-protocol(7) has the RESP3 lore if you need it. โจ
In RESP3, this is a string of length len that is tagged as a BigNumber โ however, it's up to the caller to make sure it's actually a valid BigNumber. We're not your validator, we're your messenger. ๐ In RESP2, this is just a plain bulk string response. Simple like that.
The function always returns VALKEYMODULE_OK. Periodt. ๐
๐ฆ ValkeyModule_ReplyWithLongDouble
int ValkeyModule_ReplyWithLongDouble(ValkeyModuleCtx *ctx, long double ld);
Available since: 6.0.0 ๐
Send a string reply obtained by converting the long double ld into a bulk string. Just like its sibling above, this is basically equivalent to converting a long double into a string into a C buffer, and then calling ValkeyModule_ReplyWithStringBuffer() with the buffer and length. ๐
Oh and the double string uses human readable formatting (see addReplyHumanLongDouble in networking.c) โ so it's giving readable, not giving chaos. ๐ง
The function always returns VALKEYMODULE_OK. Obviously. ๐
๐ Commands replication API
ValkeyModule_Replicate
int ValkeyModule_Replicate(ValkeyModuleCtx *ctx,
const char *cmdname,
const char *fmt,
...);
Available since: 4.0.0 โญ
Replicate the specified command and arguments to replicas and AOF, as an effect of execution of the calling command implementation. It's giving "do the thing, but also tell everyone else about it." ๐ฃ
The replicated commands are always wrapped into the MULTI/EXEC that contains all the commands replicated in a given module command execution. However โ plot twist โ the commands replicated with ValkeyModule_Call() are the first items, and the ones replicated with ValkeyModule_Replicate() will all follow before the EXEC. ๐ฎ
Modules should try to use one interface or the other. Pick a lane, bestie. ๐ฃ๏ธ
This command follows exactly the same interface as ValkeyModule_Call(), so you gotta pass a set of format specifiers, followed by arguments matching the provided format specifiers. Refer to ValkeyModule_Call() for more info, iykyk. ๐
Using the special "A" and "R" modifiers, the caller can exclude either the AOF or the replicas from the propagation of the specified command. Otherwise, by default, the command gets propagated in both channels. Both. No cap. ๐ฏ
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 Module API, the server 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. ๐ซถ
But when you call this function from a threaded safe context that can live an undefined amount of time, and can be locked/unlocked at will, the behavior is different: the MULTI/EXEC wrapper is not emitted, and the command specified is inserted in the AOF and replication stream immediately. โก Different vibe, different rules.
Return value
The command returns VALKEYMODULE_ERR if the format specifiers are invalid or the command name does not belong to a known command. ๐ซ
๐ ValkeyModule_ReplicateVerbatim
int ValkeyModule_ReplicateVerbatim(ValkeyModuleCtx *ctx);
Available since: 4.0.0 โจ
This function will replicate the command exactly as it was invoked by the client. Copy-paste energy, but make it replication. ๐
Heads up though: this function will not wrap the command into a MULTI/EXEC stanza, so it should not be mixed with other replication commands. Do not do it. That's the rule. โ ๏ธ
Basically this form of replication is useful when you want to propagate the command to the replicas and AOF file exactly as it was called, since the command can just be re-executed to deterministically re-create the new state starting from the old one. Chef's kiss. ๐จโ๐ณ๐
The function always returns VALKEYMODULE_OK. ๐
๐ DB and Key APIs โ Generic API
ValkeyModule_GetClientId
unsigned long long ValkeyModule_GetClientId(ValkeyModuleCtx *ctx);
Available since: 4.0.0 โญ
Return the ID of the current client calling the currently active module command. The returned ID has a few guarantees, and we're listing them because they matter: ๐
- The ID is different for each different client, so if the same client executes a module command multiple times, it can be recognized as having the same ID, otherwise the ID will be different. ๐
- The ID increases monotonically. Clients connecting to the server later are guaranteed to get IDs greater than any past ID previously seen. ๐
Valid IDs are from 1 to 2^64 - 1. If 0 is returned, it means there is no way to fetch the ID in the context the function was currently called. ๐ซ
After obtaining the ID, it's possible to check if the command execution is actually happening in the context of AOF loading, using this macro:
if (ValkeyModule_IsAOFClient(ValkeyModule_GetClientId(ctx)) {
// Handle it differently.
}
๐ ValkeyModule_GetClientUserNameById
ValkeyModuleString *ValkeyModule_GetClientUserNameById(ValkeyModuleCtx *ctx,
uint64_t id);
Available since: 6.2.1 ๐ธ
Return the ACL user name used by the client with the specified client ID. That client ID can be obtained with the ValkeyModule_GetClientId() API. ๐
If the client does not exist, NULL is returned and errno is set to ENOENT. If the client isn't using an ACL user, NULL is returned and errno is set to ENOTSUP. Two different NULLs, two different reasons. ๐
ValkeyModule_MustObeyClient
int ValkeyModule_MustObeyClient(ValkeyModuleCtx *ctx);
Available since: 8.1.0 ๐
Returns 1 if commands are arriving from the primary client or AOF client and should never be rejected. ๐
โโ๏ธ
This check can be used in places such as skipping validation of commands on replicas (so you don't diverge from the primary) or from AOF files. Returns 0 otherwise (and also if ctx or if the client is NULL). Zero means nah. ๐ซ
โญ ValkeyModule_GetClientInfoById
int ValkeyModule_GetClientInfoById(void *ci, uint64_t id);
Available since: 6.0.0 ๐ซ
Return information about the client with the specified ID (that was previously obtained via the ValkeyModule_GetClientId() API). If the client exists, VALKEYMODULE_OK is returned, otherwise VALKEYMODULE_ERR is returned. ๐
When the client exists and the ci pointer is not NULL, but points to a structure of type ValkeyModuleClientInfoV1, previously initialized with the correct VALKEYMODULE_CLIENTINFO_INITIALIZER_V1, the structure gets populated with these fields:
uint64_t flags; // VALKEYMODULE_CLIENTINFO_FLAG_*
uint64_t id; // Client ID
char addr[46]; // IPv4 or IPv6 address.
uint16_t port; // TCP port.
uint16_t db; // Selected DB.
Note: the client ID is useless in the context of this call, since we already know it โ however the same structure could be used in other contexts where we don't know the client ID, yet the same structure is returned. Reusable icon behavior. โป๏ธ
With flags having the following meaning:
VALKEYMODULE_CLIENTINFO_FLAG_SSL Client using SSL connection.
VALKEYMODULE_CLIENTINFO_FLAG_PUBSUB Client in Pub/Sub mode.
VALKEYMODULE_CLIENTINFO_FLAG_BLOCKED Client blocked in command.
VALKEYMODULE_CLIENTINFO_FLAG_TRACKING Client with keys tracking on.
VALKEYMODULE_CLIENTINFO_FLAG_UNIXSOCKET Client using unix domain socket.
VALKEYMODULE_CLIENTINFO_FLAG_MULTI Client in MULTI state.
VALKEYMODULE_CLIENTINFO_FLAG_READONLY Client in ReadOnly state.
However, passing NULL is a way to just check if the client exists, in case we are not interested in any additional information. Low-effort, still valid. ๐
This is the correct usage when we want the client info structure returned:
ValkeyModuleClientInfo ci = VALKEYMODULE_CLIENTINFO_INITIALIZER;
int retval = ValkeyModule_GetClientInfoById(&ci,client_id);
if (retval == VALKEYMODULE_OK) {
printf("Address: %s\n", ci.addr);
}
๐ฆ ValkeyModule_GetClientNameById
ValkeyModuleString *ValkeyModule_GetClientNameById(ValkeyModuleCtx *ctx,
uint64_t id);
Available since: 7.0.3 ๐ธ
Returns the name of the client connection with the given ID. ๐ท๏ธ
If the client ID does not exist, or if the client has no name associated with it, NULL is returned. Either way, NULL. ๐
๐
ValkeyModule_SetClientNameById
int ValkeyModule_SetClientNameById(uint64_t id, ValkeyModuleString *name);
Available since: 7.0.3 โจ
Sets the name of the client with the given ID. This is equivalent to the client calling CLIENT SETNAME name. Same thing, just from the inside. ๐
Returns VALKEYMODULE_OK on success. On failure, VALKEYMODULE_ERR is returned and errno is set as follows:
ENOENTif the client does not existEINVALif the name contains invalid characters
๐ ValkeyModule_PublishMessage
int ValkeyModule_PublishMessage(ValkeyModuleCtx *ctx,
ValkeyModuleString *channel,
ValkeyModuleString *message);
Available since: 6.0.0 ๐ฃ
Publish a message to subscribers (see the PUBLISH command). Everyone subscribed is about to hear it. ๐ซถ
๐ ValkeyModule_PublishMessageShard
int ValkeyModule_PublishMessageShard(ValkeyModuleCtx *ctx,
ValkeyModuleString *channel,
ValkeyModuleString *message);
Available since: 7.0.0 โก
Publish a message to shard-subscribers (see the SPUBLISH command). Shard-specific audience, still iconic. ๐
๐ ValkeyModule_GetSelectedDb
int ValkeyModule_GetSelectedDb(ValkeyModuleCtx *ctx);
Available since: 4.0.0 โญ
Return the currently selected DB. That's it, that's the function. ๐๏ธโจ
ValkeyModule_GetContextFlags</p>
int ValkeyModule_GetContextFlags(ValkeyModuleCtx *ctx);
Available since: 4.0.3
Okay bestie, this one hands you back all the current context's flags โจ. They're little info nuggets about the request context you're in (like, is the client a Lua script? are we inside a MULTI?) plus general gossip about the instance itself โ replication, persistence, the whole vibe ๐
.
You can totally call this with a NULL context, no cap โ but when you do, these flags just won't show up:
LUA,MULTI,REPLICATED,DIRTY(more on those below ๐).
Here's every flag and what it's giving:
VALKEYMODULE_CTX_FLAGS_LUA: the command is running inside a Lua scriptVALKEYMODULE_CTX_FLAGS_MULTI: the command is running inside a transactionVALKEYMODULE_CTX_FLAGS_REPLICATED: the command was sent over the replication link by the PRIMARYVALKEYMODULE_CTX_FLAGS_PRIMARY: the instance is a primaryVALKEYMODULE_CTX_FLAGS_REPLICA: the instance is a replicaVALKEYMODULE_CTX_FLAGS_READONLY: the instance is read-onlyVALKEYMODULE_CTX_FLAGS_CLUSTER: the instance is in cluster modeVALKEYMODULE_CTX_FLAGS_AOF: the instance has AOF enabledVALKEYMODULE_CTX_FLAGS_RDB: the instance has RDB enabledVALKEYMODULE_CTX_FLAGS_MAXMEMORY: the instance has Maxmemory setVALKEYMODULE_CTX_FLAGS_EVICT: Maxmemory is set and it has an eviction policy that may delete keys ๐ฌVALKEYMODULE_CTX_FLAGS_OOM: the server is out of memory according to the maxmemory settingVALKEYMODULE_CTX_FLAGS_OOM_WARNING: less than 25% of memory remains before reaching the maxmemory levelVALKEYMODULE_CTX_FLAGS_LOADING: server is loading RDB/AOFVALKEYMODULE_CTX_FLAGS_REPLICA_IS_STALE: no active link with the primaryVALKEYMODULE_CTX_FLAGS_REPLICA_IS_CONNECTING: the replica is trying to connect with the primaryVALKEYMODULE_CTX_FLAGS_REPLICA_IS_TRANSFERRING: primary -> replica RDB transfer is in progressVALKEYMODULE_CTX_FLAGS_REPLICA_IS_ONLINE: the replica has an active link with its primary โ literally the opposite of the STALE stateVALKEYMODULE_CTX_FLAGS_ACTIVE_CHILD: there's currently some background process active (RDB, AUX or module)VALKEYMODULE_CTX_FLAGS_MULTI_DIRTY: the nextEXECwill fail due to dirty CAS (touched keys) ๐VALKEYMODULE_CTX_FLAGS_IS_CHILD: the server is currently running inside a background child processVALKEYMODULE_CTX_FLAGS_RESP3: indicates that the client attached to this context is using RESP3VALKEYMODULE_CTX_FLAGS_SERVER_STARTUP: the instance is startingVALKEYMODULE_CTX_FLAGS_SLOT_IMPORT_CLIENT: indicates that the client attached to this context is the slot import clientVALKEYMODULE_CTX_FLAGS_SLOT_EXPORT_CLIENT: indicates that the client attached to this context is the slot export client
ValkeyModule_AvoidReplicaTraffic
int ValkeyModule_AvoidReplicaTraffic(void);
Available since: 6.0.0
Returns true when a client hit the server with CLIENT PAUSE, or when the Cluster does a manual failover and pauses the clients ๐ซถ. Why do we care? Because picture this: you've got a primary with replicas, and you wanna write something without piling more data onto the replication channel, so that the replicas' replication offset lines up with the primary's. When that lines up, failing over the primary is safe with zero data loss ๐.
But โ and this is the plot twist โ modules can make traffic anyway by calling ValkeyModule_Call() with the ! flag, or by calling ValkeyModule_Replicate(), in a context that's outside command execution. Think timeout callbacks, threads safe contexts, that whole genre. And when modules generate too much traffic, the primary and replica offsets get real hard to match, because there's just more data in the replication channel ๐ญ.
So when this function returns true, modules may wanna chill on the super heavy background work that ends up creating data for the replication channel. It's mostly for modules with background garbage collection tasks, or ones that do writes and replicate them periodically in timer callbacks or other periodic callbacks ๐ธ.
ValkeyModule_SelectDb
int ValkeyModule_SelectDb(ValkeyModuleCtx *ctx, int newid);
Available since: 4.0.0
Switches which DB is currently selected. If the id is out of range, you get an error back, periodt.
Heads up though โ the client keeps the currently selected DB even after the command implemented by the module that called this function returns. So if your module command wants to poke around in a different DB and then come home, call ValkeyModule_GetSelectedDb() first so you can restore the old DB number before you return ๐กโจ.
ValkeyModule_KeyExists
int ValkeyModule_KeyExists(ValkeyModuleCtx *ctx, robj *keyname);
Available since: 7.0.0
Checks whether a key exists โ and it does it without touching the key's last access time ๐. Sneaky, respectful, iconic.
This is equivalent to calling ValkeyModule_OpenKey with the mode VALKEYMODULE_READ | VALKEYMODULE_OPEN_KEY_NOTOUCH, then checking if NULL came back and, if it didn't, calling ValkeyModule_CloseKey on the opened key ๐
.
ValkeyModule_OpenKey
ValkeyModuleKey *ValkeyModule_OpenKey(ValkeyModuleCtx *ctx,
robj *keyname,
int mode);
Available since: 4.0.0
Returns a handle that represents a key, so you can pass that handle into other APIs as an argument and do operations on the key ๐ฅ.
What you get back is the handle, and you must close it with ValkeyModule_CloseKey() when you're done โ no leaving it open, bestie.
If the key doesn't exist and you asked for VALKEYMODULE_WRITE mode, you still get a handle back, because you can legitimately perform operations on a key that doesn't exist yet (it'll get created, for example, after a list push operation โจ). But if the mode is just VALKEYMODULE_READ and the key doesn't exist, you get NULL. And ngl, it's still totally safe to call ValkeyModule_CloseKey() and ValkeyModule_KeyType() on a NULL value ๐ซถ.
Extra flags you can pass to the API under the mode argument:
VALKEYMODULE_OPEN_KEY_NOTOUCHโ avoid touching the LRU/LFU of the key when openedVALKEYMODULE_OPEN_KEY_NONOTIFYโ don't trigger keyspace event on key missesVALKEYMODULE_OPEN_KEY_NOSTATSโ don't update keyspace hits/misses countersVALKEYMODULE_OPEN_KEY_NOEXPIREโ avoid deleting lazy expired keysVALKEYMODULE_OPEN_KEY_NOEFFECTSโ avoid any effects from fetching the key
ValkeyModule_GetOpenKeyModesAll
int ValkeyModule_GetOpenKeyModesAll(void);
Available since: 7.2.0
Hands you the full OpenKey modes mask, so your module can check whether a given set of OpenKey modes is even supported by the server version that's running ๐. Example, look:
int supportedMode = ValkeyModule_GetOpenKeyModesAll();
if (supportedMode & VALKEYMODULE_OPEN_KEY_NOTOUCH) {
// VALKEYMODULE_OPEN_KEY_NOTOUCH is supported
} else{
// VALKEYMODULE_OPEN_KEY_NOTOUCH is not supported
}
ValkeyModule_CloseKey
void ValkeyModule_CloseKey(ValkeyModuleKey *key);
Available since: 4.0.0
Closes a key handle. The handle gets freed, so likeโฆ don't touch it anymore, it's gone, bye ๐๐
ValkeyModule_KeyType
int ValkeyModule_KeyType(ValkeyModuleKey *key);
Available since: 4.0.0
Returns the type of the key. If the key pointer is NULL, then VALKEYMODULE_KEYTYPE_EMPTY comes back instead ๐ธ.
ValkeyModule_ValueLength
size_t ValkeyModule_ValueLength(ValkeyModuleKey *key);
Available since: 4.0.0
Returns the length of the value associated with the key ๐. For strings, that's the length of the string. For literally every other type, it's the number of elements (just counting keys for hashes).
If the key pointer is NULL or the key is empty, you get zero. Zero, nada, nothing โจ.
ValkeyModule_DeleteKey
int ValkeyModule_DeleteKey(ValkeyModuleKey *key);
Available since: 4.0.0
If the key is open for writing, this removes it and sets the key up to accept new writes as an empty key (which'll get created on demand, cute ๐ฑ). On success you get VALKEYMODULE_OK. If the key is not open for writing, you get VALKEYMODULE_ERR ๐ญ.
ValkeyModule_UnlinkKey
int ValkeyModule_UnlinkKey(ValkeyModuleKey *key);
Available since: 4.0.7
If the key is open for writing, this unlinks it โ meaning it deletes it in a non-blocking way, not reclaiming memory immediately โ and sets the key up to accept new writes as an empty key (created on demand, you know the drill). On success you get VALKEYMODULE_OK. If the key isn't open for writing, VALKEYMODULE_ERR is what you get ๐ซ .
ValkeyModule_GetExpire
mstime_t ValkeyModule_GetExpire(ValkeyModuleKey *key);
Available since: 4.0.0
Returns the key's expire value, as milliseconds of remaining TTL โณ. If there's no TTL associated with the key, or the key is empty, you get VALKEYMODULE_NO_EXPIRE back.
ValkeyModule_SetExpire
int ValkeyModule_SetExpire(ValkeyModuleKey *key, mstime_t expire);
Available since: 4.0.0
Sets a new expire for the key โจ. If you set the special expire VALKEYMODULE_NO_EXPIRE, the expire gets cancelled if there was one (same energy as the PERSIST command, fr fr).
Note that the expire has to be a positive integer representing the number of milliseconds of TTL the key should have.
The function returns VALKEYMODULE_OK on success, or VALKEYMODULE_ERR if the key wasn't open for writing or if it's an empty key ๐
.
ValkeyModule_GetAbsExpire
mstime_t ValkeyModule_GetAbsExpire(ValkeyModuleKey *key);
Available since: 6.2.2
Returns the key expire value, as an absolute Unix timestamp ๐ฐ๏ธ. If there's no TTL associated with the key, or the key is empty, VALKEYMODULE_NO_EXPIRE comes back.
ValkeyModule_SetAbsExpire
int ValkeyModule_SetAbsExpire(ValkeyModuleKey *key, mstime_t expire);
Available since: 6.2.2
Sets a new expire for the key ๐. If you set the special expire VALKEYMODULE_NO_EXPIRE, the expire is cancelled if there was one (same as the PERSIST command, exactly).
Note that the expire must be a positive integer representing the absolute Unix timestamp the key should have.
The function returns VALKEYMODULE_OK on success or VALKEYMODULE_ERR if the key wasn't open for writing or is an empty key ๐ญ.
ValkeyModule_ResetDataset
void ValkeyModule_ResetDataset(int restart_aof, int async);
Available since: 6.0.0
Does a similar operation to FLUSHALL, and optionally starts a new AOF file (if it's enabled) ๐ฅ. If restart_aof is true, you must make sure the command that triggered this call doesn't get propagated to the AOF file โ that part is non-negotiable, bestie. When async is set to true, the db contents get freed by a background thread ๐
.
ValkeyModule_DbSize
unsigned long long ValkeyModule_DbSize(ValkeyModuleCtx *ctx);
Available since: 6.0.0
okay so this one just hands you back the number of keys in the current db. that's it. that's the whole thing. no drama, no notes ๐
๐ธ ValkeyModule_RandomKey ๐ธ
ValkeyModuleString *ValkeyModule_RandomKey(ValkeyModuleCtx *ctx);
Available since: 6.0.0
Returns the name of a random key, or NULL if the current db is empty. like a little mystery grab bag ๐ no cap.
๐ ValkeyModule_GetKeyNameFromOptCtx ๐
const ValkeyModuleString *ValkeyModule_GetKeyNameFromOptCtx(ValkeyModuleKeyOptCtx *ctx);
Available since: 7.0.0
Returns the name of the key currently being processed. your main character key, fr fr ๐
๐ฆ ValkeyModule_GetToKeyNameFromOptCtx ๐ฆ
const ValkeyModuleString *ValkeyModule_GetToKeyNameFromOptCtx(ValkeyModuleKeyOptCtx *ctx);
Available since: 7.0.0
Returns the name of the target key currently being processed. yes bestie, there's a to key โ it's giving destination energy โจ
๐ ValkeyModule_GetDbIdFromOptCtx ๐
int ValkeyModule_GetDbIdFromOptCtx(ValkeyModuleKeyOptCtx *ctx);
Available since: 7.0.0
Returns the dbid currently being processed. which database are we in, bestie? this tells you ๐
๐ ValkeyModule_GetToDbIdFromOptCtx ๐
int ValkeyModule_GetToDbIdFromOptCtx(ValkeyModuleKeyOptCtx *ctx);
Available since: 7.0.0
Returns the target dbid currently being processed. the to dbid, the sequel, the part two ๐ซถ
โจ Key API for String type โจ
See also ValkeyModule_ValueLength(), which returns the length of a string. very much the friend of this whole section ๐
๐ ValkeyModule_StringSet ๐
int ValkeyModule_StringSet(ValkeyModuleKey *key, ValkeyModuleString *str);
Available since: 4.0.0
If the key is open for writing, set the specified string str as the value of the key, deleting the old value if any ๐ญ (rip old value, you had a good run).
On success VALKEYMODULE_OK is returned. If the key is not open for writing, or there is an active iterator, VALKEYMODULE_ERR is returned.
๐ง ValkeyModule_StringDMA ๐ง
char *ValkeyModule_StringDMA(ValkeyModuleKey *key, size_t *len, int mode);
Available since: 4.0.0
Prepare the key's associated string value for DMA access, and returns a pointer and size (by reference) that you can use to read or modify the string in-place, accessing it directly via pointer. direct access, no middleman, obsessed โจ
The mode is composed by bitwise OR-ing the following flags:
VALKEYMODULE_READโ Read accessVALKEYMODULE_WRITEโ Write access
If the DMA is not requested for writing, the pointer returned should only be accessed in a read-only fashion. do not play with it, bestie ๐
On error (wrong type) NULL is returned.
DMA access rules:
- No other key writing function should be called since the moment the pointer is obtained, for all the time we want to use DMA access to read or modify the string.
- Each time
ValkeyModule_StringTruncate()is called, to continue with the DMA access,ValkeyModule_StringDMA()should be called again to re-obtain a new pointer and length. - If the returned pointer is not
NULL, but the length is zero, no byte can be touched (the string is empty, or the key itself is empty) so aValkeyModule_StringTruncate()call should be used if there is to enlarge the string, and later callStringDMA()again to get the pointer.
lowkey these three rules are the whole game. break them and the vibes are ruined ๐
๐ธ ValkeyModule_StringTruncate ๐ธ
int ValkeyModule_StringTruncate(ValkeyModuleKey *key, size_t newlen);
Available since: 4.0.0
If the key 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, ValkeyModule_StringDMA() must be called again to continue DMA access with the new pointer. not optional, not a suggestion, it's the rule ๐ซถ
The function returns VALKEYMODULE_OK on success, and VALKEYMODULE_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.
๐ Key API for List type ๐
Many of the list functions access elements by index. Since a list is in essence a doubly-linked list, accessing elements by index is generally an O(N) operation. however!! if elements are accessed sequentially or with indices close together, the functions are optimized to seek the index from the previous index, rather than seeking from the ends of the list. so it's actually fine, bestie, iykyk ๐
This enables iteration to be done efficiently using a simple for loop:
long n = ValkeyModule_ValueLength(key);
for (long i = 0; i < n; i++) {
ValkeyModuleString *elem = ValkeyModule_ListGet(key, i);
// Do stuff...
}
Note that after modifying a list using ValkeyModule_ListPop, ValkeyModule_ListSet or ValkeyModule_ListInsert, the internal iterator is invalidated so the next operation will require a linear seek. it's giving betrayal ๐ญ
Modifying a list in any another way, for example using ValkeyModule_Call(), while a key is open will confuse the internal iterator and may cause trouble if the key is used after such modifications. The key must be reopened in this case.
See also ValkeyModule_ValueLength(), which returns the length of a list. ๐ฆ
๐ ValkeyModule_ListPush ๐
int ValkeyModule_ListPush(ValkeyModuleKey *key,
int where,
ValkeyModuleString *ele);
Available since: 4.0.0
Push an element into a list, on head or tail depending on the where argument (VALKEYMODULE_LIST_HEAD or VALKEYMODULE_LIST_TAIL). If the key refers to an empty key opened for writing, the key is created. very "manifesting a list out of nothing" of it โจ
On success, VALKEYMODULE_OK is returned. On failure, VALKEYMODULE_ERR is returned and errno is set as follows:
EINVALifkeyoreleisNULL.ENOTSUPif the key is of another type than list.EBADFif the key is not opened for writing.
Note: Before Redis OSS7.0,errnowas not set by this function.
๐ ValkeyModule_ListPop ๐
ValkeyModuleString *ValkeyModule_ListPop(ValkeyModuleKey *key, int where);
Available since: 4.0.0
Pop an element from the list, and returns it as a module string object that you should free with ValkeyModule_FreeString() or by enabling automatic memory. The where argument specifies if the element should be popped from the beginning or the end of the list (VALKEYMODULE_LIST_HEAD or VALKEYMODULE_LIST_TAIL). ๐
On failure, the command returns NULL and sets errno as follows:
EINVALifkeyisNULL.ENOTSUPif the key is empty or of another type than list.EBADFif the key is not opened for writing.
Note: Before Redis OSS7.0,errnowas not set by this function.
๐ง ValkeyModule_ListGet ๐ง
ValkeyModuleString *ValkeyModule_ListGet(ValkeyModuleKey *key, long index);
Available since: 7.0.0
Returns the element at index index in the list stored at key, like the LINDEX command. The element should be free'd using ValkeyModule_FreeString() or using automatic memory management. (the original says "freeโd" and honestly? iconic spelling, keeping the vibe ๐ซถ)
The index is zero-based, so 0 means the first element, 1 the second element and so on. Negative indices can be used to designate elements starting at the tail of the list. Here, -1 means the last element, -2 means the penultimate and so forth.
When no value is found at the given key and index, NULL is returned and errno is set as follows:
EINVALifkeyisNULL.ENOTSUPif the key is not a list.EBADFif the key is not opened for reading.EDOMif the index is not a valid index in the list.
๐ธ ValkeyModule_ListSet ๐ธ
int ValkeyModule_ListSet(ValkeyModuleKey *key,
long index,
ValkeyModuleString *value);
Available since: 7.0.0
Replaces the element at index index in the list stored at key. glow-up for one single element ๐
The index is zero-based, so 0 means the first element, 1 the second element and so on. Negative indices can be used to designate elements starting at the tail of the list. Here, -1 means the last element, -2 means the penultimate and so forth.
On success, VALKEYMODULE_OK is returned. On failure, VALKEYMODULE_ERR is returned and errno is set as follows:
EINVALifkeyorvalueisNULL.ENOTSUPif the key is not a list.EBADFif the key is not opened for writing.EDOMif the index is not a valid index in the list.
๐ ValkeyModule_ListInsert ๐
int ValkeyModule_ListInsert(ValkeyModuleKey *key,
long index,
ValkeyModuleString *value);
Available since: 7.0.0
Inserts an element at the given index. squeezing into the group chat, uninvited but welcome ๐
The index is zero-based, so 0 means the first element, 1 the second element and so on. Negative indices can be used to designate elements starting at the tail of the list. Here, -1 means the last element, -2 means the penultimate and so forth. The index is the element's index after inserting it.
On success, VALKEYMODULE_OK is returned. On failure, VALKEYMODULE_ERR is returned and errno is set as follows:
EINVALifkeyorvalueisNULL.ENOTSUPif the key of another type than list.EBADFif the key is not opened for writing.EDOMif the index is not a valid index in the list.
๐ฆ ValkeyModule_ListDelete ๐ฆ
int ValkeyModule_ListDelete(ValkeyModuleKey *key, long index);
Available since: 7.0.0
Removes an element at the given index. The index is 0-based. A negative index can also be used, counting from the end of the list. bye bestie, it's not personal ๐ญ
On success, VALKEYMODULE_OK is returned. On failure, VALKEYMODULE_ERR is returned and errno is set as follows:
EINVALifkeyorvalueisNULL.ENOTSUPif the key is not a list.EBADFif the key is not opened for writing.EDOMif the index is not a valid index in the list.
๐ Key API for Sorted Set type ๐
See also ValkeyModule_ValueLength(), which returns the length of a sorted set. the trio is complete ๐ซถโจ
โญ ValkeyModule_ZsetAdd โญ
int ValkeyModule_ZsetAdd(ValkeyModuleKey *key,
double score,
ValkeyModuleString *ele,
int *flagsptr);
Available since: 4.0.0
Add a new element into a sorted set, with the specified score. If the element already exists, the score is updated. no duplicates here, we just re-rank you ๐
A new sorted set is created at value if the key is an empty open key setup for writing.
Additional flags can be passed to the function via a pointer โ the flags are used both to receive input and to communicate state when the function returns. flagsptr can be NULL if no special flags are used. multitasking queen ๐
The input flags are:
VALKEYMODULE_ZADD_XX: Element must already exist. Do nothing otherwise.VALKEYMODULE_ZADD_NX: Element must not exist. Do nothing otherwise.VALKEYMODULE_ZADD_GT: If element exists, new score must be greater than the current score. Do nothing otherwise. Can optionally be combined withXX.VALKEYMODULE_ZADD_LT: If element exists, new score must be less than the current score. Do nothing otherwise. Can optionally be combined withXX.
The output flags are:
VALKEYMODULE_ZADD_ADDEDโ the new element was added to the sorted set. brand new, we love a fresh addition ๐VALKEYMODULE_ZADD_UPDATEDโ the score of the element was updated. glow-up complete, iconic โจVALKEYMODULE_ZADD_NOPโ no operation was performed, because of theXXorNXflags. like, nothing happened at all, the flags said no ๐
On success the function returns VALKEYMODULE_OK. But bestie, on these errors it hands you back VALKEYMODULE_ERR:
- The key was not opened for writing. ๐ซ
- The key is of the wrong type. ๐ญ
- The
scoredouble value is not a number (NaN). ๐ฉ
ValkeyModule_ZsetIncrby
int ValkeyModule_ZsetIncrby(ValkeyModuleKey *key,
double score,
ValkeyModuleString *ele,
int *flagsptr,
double *newscore);
Available since: 4.0.0
This one works exactly like ValkeyModule_ZsetAdd(), no cap โ except instead of setting a brand new score, it increments the score of the element that's already there. And if that element doesn't exist yet? It gets added, assuming the old score was zero. starting from scratch energy ๐ฆ
The input flags, the output flags, and the return value all mean the same exact thing as before. The one lil difference: this function will return VALKEYMODULE_ERR even when score is a perfectly valid double, but adding it to the existing score results in a NaN (not a number) condition. math said no ๐ฉ
There's also an extra field called newscore. If it's not NULL, it gets filled with the new score of the element after the increment โ but only if no error was returned. so, like, the receipts โจ
ValkeyModule_ZsetRem
int ValkeyModule_ZsetRem(ValkeyModuleKey *key,
ValkeyModuleString *ele,
int *deleted);
Available since: 4.0.0
Removes the specified element from the sorted set. Returns VALKEYMODULE_OK on success, and VALKEYMODULE_ERR if one of these conditions hits:
- The key was not opened for writing.
- The key is of the wrong type.
Heads up, bestie: the return value does NOT tell you whether the element was actually removed (like, whether it even existed). It only tells you whether the function ran successfully. big difference ๐
So if you actually wanna know whether the element got removed, you gotta pass that extra deleted argument โ it populates the integer by reference, setting it to 1 or 0 depending on how the operation turned out. The deleted argument can be NULL if you're just not interested in knowing whether the element was really removed. no thoughts, head empty ๐
Empty keys get handled correctly by doing nothing. slay, no crash.
ValkeyModule_ZsetScore
int ValkeyModule_ZsetScore(ValkeyModuleKey *key,
ValkeyModuleString *ele,
double *score);
Available since: 4.0.0
On success it retrieves the double score associated with the sorted set element ele, and returns VALKEYMODULE_OK. Otherwise VALKEYMODULE_ERR comes back to signal one of these:
- There is no such element
elein the sorted set. ๐ป - The key is not a sorted set.
- The key is an open empty key.
Key API for Sorted Set iterator
ValkeyModule_ZsetRangeStop
void ValkeyModule_ZsetRangeStop(ValkeyModuleKey *key);
Available since: 4.0.0
Stop a sorted set iteration. that's it, that's the whole vibe, we're done here ๐
ValkeyModule_ZsetRangeEndReached
int ValkeyModule_ZsetRangeEndReached(ValkeyModuleKey *key);
Available since: 4.0.0
Returns the โEnd of rangeโ flag value, to signal that the iteration is over. ๐
ValkeyModule_ZsetFirstInScoreRange
int ValkeyModule_ZsetFirstInScoreRange(ValkeyModuleKey *key,
double min,
double max,
int minex,
int maxex);
Available since: 4.0.0
Sets up a sorted set iterator that seeks the first element in the specified range. Returns VALKEYMODULE_OK if the iterator was correctly initialized, otherwise VALKEYMODULE_ERR comes back in the following condition:
- The value stored at key is not a sorted set, or the key is empty.
The range is specified using the two double values min and max. Both of them can be infinite, using these two macros:
VALKEYMODULE_POSITIVE_INFINITEfor a positive infinite value โพ๏ธVALKEYMODULE_NEGATIVE_INFINITEfor a negative infinite value โพ๏ธ
The minex and maxex parameters โ if true โ set up a range where the min and max values are exclusive (not included) instead of inclusive. so like, "boundaries? we don't know her" ๐
ValkeyModule_ZsetLastInScoreRange
int ValkeyModule_ZsetLastInScoreRange(ValkeyModuleKey *key,
double min,
double max,
int minex,
int maxex);
Available since: 4.0.0
Exactly like ValkeyModule_ZsetFirstInScoreRange(), but the last element of the range is selected as the start of the iteration instead. plot twist ๐ฅ
ValkeyModule_ZsetFirstInLexRange
int ValkeyModule_ZsetFirstInLexRange(ValkeyModuleKey *key,
ValkeyModuleString *min,
ValkeyModuleString *max);
Available since: 4.0.0
Sets up a sorted set iterator that seeks the first element in the specified lexicographical range. Returns VALKEYMODULE_OK if the iterator was correctly initialized, otherwise VALKEYMODULE_ERR comes back in the following conditions:
- The value stored at key is not a sorted set, or the key is empty.
- The lexicographical range
minandmaxformat is invalid. ๐ฉ
min and max should be provided as two ValkeyModuleString objects, in the same format as the parameters you'd pass to the ZRANGEBYLEX command. The function does not take ownership of the objects, so they can be released ASAP after the iterator is set up. freeing them the moment you're done, as you should ๐ซถ
ValkeyModule_ZsetLastInLexRange
int ValkeyModule_ZsetLastInLexRange(ValkeyModuleKey *key,
ValkeyModuleString *min,
ValkeyModuleString *max);
Available since: 4.0.0
Exactly like ValkeyModule_ZsetFirstInLexRange(), but the last element of the range is selected as the start of the iteration instead. again with the plot twist, we love consistency โจ
ValkeyModule_ZsetRangeCurrentElement
ValkeyModuleString *ValkeyModule_ZsetRangeCurrentElement(ValkeyModuleKey *key,
double *score);
Available since: 4.0.0
Returns the current sorted set element of an active sorted set iterator โ or NULL if the range specified in the iterator doesn't include any element at all. nothing to see here ๐ญ
ValkeyModule_ZsetRangeNext
int ValkeyModule_ZsetRangeNext(ValkeyModuleKey *key);
Available since: 4.0.0
Goes to the next element of the sorted set iterator. Returns 1 if there was a next element, 0 if we're already at the latest element, or if the range doesn't include any item at all. we're at the end of the line, bestie ๐
ValkeyModule_ZsetRangePrev
int ValkeyModule_ZsetRangePrev(ValkeyModuleKey *key);
Available since: 4.0.0
Goes to the previous element of the sorted set iterator. Returns 1 if there was a previous element, 0 if we're already at the first element, or if the range doesn't include any item at all. going backwards, main character energy ๐๐
Key API for Hash type
See also ValkeyModule_ValueLength(), which returns the number of fields in a hash. iykyk ๐
ValkeyModule_HashSet
int ValkeyModule_HashSet(ValkeyModuleKey *key, int flags, ...);
Available since: 4.0.0
Sets the field of the specified hash field to the specified value. If the key is an empty key open for writing, it gets created with an empty hash value, so the specified field can be set. building it from scratch, we love to see it ๐ฑ
The function is variadic, so you have to specify pairs of field names and values, both as ValkeyModuleString pointers (unless the CFIELD option is set, more on that later). At the end of all the field/value-ptr pairs, NULL must be specified as the last argument to signal the end of the arguments in this variadic function. that NULL is the periodt of the argument list ๐
Example to set the hash argv[1] to the value argv[2]:
ValkeyModule_HashSet(key,VALKEYMODULE_HASH_NONE,argv[1],argv[2],NULL);
The function can also be used to delete fields (if they exist), by setting them to the specified value of VALKEYMODULE_HASH_DELETE:
ValkeyModule_HashSet(key,VALKEYMODULE_HASH_NONE,argv[1],
VALKEYMODULE_HASH_DELETE,NULL);
The behavior of the command changes with the specified flags, which can be set to VALKEYMODULE_HASH_NONE if no special behavior is needed.
VALKEYMODULE_HASH_NXโ the operation is performed only if the field was not already existing in the hash.VALKEYMODULE_HASH_XXโ the operation is performed only if the field was already existing, so that a new value could be associated to an existing field, but no new fields are created.VALKEYMODULE_HASH_CFIELDSโ the field names passed are null terminated C strings instead ofValkeyModuleStringobjects.VALKEYMODULE_HASH_COUNT_ALLโ include the number of inserted fields in the returned number, in addition to the number of updated and deleted fields. (Added in Redis OSS 6.2.)
Unless NX is specified, the command overwrites the old field value with the new one. out with the old, in with the new โจ
When using VALKEYMODULE_HASH_CFIELDS, field names are reported using normal C strings, so for example, to delete the field โfooโ the following code can be used:
ValkeyModule_HashSet(key,VALKEYMODULE_HASH_CFIELDS,"foo",
VALKEYMODULE_HASH_DELETE,NULL);
Return value:
The number of fields existing in the hash prior to the call, which have been updated (their old value has been replaced by a new value) or deleted. If the flag VALKEYMODULE_HASH_COUNT_ALL is set, inserted fields that weren't previously existing in the hash are also counted. ๐
If the return value is zero, errno is set (since Redis OSS 6.2) as follows:
EINVALif any unknown flags are set, or if key isNULL.ENOTSUPif the key is associated with a non Hash value.EBADFif the key was not opened for writing.ENOENTif no fields were counted as described under Return value above. This is not actually an error. The return value can be zero if all fields were just created and theCOUNT_ALLflag was unset, or if changes were held back due to theNXandXXflags.
NOTICE: The return value semantics of this function are very different between Redis OSS 6.2 and older versions. Modules that use it should determine the server version and handle it accordingly. like, fr fr, check the version first ๐จ
ValkeyModule_HashGet
int ValkeyModule_HashGet(ValkeyModuleKey *key, int flags, ...);
Available since: 4.0.0 โจ
Grab fields out of a hash value. Okay so here's the tea: this function is variadic, meaning you call it with a whole lil parade of arguments โ it alternates between a field name (a ValkeyModuleString pointer) and a pointer-to-a-ValkeyModuleString-pointer, which gets set to that field's value if the field exists, or NULL if it doesn't. And at the very end of all those field/value-ptr pairs? You must drop a NULL in there as the last argument to signal "okay we're done here" to the variadic function. No NULL, no closure. ๐
Here's how that looks in practice:
ValkeyModuleString *first, *second;
ValkeyModule_HashGet(mykey,VALKEYMODULE_HASH_NONE,argv[1],&first,
argv[2],&second,NULL);
And just like with ValkeyModule_HashSet(), you can change up the behavior by passing flags that aren't VALKEYMODULE_HASH_NONE:
VALKEYMODULE_HASH_CFIELDS: field names come in as null terminated C strings.VALKEYMODULE_HASH_EXISTS: instead of actually setting the value of the field โ which would normally want aValkeyModuleStringpointer-to-pointer โ the function just reports whether the field exists or not, and it expects an integer pointer as the second element of each pair. ๐ซถ
Example of VALKEYMODULE_HASH_CFIELDS:
ValkeyModuleString *username, *hashedpass;
ValkeyModule_HashGet(mykey,VALKEYMODULE_HASH_CFIELDS,"username",&username,"hp",&hashedpass, NULL);
Example of VALKEYMODULE_HASH_EXISTS:
int exists;
ValkeyModule_HashGet(mykey,VALKEYMODULE_HASH_EXISTS,argv[1],&exists,NULL);
The function returns VALKEYMODULE_OK on success, and VALKEYMODULE_ERR if the key isn't a hash value. That's the whole vibe check. ๐
Memory management:
Those ValkeyModuleString objects you get back? You gotta release them with ValkeyModule_FreeString(), or just enable automatic memory management and let it handle the cleanup for you. Bestie behavior honestly. ๐งนโจ
๐ฆ Key API for Stream type ๐ฆ
Wanna get introduced to streams first? Go peek at valkey-streams-intro(7). ๐
The type ValkeyModuleStreamID, which shows up all over the stream functions, is a struct with two 64-bit fields and is defined like this:
typedef struct ValkeyModuleStreamID {
uint64_t ms;
uint64_t seq;
} ValkeyModuleStreamID;
Also check out ValkeyModule_ValueLength(), which hands you the length of a stream, plus the conversion functions ValkeyModule_StringToStreamID() and ValkeyModule_CreateStringFromStreamID(). Conversion girlies stay winning. ๐
โญ ValkeyModule_StreamAdd โญ
int ValkeyModule_StreamAdd(ValkeyModuleKey *key,
int flags,
ValkeyModuleStreamID *id,
ValkeyModuleString **argv,
long numfields);
Available since: 6.2.0 ๐ธ
Adds an entry to a stream. It's giving XADD but with no trimming. Periodt.
key: The key where the stream is (or will be) stored ๐๏ธflags: A bit field ofVALKEYMODULE_STREAM_ADD_AUTOID: Assign a stream ID automatically, like*in theXADDcommand.
id: If theAUTOIDflag is set, this is where the assigned ID is returned. Can beNULLifAUTOIDis set, if you don't care to receive the ID. IfAUTOIDis not set, this is the requested ID.argv: A pointer to an array of sizenumfields * 2containing the fields and values.numfields: The number of field-value pairs inargv.
Returns VALKEYMODULE_OK if an entry has been added. On failure, VALKEYMODULE_ERR is returned and errno is set as follows:
EINVALif called with invalid argumentsENOTSUPif the key refers to a value of a type other than streamEBADFif the key was not opened for writingEDOMif the given ID was0-0or not greater than all other IDs in the stream (only if theAUTOIDflag is unset)EFBIGif the stream has reached the last possible IDERANGEif the elements are too large to be stored.
All of those errno values are very much real and very much your problem to handle, so don't sleep on 'em. ๐ญ
๐ ValkeyModule_StreamDelete ๐
int ValkeyModule_StreamDelete(ValkeyModuleKey *key, ValkeyModuleStreamID *id);
Available since: 6.2.0 ๐ธ
Deletes an entry from a stream. Bye bye bestie ๐
key: A key opened for writing, with no stream iterator started.id: The stream ID of the entry to delete.
Returns VALKEYMODULE_OK on success. On failure, VALKEYMODULE_ERR is returned and errno is set as follows:
EINVALif called with invalid argumentsENOTSUPif the key refers to a value of a type other than stream or if the key is emptyEBADFif the key was not opened for writing or if a stream iterator is associated with the keyENOENTif no entry with the given stream ID exists
Also go look at ValkeyModule_StreamIteratorDelete() if you wanna delete the current entry while iterating with a stream iterator. Different tool, same dream. ๐ฏ
๐ ValkeyModule_StreamIteratorStart ๐
int ValkeyModule_StreamIteratorStart(ValkeyModuleKey *key,
int flags,
ValkeyModuleStreamID *start,
ValkeyModuleStreamID *end);
Available since: 6.2.0 ๐ธ
Sets up a stream iterator. Let's goooo ๐
key: The stream key opened for reading usingValkeyModule_OpenKey().flags:VALKEYMODULE_STREAM_ITERATOR_EXCLUSIVE: Don't includestartandendin the iterated range.VALKEYMODULE_STREAM_ITERATOR_REVERSE: Iterate in reverse order, starting from the end of the range.
start: The lower bound of the range. UseNULLfor the beginning of the stream.end: The upper bound of the range. UseNULLfor the end of the stream.
Returns VALKEYMODULE_OK on success. On failure, VALKEYMODULE_ERR is returned and errno is set as follows:
EINVALif called with invalid argumentsENOTSUPif the key refers to a value of a type other than stream or if the key is emptyEBADFif the key was not opened for writing or if a stream iterator is already associated with the keyEDOMifstartorendis outside the valid range
Returns VALKEYMODULE_OK on success and VALKEYMODULE_ERR if the key doesn't refer to a stream or if invalid arguments were given.
Not me noticing it literally says "returns VALKEYMODULE_OK on success" twice in a row ๐ญ it's just built different, we don't question it, we keep both.
The stream IDs are retrieved using ValkeyModule_StreamIteratorNextID(), and for each stream ID, the fields and values are retrieved using ValkeyModule_StreamIteratorNextField(). The iterator gets freed by calling ValkeyModule_StreamIteratorStop(). The full squad: start โ next โ next โ stop. ๐
Example (error handling omitted):
ValkeyModule_StreamIteratorStart(key, 0, startid_ptr, endid_ptr);
ValkeyModuleStreamID id;
long numfields;
while (ValkeyModule_StreamIteratorNextID(key, &id, &numfields) ==
VALKEYMODULE_OK) {
ValkeyModuleString *field, *value;
while (ValkeyModule_StreamIteratorNextField(key, &field, &value) ==
VALKEYMODULE_OK) {
//
// ... Do stuff ...
//
ValkeyModule_FreeString(ctx, field);
ValkeyModule_FreeString(ctx, value);
}
}
ValkeyModule_StreamIteratorStop(key);
๐ ValkeyModule_StreamIteratorStop ๐
int ValkeyModule_StreamIteratorStop(ValkeyModuleKey *key);
Available since: 6.2.0 ๐ธ
Stops a stream iterator that was created using ValkeyModule_StreamIteratorStart() and reclaims its memory. Cleanup queen energy. ๐งผโจ
Returns VALKEYMODULE_OK on success. On failure, VALKEYMODULE_ERR is returned and errno is set as follows:
EINVALif called with aNULLkeyENOTSUPif the key refers to a value of a type other than stream or if the key is emptyEBADFif the key was not opened for writing or if no stream iterator is associated with the key
๐ ValkeyModule_StreamIteratorNextID ๐
int ValkeyModule_StreamIteratorNextID(ValkeyModuleKey *key,
ValkeyModuleStreamID *id,
long *numfields);
Available since: 6.2.0 ๐ธ
Finds the next stream entry and returns its stream ID plus the number of fields. ๐
key: Key for which a stream iterator has been started usingValkeyModule_StreamIteratorStart().id: The stream ID returned.NULLif you don't care.numfields: The number of fields in the found stream entry.NULLif you don't care.
Returns VALKEYMODULE_OK and sets *id and *numfields if an entry was found. On failure, VALKEYMODULE_ERR is returned and errno is set as follows:
EINVALif called with aNULLkeyENOTSUPif the key refers to a value of a type other than stream or if the key is emptyEBADFif no stream iterator is associated with the keyENOENTif there are no more entries in the range of the iterator
In practice, if ValkeyModule_StreamIteratorNextID() is called after a successful call to ValkeyModule_StreamIteratorStart() and with the same key, it is safe to assume that an VALKEYMODULE_ERR return value means that there are no more entries. No cap, you can trust that one. ๐ซก
Use ValkeyModule_StreamIteratorNextField() to retrieve the fields and values. See the example at ValkeyModule_StreamIteratorStart(). ๐
๐ง ValkeyModule_StreamIteratorNextField ๐ง
int ValkeyModule_StreamIteratorNextField(ValkeyModuleKey *key,
ValkeyModuleString **field_ptr,
ValkeyModuleString **value_ptr);
Available since: 6.2.0 ๐ธ
Retrieves the next field of the current stream ID and its corresponding value during a stream iteration. This one should be called over and over after calling ValkeyModule_StreamIteratorNextID(), fetching each field-value pair one at a time. ๐
key: Key where a stream iterator has been started.field_ptr: This is where the field is returned.value_ptr: This is where the value is returned.
Returns VALKEYMODULE_OK and points *field_ptr and *value_ptr to freshly allocated ValkeyModuleString objects. Those string objects get freed automatically when the callback finishes if automatic memory is enabled. On failure, VALKEYMODULE_ERR is returned and errno is set as follows:
EINVALif called with aNULLkeyENOTSUPif the key refers to a value of a type other than stream or if the key is emptyEBADFif no stream iterator is associated with the keyENOENTif there are no more fields in the current stream entry
In practice, if ValkeyModule_StreamIteratorNextField() is called after a successful call to ValkeyModule_StreamIteratorNextID() and with the same key, it is safe to assume that an VALKEYMODULE_ERR return value means that there are no more fields. Same energy as before, lowkey iconic. ๐
See the example at ValkeyModule_StreamIteratorStart(). ๐
๐ฅ ValkeyModule_StreamIteratorDelete ๐ฅ
int ValkeyModule_StreamIteratorDelete(ValkeyModuleKey *key);
Available since: 6.2.0 ๐ธ
Deletes the current stream entry while iterating. Chaotic? No bestie, it's efficient. ๐
This function can be called after ValkeyModule_StreamIteratorNextID() or after any calls to ValkeyModule_StreamIteratorNextField().
Returns VALKEYMODULE_OK on success. On failure, VALKEYMODULE_ERR is returned and errno gets set like this:
EINVALifkeyisNULLENOTSUPif the key is empty or is of another type than streamEBADFif the key is not opened for writing, if no iterator has been startedENOENTif the iterator has no current stream entry
ValkeyModule_StreamTrimByLength ๐โโ๏ธ
long long ValkeyModule_StreamTrimByLength(ValkeyModuleKey *key,
int flags,
long long length);
Available since: 6.2.0
Trim a stream by length โ it's giving XTRIM with MAXLEN, bestie. Same energy, different door.
keyโ key opened for writing.flagsโ a bitfield of:VALKEYMODULE_STREAM_TRIM_APPROXโ trim less if it improves performance, likeXTRIMwith~.
lengthโ the number of stream entries to keep after trimming.
Returns the number of entries deleted. On failure, a negative value comes back and errno is set as follows:
EINVALif called with invalid argumentsENOTSUPif the key is empty or of a type other than streamEBADFif the key is not opened for writing
ValkeyModule_StreamTrimByID ๐ชช
long long ValkeyModule_StreamTrimByID(ValkeyModuleKey *key,
int flags,
ValkeyModuleStreamID *id);
Available since: 6.2.0
Trim a stream by ID โ literally XTRIM with MINID. Slay.
keyโ key opened for writing.flagsโ a bitfield of:VALKEYMODULE_STREAM_TRIM_APPROXโ trim less if it improves performance, likeXTRIMwith~.
idโ the smallest stream ID to keep after trimming.
Returns the number of entries deleted. On failure, a negative value comes back and errno is set as follows:
EINVALif called with invalid argumentsENOTSUPif the key is empty or of a type other than streamEBADFif the key is not opened for writing
โจ Calling commands from modules โจ
ValkeyModule_Call() sends a command to the server. The remaining functions handle the reply. That's the whole vibe check: you shout a command into the void, you get a reply object back, and then you gotta deal with it. ๐ซถ
ValkeyModule_FreeCallReply ๐๏ธ
void ValkeyModule_FreeCallReply(ValkeyModuleCallReply *reply);
Available since: 4.0.0
Free a Call reply โ and all the nested replies it contains if it's an array. So you don't have to go walking the whole tree yourself, bestie. One call, everything's gone. Cleanup queen behaviour. ๐
ValkeyModule_CallReplyType ๐ท๏ธ
int ValkeyModule_CallReplyType(ValkeyModuleCallReply *reply);
Available since: 4.0.0
Return the reply type as one of the following:
VALKEYMODULE_REPLY_UNKNOWNVALKEYMODULE_REPLY_STRINGVALKEYMODULE_REPLY_ERRORVALKEYMODULE_REPLY_INTEGERVALKEYMODULE_REPLY_ARRAYVALKEYMODULE_REPLY_NULLVALKEYMODULE_REPLY_MAPVALKEYMODULE_REPLY_SETVALKEYMODULE_REPLY_BOOLVALKEYMODULE_REPLY_DOUBLEVALKEYMODULE_REPLY_BIG_NUMBERVALKEYMODULE_REPLY_VERBATIM_STRINGVALKEYMODULE_REPLY_ATTRIBUTEVALKEYMODULE_REPLY_PROMISE
Check it before you touch it, fr fr. No assumptions. ๐
ValkeyModule_CallReplyLength ๐
size_t ValkeyModule_CallReplyLength(ValkeyModuleCallReply *reply);
Available since: 4.0.0
Return the reply type length, where applicable. That last part is doing a lot of heavy lifting, so mind it. ๐
ValkeyModule_CallReplyArrayElement ๐งบ
ValkeyModuleCallReply *ValkeyModule_CallReplyArrayElement(ValkeyModuleCallReply *reply,
size_t idx);
Available since: 4.0.0
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. Two ways to get NULL, bestie โ check before you dereference. ๐ญ
ValkeyModule_CallReplyInteger ๐ข
long long ValkeyModule_CallReplyInteger(ValkeyModuleCallReply *reply);
Available since: 4.0.0
Return the long long of an integer reply. Big number energy, small number type. ๐
ValkeyModule_CallReplyDouble ๐ฅ
double ValkeyModule_CallReplyDouble(ValkeyModuleCallReply *reply);
Available since: 7.0.0
Return the double value of a double reply. Decimals finally getting their moment. โจ
ValkeyModule_CallReplyBigNumber ๐
const char *ValkeyModule_CallReplyBigNumber(ValkeyModuleCallReply *reply,
size_t *len);
Available since: 7.0.0
Return the big number value of a big number reply. It comes back as a string plus a length, because some numbers are simply too iconic to fit in a normal type. ๐
ValkeyModule_CallReplyVerbatim ๐
const char *ValkeyModule_CallReplyVerbatim(ValkeyModuleCallReply *reply,
size_t *len,
const char **format);
Available since: 7.0.0
Return the value of a verbatim string reply. An optional output argument can be given to get the verbatim reply format. Optional means you can pass it or not, no pressure. ๐
ValkeyModule_CallReplyBool โ
int ValkeyModule_CallReplyBool(ValkeyModuleCallReply *reply);
Available since: 7.0.0
Return the Boolean value of a Boolean reply. Yes or no. That's it. That's the function. ๐ธ
ValkeyModule_CallReplySetElement ๐ฏ
ValkeyModuleCallReply *ValkeyModule_CallReplySetElement(ValkeyModuleCallReply *reply,
size_t idx);
Available since: 7.0.0
Return the idx-th nested call reply element of a set reply, or NULL if the reply type is wrong or the index is out of range. Same cautionary tale as the array one, just in a different outfit. ๐
ValkeyModule_CallReplyMapElement ๐บ๏ธ
int ValkeyModule_CallReplyMapElement(ValkeyModuleCallReply *reply,
size_t idx,
ValkeyModuleCallReply **key,
ValkeyModuleCallReply **val);
Available since: 7.0.0
Retrieve the idx-th key and value of a map reply.
Returns:
VALKEYMODULE_OKon success.VALKEYMODULE_ERRifidxout of range or if the reply type is wrong.
The key and val arguments are used to return by reference, and may be NULL if not required. So you can totally only care about half the pair, no judgement. ๐
ValkeyModule_CallReplyAttribute ๐ท๏ธโจ
ValkeyModuleCallReply *ValkeyModule_CallReplyAttribute(ValkeyModuleCallReply *reply);
Available since: 7.0.0
Return the attribute of the given reply, or NULL if no attribute exists. Reply but make it accessorised. ๐
ValkeyModule_CallReplyAttributeElement ๐
int ValkeyModule_CallReplyAttributeElement(ValkeyModuleCallReply *reply,
size_t idx,
ValkeyModuleCallReply **key,
ValkeyModuleCallReply **val);
Available since: 7.0.0
Retrieve the idx-th key and value of an attribute reply.
Returns:
VALKEYMODULE_OKon success.VALKEYMODULE_ERRifidxout of range or if the reply type is wrong.
The key and val arguments are used to return by reference, and may be NULL if not required. Same deal as the map version, just for attributes. ๐ฆ
ValkeyModule_CallReplyPromiseSetUnblockHandler ๐
void ValkeyModule_CallReplyPromiseSetUnblockHandler(ValkeyModuleCallReply *reply,
ValkeyModuleOnUnblocked on_unblock,
void *private_data);
Available since: 7.2.0
Set an unblock handler (callback and private data) on the given promise ValkeyModuleCallReply. The given reply must be of promise type (VALKEYMODULE_REPLY_PROMISE). No prom, no handler, bestie โ that's the rule. ๐ซถ
ValkeyModule_CallReplyPromiseAbort ๐
int ValkeyModule_CallReplyPromiseAbort(ValkeyModuleCallReply *reply,
void **private_data);
Available since: 7.2.0
Abort the execution of a given promise ValkeyModuleCallReply. Returns REDMODULE_OK in case the abort was done successfully and VALKEYMODULE_ERR if it's not possible to abort the execution (execution already finished). Yes, the docs literally say REDMODULE_OK there. Not a typo of mine. ๐
In case the execution was aborted (REDMODULE_OK was returned), the private_data out parameter will be set with the value of the private data that was given on ValkeyModule_CallReplyPromiseSetUnblockHandler, so the caller will be able to release the private data. Handing the luggage back so nobody leaks it. Iconic.
If the execution was aborted successfully, it is promised that the unblock handler will not be called. That said, it is possible that the abort operation will succeed but the operation will still continue. This can happen if, for example, a module implements some blocking command and does not respect the disconnect callback. For server-provided commands this can not happen.
Lowkey important: that "aborted successfully but still running" case is real and it's not your fault. But for server-provided commands, it can't happen. So know which one you're dealing with. ๐ค
ValkeyModule_CallReplyStringPtr ๐ชก
const char *ValkeyModule_CallReplyStringPtr(ValkeyModuleCallReply *reply,
size_t *len);
Available since: 4.0.0
Return the pointer and length of a string or error reply. Errors are strings too in this house. โจ
ValkeyModule_CreateStringFromCallReply ๐งต
ValkeyModuleString *ValkeyModule_CreateStringFromCallReply(ValkeyModuleCallReply *reply);
Available since: 4.0.0
Return a new string object from a call reply of type string, error or integer. Otherwise (wrong reply type) return NULL. Three types welcome, everything else gets the door. ๐
ValkeyModule_SetContextUser ๐ค
void ValkeyModule_SetContextUser(ValkeyModuleCtx *ctx,
const ValkeyModuleUser *user);
Available since: 7.0.6
Modifies the user that ValkeyModule_Call will use (e.g. for ACL checks). Identity crisis, but make it deliberate. ๐
ValkeyModule_Call ๐
ValkeyModuleCallReply *ValkeyModule_Call(ValkeyModuleCtx *ctx,
const char *cmdname,
const char *fmt,
...);
Available since: 4.0.0
The exported API to call any command from modules. This is the main character of the section, no cap.
cmdnameโ the command to call.fmtโ a format specifier string for the command's arguments. Each of the arguments should be specified by a valid type specification. The format specifier can also contain the modifiers!,A,3andR, which don't have a corresponding argument.bโ the argument is a buffer and is immediately followed by another argument that is the buffer's length.cโ the argument is a pointer to a plain C string (null-terminated).lโ the argument is along longinteger.sโ the argument is aValkeyModuleString.vโ the argument(s) is a vector ofValkeyModuleString.!โ sends the command and its arguments to replicas and AOF.Aโ suppress AOF propagation, send only to replicas (requires!).Rโ suppress replicas propagation, send only to AOF (requires!).3โ return a RESP3 reply. This will change the command reply. e.g.,HGETALLreturns a map instead of a flat array.0โ return the reply in auto mode, i.e. the reply format will be the same as the client attached to the givenValkeyModuleCtx. This will probably used when you want to pass the reply directly to the client.
Heads up bestie:AandRboth need!to even work. Don't be out here trying to suppress propagation on a command you never told to propagate. That's not a vibe, that's a bug. ๐ซ
Cโ run the command as the user attached to the context, bestie ๐. That user got attached either automatically via the client that directly issued the command and created the context, or viaValkeyModule_SetContextUser. Heads up though: if the context was not directly created by an issued command (like a background context, and nobody set a user on it viaValkeyModule_SetContextUser), thenValkeyModule_Callwill fail. This flag checks whether the command can be executed according to ACL rules and makes it run as that determined user, so any future user-dependent activity โ like ACL checks inside scripts โ proceeds as expected. Otherwise the command just runs as the unrestricted user. No cap.Sโ run the command in script mode ๐. Translation: it'll raise an error if you invoke a command that isn't allowed inside a script (any command flagged with thedeny-scriptflag, likeSHUTDOWN๐ณ). Also, in script mode, write commands are not allowed if there aren't enough good replicas (as configured withmin-replicas-to-write) or when the server can't persist to disk.Wโ do not allow running any write command (any command flagged with thewriteflag) ๐ โโ๏ธ.Mโ do not allowdeny-oomflagged commands when you're over the memory limit ๐ง ๐ฅ.Eโ return errors as aValkeyModuleCallReply. If there's an error before invoking the command, the error comes back through theerrnomechanism. This flag is what lets you get the error also as an errorCallReplywith the relevant error message, periodt.D'โ a โDry Runโ mode ๐โโ๏ธ๐จ. It returns before executing the underlyingcall(). If everything succeeded, it returns aNULL; otherwise it returns aCallReplyobject denoting the error, exactly as if it had been called with theE'code.K'โ allow running blocking commands ๐. If it's enabled and the command gets blocked, a specialVALKEYMODULE_REPLY_PROMISEgets returned. This reply type is basically saying โthis command got blocked and the reply is coming asynchronously, iykyk.โ The module can use this reply object to set a handler that'll be called when the command gets unblocked, usingValkeyModule_CallReplyPromiseSetUnblockHandler. The handler must be set immediately after the command invocation (without releasing the lock in between, fr fr). If the handler isn't set, the blocking command still continues its execution but the reply gets ignored (fire and forget) โ and notice that this is dangerous in the case of a role change, as explained below. The module can useValkeyModule_CallReplyPromiseAbortto abort the command invocation if it wasn't yet finished (see theValkeyModule_CallReplyPromiseAbortdocumentation for more details). It's also the module's responsibility to abort the execution on role change, either by using a server event (to get notified when the instance becomes a replica) or by relying on the disconnect callback of the original client. Failing to do so can result in a write operation on a replica ๐ญ. Unlike other call replies, a promise call reply must be freed while the GIL is locked. Notice that on unblocking, the only promise is that the unblock handler will be called โ if the blockingValkeyModule_Callcaused the module to also block some real client (usingValkeyModule_BlockClient), it's the module's responsibility to unblock that client on the unblock handler. On the unblock handler it's only allowed to perform the following:- Calling additional commands using
ValkeyModule_Call - Open keys using
ValkeyModule_OpenKey - Replicate data to the replica or AOF
- Calling additional commands using
Specifically, it is not allowed to call any module API which are client related such as: -ValkeyModule_Reply*API's -ValkeyModule_BlockClient-ValkeyModule_GetCurrentUserName
...โ the actual arguments to the command ๐ซถ.
On success, a ValkeyModuleCallReply object is returned. Otherwise NULL is returned and errno is set to one of these values:
EBADF: wrong format specifier ๐.EINVAL: wrong command arity.ENOENT: command does not exist.EPERM: operation in a Cluster instance with a key in a non-local slot.EROFS: operation in a Cluster instance when a write command is sent in a readonly state.ENETDOWN: operation in a Cluster instance when the cluster is down.ENOTSUP: no ACL user for the specified module context.EACCES: command cannot be executed, according to ACL rules.ENOSPC: write ordeny-oomcommand is not allowed.ESPIPE: command not allowed on script mode.
Example code fragment, cute and to the point โจ:
reply = ValkeyModule_Call(ctx,"INCRBY","sc",argv[1],"10");
if (ValkeyModule_CallReplyType(reply) == VALKEYMODULE_REPLY_INTEGER) {
long long myval = ValkeyModule_CallReplyInteger(reply);
// Do something with myval.
}
This API is documented here: valkey-modules-intro(7) ๐.
๐ธ ValkeyModule_CallReplyProto ๐ธ
const char *ValkeyModule_CallReplyProto(ValkeyModuleCallReply *reply,
size_t *len);
Available since: 4.0.0
Return a pointer, and a length, to the protocol returned by the command that returned the reply object ๐.
๐ Modules data types ๐
When String DMA or using existing data structures just isn't enough, you can create brand-new data types from scratch, bestie ๐ฆ. The module has to provide a set of callbacks for handling the new values it exports (for example, in order to provide RDB saving/loading, AOF rewrite, and so forth). This section defines this API.
๐ ValkeyModule_CreateDataType ๐
moduleType *ValkeyModule_CreateDataType(ValkeyModuleCtx *ctx,
const char *name,
int encver,
void *typemethods_ptr);
Available since: 4.0.0
Register a new data type exported by the module ๐ฅ. Here are the parameters โ and please, for in-depth documentation, check the modules API documentation, especially valkey-modules-native-types(7).
name: a 9-character data type name that MUST be unique in the Modules ecosystem. Be creative... and there will be no collisions ๐ . Use the charsetA-Za-z9-0, plus the two-_characters. A good idea is to use, for example<typename>-<vendor>. For example,tree-AntZmay mean โTree data structure by @antirezโ. Using both lower case and upper case letters helps in order to prevent collisions.encver: the encoding version โ which is, the version of the serialization that a module used in order to persist data. As long as thenamematches, the RDB loading gets dispatched to the type callbacks whateverencveris used, however the module can understand if the encoding it must load is of an older version of the module. For example, the moduletree-AntZinitially usedencver=0. Later, after an upgrade, it started to serialize data in a different format and to register the type withencver=1. However, this module may still load old data produced by an older version if therdb_loadcallback is able to check theencvervalue and act accordingly. Theencvermust be a positive value between0and1023.typemethods_ptris a pointer to aValkeyModuleTypeMethodsstructure that should be populated with the methods callbacks and structure version, like in the following example:
ValkeyModuleTypeMethods tm = {
.version = VALKEYMODULE_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_load = myType_AuxRDBLoadCallBack,
.aux_save = myType_AuxRDBSaveCallBack,
.free_effort = myType_FreeEffortCallBack,
.unlink = myType_UnlinkCallBack,
.copy = myType_CopyCallback,
.defrag = myType_DefragCallback
// Enhanced optional fields
.mem_usage2 = myType_MemUsageCallBack2,
.free_effort2 = myType_FreeEffortCallBack2,
.unlink2 = myType_UnlinkCallBack2,
.copy2 = myType_CopyCallback2,
}
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.digest: a callback function pointer that is used forDEBUG DIGEST.free: a callback function pointer that can free a type value.aux_save: a callback function pointer that saves out-of-keyspace data to RDB files. Thewhenargument is eitherVALKEYMODULE_AUX_BEFORE_RDBorVALKEYMODULE_AUX_AFTER_RDB.aux_load: a callback function pointer that loads out-of-keyspace data from RDB files. Similar toaux_save, it returnsVALKEYMODULE_OKon success, andERRotherwise.free_effort: a callback function pointer that's used to determine whether the module's memory needs to be lazy reclaimed. The module should return the complexity involved by freeing the value โ for example, how many pointers are gonna be freed. Note that if it returns0, we'll always do an async free โก.unlink: a callback function pointer that's used to notify the module that the key has been removed from the DB by the server, and may soon be freed by a background thread. Note that it won't be called onFLUSHALL/FLUSHDB(both sync and async), and the module can use theValkeyModuleEvent_FlushDBto hook into that ๐ช.copy: a callback function pointer that's used to make a copy of the specified key. The module is expected to perform a deep copy of the specified value and return it. In addition, hints about the names of the source and destination keys is provided. ANULLreturn value is considered an error and the copy operation fails. Note: if the target key exists and is being overwritten, the copy callback will be called first, followed by a free callback to the value that is being replaced ๐.defragโ a callback function pointer that's basically the "hey bestie, go tidy this key up" signal ๐ it asks the module to defrag a key. The module should then iterate pointers and call the relevantValkeyModule_Defrag*()functions to defragment pointers or complex types. The module should keep iterating as long asValkeyModule_DefragShouldStop()returns a zero value, and return a zero value if finished or non-zero value if more work is left to be done. If more work needs to be done,ValkeyModule_DefragCursorSet()andValkeyModule_DefragCursorGet()can be used to track this work across different calls. Normally, the defrag mechanism invokes the callback without a time limit, soValkeyModule_DefragShouldStop()always returns zero. The โlate defragโ mechanism which has a time limit and provides cursor support is used only for keys that are determined to have significant internal complexity. To determine this, the defrag mechanism uses thefree_effortcallback and theactive-defrag-max-scan-fieldsconfig directive. NOTE: the value is passed as avoid**and the function is expected to update the pointer if the top-level value pointer is defragmented and consequently changes. ๐mem_usage2โ same vibe asmem_usage, but it also hands you theValkeyModuleKeyOptCtxparameter so you can get at meta information like key name and db id, plus thesample_sizefor size estimation (peep theMEMORY USAGEcommand). โจfree_effort2โ same vibe asfree_effort, but it also hands you theValkeyModuleKeyOptCtxparameter so you can get at meta information like key name and db id. ๐ฆunlink2โ same vibe asunlink, but it also hands you theValkeyModuleKeyOptCtxparameter so you can get at meta information like key name and db id. ๐ธcopy2โ same vibe ascopy, but it also hands you theValkeyModuleKeyOptCtxparameter so you can get at meta information like key names and db ids. ๐aux_save2โ same vibe asaux_save, but with a small semantic change: if the module saves nothing on this callback then no data about this aux field will be written to the RDB, and it will be possible to load the RDB even if the module is not loaded. lowkey iconic ๐
Note: the module name AAAAAAAAA is reserved and produces an error, it happens to be pretty lame as well. ๐ (not us picking a nine-A name, bestie, please)
If ValkeyModule_CreateDataType() is called outside of ValkeyModule_OnLoad() function, there is already a module registering a type with the same name, or if the module name or encver is invalid, NULL is returned. Otherwise the new type is registered into the server, and a reference of type ValkeyModuleType is returned: the caller of the function should store this reference into a global variable to make future use of it in the modules type API, since a single module may register multiple types. Example code fragment:
static ValkeyModuleType *BalancedTreeType;
int ValkeyModule_OnLoad(ValkeyModuleCtx *ctx) {
// some code here ...
BalancedTreeType = ValkeyModule_CreateDataType(...);
}
ValkeyModule_ModuleTypeSetValue
int ValkeyModule_ModuleTypeSetValue(ValkeyModuleKey *key,
moduleType *mt,
void *value);
Available since: 4.0.0
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 VALKEYMODULE_OK is returned. If the key is not open for writing or there is an active iterator, VALKEYMODULE_ERR is returned. no cap, you gotta have that key open for writing ๐
ValkeyModule_ModuleTypeGetType
moduleType *ValkeyModule_ModuleTypeGetType(ValkeyModuleKey *key);
Available since: 4.0.0
Assuming ValkeyModule_KeyType() returned VALKEYMODULE_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.
ValkeyModule_ModuleTypeGetValue
void *ValkeyModule_ModuleTypeGetValue(ValkeyModuleKey *key);
Available since: 4.0.0
Assuming ValkeyModule_KeyType() returned VALKEYMODULE_KEYTYPE_MODULE on the key, returns the module type low-level value stored at key, as it was set by the user via ValkeyModule_ModuleTypeSetValue(). ๐
If the key is NULL, is not associated with a module type, or is empty, then NULL is returned instead.
โจ RDB loading and saving functions โจ
ValkeyModule_IsIOError
int ValkeyModule_IsIOError(ValkeyModuleIO *io);
Available since: 6.0.0
Returns true if any previous IO API failed. for Load* APIs the VALKEYMODULE_OPTIONS_HANDLE_IO_ERRORS flag must be set with ValkeyModule_SetModuleOptions first. โ ๏ธ don't skip that step bestie
ValkeyModule_SaveUnsigned
void ValkeyModule_SaveUnsigned(ValkeyModuleIO *io, uint64_t value);
Available since: 4.0.0
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. ๐ฅ
ValkeyModule_LoadUnsigned
uint64_t ValkeyModule_LoadUnsigned(ValkeyModuleIO *io);
Available since: 4.0.0
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. โญ
ValkeyModule_SaveSigned
void ValkeyModule_SaveSigned(ValkeyModuleIO *io, int64_t value);
Available since: 4.0.0
Like ValkeyModule_SaveUnsigned() but for signed 64 bit values. ๐
ValkeyModule_LoadSigned
int64_t ValkeyModule_LoadSigned(ValkeyModuleIO *io);
Available since: 4.0.0
Like ValkeyModule_LoadUnsigned() but for signed 64 bit values. ๐ง
ValkeyModule_SaveString
void ValkeyModule_SaveString(ValkeyModuleIO *io, ValkeyModuleString *s);
Available since: 4.0.0
In the context of the rdb_save method of a module type, saves a string into the RDB file taking as input a ValkeyModuleString. ๐ธ
The string can be later loaded with ValkeyModule_LoadString() or other Load family functions expecting a serialized string inside the RDB file.
ValkeyModule_SaveStringBuffer
void ValkeyModule_SaveStringBuffer(ValkeyModuleIO *io,
const char *str,
size_t len);
Available since: 4.0.0
Like ValkeyModule_SaveString() but takes a raw C pointer and length as input. ๐
ValkeyModule_LoadString
ValkeyModuleString *ValkeyModule_LoadString(ValkeyModuleIO *io);
Available since: 4.0.0
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 ValkeyModule_SaveString() functions family. โจ
The returned string is a newly allocated ValkeyModuleString object, and the user should at some point free it with a call to ValkeyModule_FreeString(). ๐ซถ (yes, that's on you, don't leak, bestie)
If the data structure does not store strings as ValkeyModuleString objects, the similar function ValkeyModule_LoadStringBuffer() could be used instead.
ValkeyModule_LoadStringBuffer
char *ValkeyModule_LoadStringBuffer(ValkeyModuleIO *io, size_t *lenptr);
Available since: 4.0.0
Like ValkeyModule_LoadString() but returns a heap allocated string that was allocated with ValkeyModule_Alloc(), and can be resized or freed with ValkeyModule_Realloc() or ValkeyModule_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 inside the RDB file. โ ๏ธ so do not go assuming it ends nicely
ValkeyModule_SaveDouble
void ValkeyModule_SaveDouble(ValkeyModuleIO *io, double value);
Available since: 4.0.0
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 ValkeyModule_LoadDouble(). ๐ฅ
ValkeyModule_LoadDouble
double ValkeyModule_LoadDouble(ValkeyModuleIO *io);
Available since: 4.0.0
In the context of the rdb_save method of a module data type, loads back the double value saved by ValkeyModule_SaveDouble(). ๐
ValkeyModule_SaveFloat
void ValkeyModule_SaveFloat(ValkeyModuleIO *io, float value);
Available since: 4.0.0
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 ValkeyModule_LoadFloat(). ๐
ValkeyModule_LoadFloat
float ValkeyModule_LoadFloat(ValkeyModuleIO *io);
Available since: 4.0.0
In the context of the rdb_save method of a module data type, loads back the float value saved by ValkeyModule_SaveFloat(). ๐ฆ
ValkeyModule_SaveLongDouble
void ValkeyModule_SaveLongDouble(ValkeyModuleIO *io, long double value);
Available since: 6.0.0
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 ValkeyModule_LoadLongDouble(). โญ
ValkeyModule_LoadLongDouble
long double ValkeyModule_LoadLongDouble(ValkeyModuleIO *io);
Available since: 6.0.0
In the context of the rdb_save method of a module data type, loads back the long double value saved by ValkeyModule_SaveLongDouble(). ๐
๐ Key digest API (DEBUG DIGEST interface for modules types) ๐
ValkeyModule_DigestAddStringBuffer
void ValkeyModule_DigestAddStringBuffer(ValkeyModuleDigest *md,
const char *ele,
size_t len);
Available since: 4.0.0
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 ValkeyModule_DigestEndSequence eventually, when all the elements that are always in a given order are added. See the Modules data types documentation for more info. However this is a quick example that uses the Set, Hash and List data types as an example. ๐
To add a sequence of unordered elements (for example in the case of a 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 a Hash, then one should use:
foreach key,value {
AddElement(key);
AddElement(value);
EndSequence();
}
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 hash. ๐
A list of ordered elements would be implemented with:
foreach element {
AddElement(element);
}
EndSequence();
ValkeyModule_DigestAddLongLong
void ValkeyModule_DigestAddLongLong(ValkeyModuleDigest *md, long long ll);
Available since: 4.0.0
okay so this is literally the same vibe as ValkeyModule_DigestAddStringBuffer() except it takes a long long as input, and that long long gets converted into a string before it's added to the digest. slay ๐ซถ
ValkeyModule_DigestEndSequence
void ValkeyModule_DigestEndSequence(ValkeyModuleDigest *md);
Available since: 4.0.0
bestie, go read the docs for ValkeyModule_DigestAddElement() โ that's where the tea is ๐
ValkeyModule_LoadDataTypeFromStringEncver
void *ValkeyModule_LoadDataTypeFromStringEncver(const ValkeyModuleString *str,
const moduleType *mt,
int encver);
Available since: 7.0.0
This one decodes a serialized representation of a module data type mt, in a specific encoding version encver, from the string str, and it returns a newly allocated value โ or NULL if the decoding failed. ๐ญ
Basically this call reuses the rdb_load callback that module data types implement, which is what lets a module arbitrarily serialize/de-serialize keys, similar to how the DUMP and RESTORE commands are implemented. iconic tbh โจ
Modules should generally use theVALKEYMODULE_OPTIONS_HANDLE_IO_ERRORSflag and make sure the de-serialization code properly checks and handles IO errors (freeing allocated buffers and returning aNULL).
If this is NOT done, the server will handle corrupted (or just truncated) serialized data by producing an error message and terminating the process. ๐ so like, do the flag, bestie.
ValkeyModule_LoadDataTypeFromString
void *ValkeyModule_LoadDataTypeFromString(const ValkeyModuleString *str,
const moduleType *mt);
Available since: 6.0.0
Same energy as ValkeyModule_LoadDataTypeFromStringEncver โ this is the original version of the API, kept around for backward compatibility. ๐
ValkeyModule_SaveDataTypeToString
ValkeyModuleString *ValkeyModule_SaveDataTypeToString(ValkeyModuleCtx *ctx,
void *data,
const moduleType *mt);
Available since: 6.0.0
Encodes a module data type mt value data into serialized form, and returns it as a newly allocated ValkeyModuleString. ๐ฆ
And yes, this call also basically reuses the rdb_save callback that module data types implement, so a module can arbitrarily serialize/de-serialize keys โ same deal as how the DUMP and RESTORE commands are implemented. iykyk.
ValkeyModule_GetKeyNameFromDigest
const ValkeyModuleString *ValkeyModule_GetKeyNameFromDigest(ValkeyModuleDigest *dig);
Available since: 7.0.0
Returns the name of the key currently being processed. ๐
ValkeyModule_GetDbIdFromDigest
int ValkeyModule_GetDbIdFromDigest(ValkeyModuleDigest *dig);
Available since: 7.0.0
Returns the database id of the key currently being processed. ๐
AOF API for modules data types
ValkeyModule_EmitAOF
void ValkeyModule_EmitAOF(ValkeyModuleIO *io,
const char *cmdname,
const char *fmt,
...);
Available since: 4.0.0
This emits a command into the AOF during the AOF rewriting process. ๐ It's only called in the context of the aof_rewrite method of data types exported by a module.
The command works exactly like ValkeyModule_Call() in the way the parameters are passed โ but it doesn't return anything, because the error handling is performed by the server itself. no cap ๐ซถ
IO context handling
ValkeyModule_GetKeyNameFromIO
const ValkeyModuleString *ValkeyModule_GetKeyNameFromIO(ValkeyModuleIO *io);
Available since: 5.0.5
Returns the name of the key currently being processed. Heads up though: there is no guarantee that the key name is always available, so this may return NULL. ๐
ValkeyModule_GetKeyNameFromModuleKey
const ValkeyModuleString *ValkeyModule_GetKeyNameFromModuleKey(ValkeyModuleKey *key);
Available since: 6.0.0
Returns a ValkeyModuleString with the name of the key from ValkeyModuleKey. โจ
ValkeyModule_GetDbIdFromModuleKey
int ValkeyModule_GetDbIdFromModuleKey(ValkeyModuleKey *key);
Available since: 7.0.0
Returns a database id of the key from ValkeyModuleKey. ๐
ValkeyModule_GetDbIdFromIO
int ValkeyModule_GetDbIdFromIO(ValkeyModuleIO *io);
Available since: 7.0.0
Returns the database id of the key currently being processed. And same little caveat as before: there is no guarantee that this info is always available, so this may return -1. ๐ญ
Logging
ValkeyModule_Log
void ValkeyModule_Log(ValkeyModuleCtx *ctx,
const char *levelstr,
const char *fmt,
...);
Available since: 4.0.0
Produces a log message to the standard server log. ๐ธ The format accepts printf-alike specifiers, while level is a string describing the log level to use when emitting the log, and it must be one of these:
- โdebugโ (
VALKEYMODULE_LOGLEVEL_DEBUG) - โverboseโ (
VALKEYMODULE_LOGLEVEL_VERBOSE) - โnoticeโ (
VALKEYMODULE_LOGLEVEL_NOTICE) - โwarningโ (
VALKEYMODULE_LOGLEVEL_WARNING)
If the specified log level is invalid, verbose is used by default. There's also a fixed limit to the length of the log line this function is able to emit โ the limit is not specified, but it's guaranteed to be more than a few lines of text. โ๏ธ
The ctx argument may be NULL if it cannot be provided in the context of the caller, for instance threads or callbacks โ in which case a generic โmoduleโ will be used instead of the module name. ๐ซก
ValkeyModule_LogIOError
void ValkeyModule_LogIOError(ValkeyModuleIO *io,
const char *levelstr,
const char *fmt,
...);
Available since: 4.0.0
Log errors from RDB / AOF serialization callbacks. ๐ฅ
This function should be used when a callback is returning a critical error to the caller, since it cannot load or save the data for some critical reason. like, it's giving "something went very wrong" energy.
ValkeyModule__Assert
void ValkeyModule__Assert(const char *estr, const char *file, int line);
Available since: 6.0.0
Valkey assert function. ๐
The macro ValkeyModule_Assert(expression) is recommended, rather than calling this function directly.
A failed assertion will shut down the server and produce logging information that looks identical to information generated by the server itself. ๐ iconic but terrifying.
ValkeyModule_LatencyAddSample
void ValkeyModule_LatencyAddSample(const char *event, mstime_t latency);
Available since: 6.0.0
Lets you add an event to the latency monitor so it can be observed by the LATENCY command. ๐
The call is skipped if the latency is smaller than the configured latency-monitor-threshold. so it's not gonna spam you with tiny little blips, bestie.
Blocking clients from modules
For a guide about blocking commands in modules, see valkey-modules-blocking-ops(7). ๐
ValkeyModule_RegisterAuthCallback
void ValkeyModule_RegisterAuthCallback(ValkeyModuleCtx *ctx,
ValkeyModuleAuthCallback cb);
Available since: 7.2.0
This API registers a callback to execute in addition to normal password based authentication. Multiple callbacks can be registered across different modules. When a Module is unloaded, all the auth callbacks registered by it are unregistered. ๐ง
The callbacks are attempted (in the order of most recently registered first) when the AUTH/HELLO (with AUTH field provided) commands are called. The callbacks get called with a module context along with a username and a password, and they're expected to take one of the following actions:
- Authenticate โ Use the
ValkeyModule_AuthenticateClient*API and returnVALKEYMODULE_AUTH_HANDLED. This will immediately end the auth chain as successful and add theOKreply. โ - Deny Authentication โ Return
VALKEYMODULE_AUTH_HANDLEDwithout authenticating or blocking the client. Optionally,errcan be set to a custom error message, anderrwill be automatically freed by the server. This will immediately end the auth chain as unsuccessful and add theERRreply. โ - Block a client on authentication โ Use the
ValkeyModule_BlockClientOnAuthAPI and returnVALKEYMODULE_AUTH_HANDLED. Here, the client will be blocked until theValkeyModule_UnblockClientAPI is used, which will trigger the auth reply callback (provided through theValkeyModule_BlockClientOnAuth). In this reply callback, the Module should authenticate, deny, or skip handling authentication. โณ - Skip handling Authentication โ Return
VALKEYMODULE_AUTH_NOT_HANDLEDwithout blocking the client. This will allow the engine to attempt the next module auth callback. If none of the callbacks authenticate or deny auth, then password based auth is attempted and will authenticate or add failure logs and reply to the clients accordingly. โญ๏ธ
Note: If a client is disconnected while it was in the middle of blocking module auth, that occurrence of theAUTHorHELLOcommand will not be tracked in theINFOcommand stats. ๐
The following is an example of how non-blocking module based authentication can be used:
int auth_cb(ValkeyModuleCtx *ctx, ValkeyModuleString *username, ValkeyModuleString *password, ValkeyModuleString **err) {
const char *user = ValkeyModule_StringPtrLen(username, NULL);
const char *pwd = ValkeyModule_StringPtrLen(password, NULL);
if (!strcmp(user,"foo") && !strcmp(pwd,"valid_password")) {
ValkeyModule_AuthenticateClientWithACLUser(ctx, "foo", 3, NULL, NULL, NULL);
return VALKEYMODULE_AUTH_HANDLED;
}
else if (!strcmp(user,"foo") && !strcmp(pwd,"wrong_password")) {
ValkeyModuleString *log = ValkeyModule_CreateString(ctx, "Module Auth", 11);
ValkeyModule_ACLAddLogEntryByUserName(ctx, username, log, VALKEYMODULE_ACL_LOG_AUTH);
ValkeyModule_FreeString(ctx, log);
const char *err_msg = "Auth denied by Misc Module.";
*err = ValkeyModule_CreateString(ctx, err_msg, strlen(err_msg));
return VALKEYMODULE_AUTH_HANDLED;
}
return VALKEYMODULE_AUTH_NOT_HANDLED;
}
int ValkeyModule_OnLoad(ValkeyModuleCtx *ctx, ValkeyModuleString **argv, int argc) {
if (ValkeyModule_Init(ctx,"authmodule",1,VALKEYMODULE_APIVER_1)== VALKEYMODULE_ERR)
return VALKEYMODULE_ERR;
ValkeyModule_RegisterAuthCallback(ctx, auth_cb);
return VALKEYMODULE_OK;
}
๐ ValkeyModule_BlockClient
ValkeyModuleBlockedClient *ValkeyModule_BlockClient(ValkeyModuleCtx *ctx,
ValkeyModuleCmdFunc reply_callback,
ValkeyModuleCmdFunc timeout_callback,
void (*free_privdata)(ValkeyModuleCtx *, void *),
long long timeout_ms);
Available since: 4.0.0
okay so bestie, this one blocks a client in the context of a blocking command, and it hands you back a handle that you're gonna use later to unblock that client by calling ValkeyModule_UnblockClient(). โจ The arguments are where you tell it your callback functions and a timeout, after which the client gets unblocked. no cap.
The callbacks get called in these contexts:
reply_callback: called after a successfulValkeyModule_UnblockClient()call in order to reply to the client and unblock it. ๐ซถtimeout_callback: called when the timeout is reached or ifCLIENT UNBLOCKis invoked, in order to send an error to the client. ๐ญfree_privdata: called in order to free the private data that is passed by theValkeyModule_UnblockClient()call. ๐ง
Notes, and pls read these fr fr: 1. ValkeyModule_UnblockClient should be called for every blocked client, even if the client was killed, timed-out or disconnected. Failing to do so will result in memory leaks. ๐ 2. Attempting to block the client on keyspace event notification in versions prior to 8.1.1 leads to a crash.
There are some cases where ValkeyModule_BlockClient() cannot be used:
- If the client is executing a script.
- If the client is executing a
MULTIblock. - If the client is a temporary module client.
- If the client is already blocked.
In cases 1 and 2, a call to ValkeyModule_BlockClient() will not block the client, but instead produce a specific error reply. Note that if the BlockClient call originated from within a keyspace notification, no error reply is generated but nullptr is returned while the errno is set to EINVAL.
In case 3 and 4, a call to ValkeyModule_BlockClient() are no-op, returning nullptr. errno is set to EINVAL for case 3 while ENOTSUP for case 4. ๐ธ
A module that registers a timeout_callback function can also be unblocked using the CLIENT UNBLOCK command, which will trigger the timeout callback. If a callback function is not registered, then the blocked client will be treated as if it is not in a blocked state and CLIENT UNBLOCK will return a zero value.
Measuring background time: By default the time spent in the blocked command is not account for the total command duration. To include such time you should use ValkeyModule_BlockedClientMeasureTimeStart() and ValkeyModule_BlockedClientMeasureTimeEnd() one, or multiple times within the blocking command background work. โฑ๏ธ
๐ ValkeyModule_BlockClientOnAuth
ValkeyModuleBlockedClient *ValkeyModule_BlockClientOnAuth(ValkeyModuleCtx *ctx,
ValkeyModuleAuthCallback reply_callback,
void (*free_privdata)(ValkeyModuleCtx *, void *));
Available since: 7.2.0
Block the current client for module authentication in the background. If module auth is not in progress on the client, the API returns NULL. Otherwise, the client is blocked and the ValkeyModule_BlockedClient is returned similar to the ValkeyModule_BlockClient API. โจ Note: Only use this API from the context of a module auth callback.
There are some cases where ValkeyModule_BlockClientOnAuth() cannot be used:
- If the client is not in the middle of module based authentication. This will not block the client but instead produce a specific error reply.
For details on other return values and error codes, see the comment block for ValkeyModule_BlockClient(). ๐
๐ฆ ValkeyModule_BlockClientGetPrivateData
void *ValkeyModule_BlockClientGetPrivateData(ValkeyModuleBlockedClient *blocked_client);
Available since: 7.2.0
Get the private data that was previously set on a blocked client ๐
๐ธ ValkeyModule_BlockClientSetPrivateData
void ValkeyModule_BlockClientSetPrivateData(ValkeyModuleBlockedClient *blocked_client,
void *private_data);
Available since: 7.2.0
Set private data on a blocked client โจ
๐ ValkeyModule_BlockClientOnKeys
ValkeyModuleBlockedClient *ValkeyModule_BlockClientOnKeys(ValkeyModuleCtx *ctx,
ValkeyModuleCmdFunc reply_callback,
ValkeyModuleCmdFunc timeout_callback,
void (*free_privdata)(ValkeyModuleCtx *, void *),
long long timeout_ms,
ValkeyModuleString **keys,
int numkeys,
void *privdata);
Available since: 6.0.0
This call is similar to ValkeyModule_BlockClient(), however in this case we don't just block the client, but also ask the server to unblock it automatically once certain keys become "ready", that is, contain more data. ๐
Basically this is similar to what a typical command usually does, like BLPOP or BZPOPMAX: the client blocks if it cannot be served ASAP, and later when the key receives new data (a list push for instance), the client is unblocked and served. ๐
However in the case of this module API, when the client is unblocked?
- 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
RPUSHcommand may unblock our client and so forth. - 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
ValkeyModule_SignalKeyAsReady().
Anyway we can't be sure if the client should be unblocked just because the key is signaled as ready: for instance a successive operation may change the key, or a client in queue before this one can be served, modifying the key as well and making it empty again. So when a client is blocked with ValkeyModule_BlockClientOnKeys() the reply callback is not called after ValkeyModule_UnblockClient() is called, but every time a key is signaled as ready: if the reply callback can serve the client, it returns VALKEYMODULE_OK and the client is unblocked, otherwise it will return VALKEYMODULE_ERR and we'll try again later. ๐
The reply callback can access the key that was signaled as ready by calling the API ValkeyModule_GetBlockedClientReadyKey(), that returns just the string name of the key as a ValkeyModuleString object.
Thanks to this system we can setup complex blocking scenarios, like unblocking a client only if a list contains at least 5 items or other more fancy logics. ๐
Note that another difference with ValkeyModule_BlockClient(), is that here we pass the private data directly when blocking the client: it will be accessible later in the reply callback. Normally when blocking with ValkeyModule_BlockClient() the private data to reply to the client is passed when calling ValkeyModule_UnblockClient() but here the unblocking is performed by the server itself, so we need to have some private data before hand. The private data is used to store any information about the specific unblocking operation that you are implementing. Such information will be freed using the free_privdata callback provided by the user.
However the reply callback will be able to access the argument vector of the command, so the private data is often not needed. ๐ซถ
Note: Under normal circumstancesValkeyModule_UnblockClientshould not be called for clients that are blocked on keys (Either the key will become ready or a timeout will occur). If for some reason you do want to callValkeyModule_UnblockClientit is possible: Client will be handled as if it were timed-out (You must implement the timeout callback in that case). ๐ญ
โญ ValkeyModule_BlockClientOnKeysWithFlags
ValkeyModuleBlockedClient *ValkeyModule_BlockClientOnKeysWithFlags(ValkeyModuleCtx *ctx,
ValkeyModuleCmdFunc reply_callback,
ValkeyModuleCmdFunc timeout_callback,
void (*free_privdata)(ValkeyModuleCtx *, void *),
long long timeout_ms,
ValkeyModuleString **keys,
int numkeys,
void *privdata,
int flags);
Available since: 7.2.0
Same as ValkeyModule_BlockClientOnKeys, but can take VALKEYMODULE_BLOCK_* flags. Can be either VALKEYMODULE_BLOCK_UNBLOCK_DEFAULT, which means default behavior (same as calling ValkeyModule_BlockClientOnKeys) โจ
The flags is a bit mask of these:
VALKEYMODULE_BLOCK_UNBLOCK_DELETED: The clients should to be awakened in case any of keys are deleted. Mostly useful for commands that require the key to exist (likeXREADGROUP) ๐
๐ ValkeyModule_SignalKeyAsReady
void ValkeyModule_SignalKeyAsReady(ValkeyModuleCtx *ctx,
ValkeyModuleString *key);
Available since: 6.0.0
This function is used in order to potentially unblock a client blocked on keys with ValkeyModule_BlockClientOnKeys(). When this function is called, all the clients blocked for this key will get their reply_callback called. ๐
๐ฅ ValkeyModule_UnblockClient
int ValkeyModule_UnblockClient(ValkeyModuleBlockedClient *bc, void *privdata);
Available since: 4.0.0
Unblock a client blocked by ValkeyModule_BlockedClient. This will trigger the reply callbacks to be called in order to reply to the client. The privdata argument will be accessible by the reply callback, so the caller of this function can pass any value that is needed in order to actually reply to the client. ๐ซถ
A common usage for privdata is, like, a thread that computes something it needs to hand over to the client โ could be a slow-to-compute reply, could be a reply it got over the network, doesn't matter, no judgment. ๐
It returns VALKEYMODULE_OK on success. If it fails you get VALKEYMODULE_ERR back and errno gets set like this:
EINVALifbcis NULL.ENOTSUPifbccontains blocked on keys but its timeout callback is NULL.
Note 1: this function can be called from threads spawned by the module. Yes bestie, threads. Go off. ๐งต
Note 2: when we unblock a client that's blocked for keys using the APIValkeyModule_BlockClientOnKeys(), theprivdataargument here is not used. Unblocking a client that was blocked for keys using this API will still require the client to get some reply, so the function will use the โtimeoutโ handler in order to do so (Theprivdataprovided inValkeyModule_BlockClientOnKeys()is accessible from the timeout callback viaValkeyModule_GetBlockedClientPrivateData).
ValkeyModule_AbortBlock
int ValkeyModule_AbortBlock(ValkeyModuleBlockedClient *bc);
Available since: 4.0.0
Abort a blocked client blocking operation: the client will be unblocked without firing any callback. Just, like, poof. Gone. Nothing happens. ๐ซฅ
ValkeyModule_SetDisconnectCallback
void ValkeyModule_SetDisconnectCallback(ValkeyModuleBlockedClient *bc,
ValkeyModuleDisconnectFunc callback);
Available since: 5.0.0
Set a callback that will be called if a blocked client disconnects before the module has a chance to call ValkeyModule_UnblockClient()
Usually what you wanna do there is clean up your module state so you can call ValkeyModule_UnblockClient() safely โ otherwise the client stays blocked forever if the timeout is large. And nobody wants that. ๐ญ
Notes:
- It is not safe to call
Reply*family functions here, it is also useless since the client is gone. - 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.
ValkeyModule_IsBlockedReplyRequest
int ValkeyModule_IsBlockedReplyRequest(ValkeyModuleCtx *ctx);
Available since: 4.0.0
Return non-zero if a module command was called in order to fill the reply for a blocked client.
ValkeyModule_IsBlockedTimeoutRequest
int ValkeyModule_IsBlockedTimeoutRequest(ValkeyModuleCtx *ctx);
Available since: 4.0.0
Return non-zero if a module command was called in order to fill the reply for a blocked client that timed out. โฐ
ValkeyModule_GetBlockedClientPrivateData
void *ValkeyModule_GetBlockedClientPrivateData(ValkeyModuleCtx *ctx);
Available since: 4.0.0
Get the private data set by ValkeyModule_UnblockClient()
ValkeyModule_GetBlockedClientReadyKey
ValkeyModuleString *ValkeyModule_GetBlockedClientReadyKey(ValkeyModuleCtx *ctx);
Available since: 6.0.0
Get the key that is ready when the reply callback is called in the context of a client blocked by ValkeyModule_BlockClientOnKeys(). ๐
ValkeyModule_GetBlockedClientHandle
ValkeyModuleBlockedClient *ValkeyModule_GetBlockedClientHandle(ValkeyModuleCtx *ctx);
Available since: 5.0.0
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.
ValkeyModule_BlockedClientDisconnected
int ValkeyModule_BlockedClientDisconnected(ValkeyModuleCtx *ctx);
Available since: 5.0.0
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.
๐ซ Thread Safe Contexts ๐ซ
ValkeyModule_GetThreadSafeContext
ValkeyModuleCtx *ValkeyModule_GetThreadSafeContext(ValkeyModuleBlockedClient *bc);
Available since: 4.0.0
Return a context which can be used inside threads to make calls requiring a context 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 ValkeyModule_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:
ValkeyModule_ThreadSafeContextLock(ctx);
... make your call here ...
ValkeyModule_ThreadSafeContextUnlock(ctx);
This is not needed when using ValkeyModule_Reply* functions, assuming that a blocked client was used when the context was created, otherwise no ValkeyModule_Reply* call should be made at all.
NOTE: If youโre creating a detached thread safe context (bcis NULL), consider usingValkeyModule_GetDetachedThreadSafeContextwhich will also retain the module ID and thus be more useful for logging. iykyk โจ
ValkeyModule_GetDetachedThreadSafeContext
ValkeyModuleCtx *ValkeyModule_GetDetachedThreadSafeContext(ValkeyModuleCtx *ctx);
Available since: 6.0.9
Return a detached thread safe context that is not associated with any specific blocked client, but is associated with the moduleโs context.
This is useful for modules that wish to hold a global context over a long term, for purposes such as logging. ๐
ValkeyModule_FreeThreadSafeContext
void ValkeyModule_FreeThreadSafeContext(ValkeyModuleCtx *ctx);
Available since: 4.0.0
Release a thread safe context. Bye bestie ๐ซถ
ValkeyModule_ThreadSafeContextLock
void ValkeyModule_ThreadSafeContextLock(ValkeyModuleCtx *ctx);
Available since: 4.0.0
Acquire the server lock before executing a thread safe API call. This is not needed for ValkeyModule_Reply* calls when there is a blocked client connected to the thread safe context.
ValkeyModule_ThreadSafeContextTryLock
int ValkeyModule_ThreadSafeContextTryLock(ValkeyModuleCtx *ctx);
Available since: 6.0.8
Similar to ValkeyModule_ThreadSafeContextLock but this function would not block if the server lock is already acquired.
If successful (lock acquired) VALKEYMODULE_OK is returned, otherwise VALKEYMODULE_ERR is returned and errno is set accordingly.
ValkeyModule_ThreadSafeContextUnlock
void ValkeyModule_ThreadSafeContextUnlock(ValkeyModuleCtx *ctx);
Available since: 4.0.0
Release the server lock after a thread safe API call was executed.
๐ Module Keyspace Notifications API ๐
ValkeyModule_SubscribeToKeyspaceEvents
int ValkeyModule_SubscribeToKeyspaceEvents(ValkeyModuleCtx *ctx,
int types,
ValkeyModuleNotificationFunc callback);
Available since: 4.0.9
Subscribe to keyspace notifications. This is a low-level version of the keyspace-notifications API. A module can register callbacks to be notified when keyspace events occur. ๐
Notification events are filtered by their type (string events, set events, etc), and the subscriber callback receives only events that match a specific mask of event types.
When subscribing to notifications with ValkeyModule_SubscribeToKeyspaceEvents the module must provide an event type-mask, denoting the events the subscriber is interested in. This can be an ORed mask of any of the following flags:
VALKEYMODULE_NOTIFY_GENERIC: Generic commands likeDEL,EXPIRE,RENAMEVALKEYMODULE_NOTIFY_STRING: String eventsVALKEYMODULE_NOTIFY_LIST: List eventsVALKEYMODULE_NOTIFY_SET: Set eventsVALKEYMODULE_NOTIFY_HASH: Hash eventsVALKEYMODULE_NOTIFY_ZSET: Sorted Set eventsVALKEYMODULE_NOTIFY_EXPIRED: Expiration eventsVALKEYMODULE_NOTIFY_EVICTED: Eviction eventsVALKEYMODULE_NOTIFY_STREAM: Stream eventsVALKEYMODULE_NOTIFY_MODULE: Module types eventsVALKEYMODULE_NOTIFY_KEYMISS: Key-miss events Notice, key-miss event is the only type of event that is fired from within a read command. PerformingValkeyModule_Callwith a write command from within this notification is wrong and discourage. It will cause the read command that trigger the event to be replicated to the AOF/Replica. ๐VALKEYMODULE_NOTIFY_ALL: All events (ExcludingVALKEYMODULE_NOTIFY_KEYMISS)VALKEYMODULE_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, useValkeyModule_CreateStringFromStringinstead.
We do not distinguish between key events and keyspace events, and it is up to the module to filter the actions taken based on the key.
The subscriber signature is:
int (*ValkeyModuleNotificationFunc) (ValkeyModuleCtx *ctx, int type,
const char *event,
ValkeyModuleString *key);
type is the event type bit, that must match the mask given at registration time. The event string is the actual command being executed, and key is the relevant key.
Notification callback gets executed with a context that can not be used to send anything to the client, and has the db number where the event occurred as its selected db number.
Notice that it is not necessary to enable notifications in valkey.conf for module notifications to work. Slay, one less config file to touch. ๐
Warning: the notification callbacks are performed in a synchronous manner, so notification callbacks must to be fast, or they would slow the server down. If you need to take long actions, use threads to offload them.
Moreover, the fact that the notification is executed synchronously means that the notification code will be executed in the middle of server logic (commands logic, eviction, expire). Changing the key space while the logic runs is dangerous and discouraged. In order to react to key space events with write actions, please refer to ValkeyModule_AddPostNotificationJob.
See valkey-notifications(7) for more information.
ValkeyModule_AddPostNotificationJob
int ValkeyModule_AddPostNotificationJob(ValkeyModuleCtx *ctx,
ValkeyModulePostNotificationJobFunc callback,
void *privdata,
void (*free_privdata)(void *));
Available since: 7.2.0
okay so real talk bestie โ if you're sitting inside a key space notification callback and you're like "imma just write something real quick"โฆ don't. It's dangerous and highly discouraged, fr. (peep ValkeyModule_SubscribeToKeyspaceEvents if you wanna know why). But if you gotta do a write in that situation, the server hands you the ValkeyModule_AddPostNotificationJob API. ๐ซถ
What that API does is let you register a job callback, and the server promises to call it once these two conditions are met:
- It is safe to perform any write operation. โจ
- The job will be called atomically along side the key space notification. ๐
Now heads up: one job can trigger key space notifications, which can trigger more jobs, and suddenly you're in infinite-loop territory. We treat infinite loops as a logical bug that needs fixing in the module itself โ the server will make no attempt to protect your module from infinite loops, because trying to halt execution to protect against them could break feature correctness. So yeah, that's on you, bestie. ๐
Also free_pd can be NULL, and in that case it just won't be used. No drama.
Return VALKEYMODULE_OK on success, and VALKEYMODULE_ERR if it was called while loading data from disk (AOF or RDB) or if the instance is a readonly replica.
ValkeyModule_GetNotifyKeyspaceEvents
int ValkeyModule_GetNotifyKeyspaceEvents(void);
Available since: 6.0.0
Grab the configured bitmap of notify-keyspace-events โ super handy if you want to do extra filtering inside ValkeyModuleNotificationFunc. ๐
ValkeyModule_NotifyKeyspaceEvent
int ValkeyModule_NotifyKeyspaceEvent(ValkeyModuleCtx *ctx,
int type,
const char *event,
ValkeyModuleString *key);
Available since: 6.0.0
Exposes notifyKeyspaceEvent to modules. That's it, that's the vibe. โจ
Modules Cluster API
ValkeyModule_RegisterClusterMessageReceiver
void ValkeyModule_RegisterClusterMessageReceiver(ValkeyModuleCtx *ctx,
uint8_t type,
ValkeyModuleClusterMessageReceiver callback);
Available since: 5.0.0
Register a callback receiver for cluster messages of type type. If there was already a registered callback, this replaces the callback function with the one you provided. Otherwise, if the callback is set to NULL and there's already a callback for this function, the callback gets unregistered โ which means this same API call is how you delete a receiver. Two birds, one slay. ๐
When a message of this type comes in, the registered callback gets invoked with details, including the 40-byte node ID of the sender.
Version tea: In Valkey 8.1 and later, the node ID is null-terminated. Prior to 8.1, it was not null-terminated.
ValkeyModule_SendClusterMessage
int ValkeyModule_SendClusterMessage(ValkeyModuleCtx *ctx,
const char *target_id,
uint8_t type,
const char *msg,
uint32_t len);
Available since: 5.0.0
Send a message to all the nodes in the cluster if target is NULL โ otherwise it goes to the specified target, which is a VALKEYMODULE_NODE_ID_LEN bytes node ID, as returned by the receiver callback or by the nodes iteration functions. ๐จ
In Valkey 8.1 and later, the cluster protocol overhead for this message is ~30B, compared to earlier versions where it's ~2KB. glow-up fr.
The function returns VALKEYMODULE_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, VALKEYMODULE_ERR is returned.
ValkeyModule_GetClusterNodesList
char **ValkeyModule_GetClusterNodesList(ValkeyModuleCtx *ctx, size_t *numnodes);
Available since: 5.0.0
Returns an array of string pointers, and each string pointer points to a cluster node ID of exactly VALKEYMODULE_NODE_ID_LEN bytes (without any null term, okay?). The number of returned node IDs gets stored into *numnodes. But if this function is called by a module not running an an instance with Cluster enabled, NULL is returned instead.
The IDs you get back can be used with ValkeyModule_GetClusterNodeInfo() to get more info about a single node. ๐ต๏ธ
The array returned by this function must be freed using ValkeyModule_FreeClusterNodesList().
Example:
size_t count, j;
char **ids = ValkeyModule_GetClusterNodesList(ctx,&count);
for (j = 0; j < count; j++) {
ValkeyModule_Log(ctx,"notice","Node %.*s",
VALKEYMODULE_NODE_ID_LEN,ids[j]);
}
ValkeyModule_FreeClusterNodesList(ids);
ValkeyModule_FreeClusterNodesList
void ValkeyModule_FreeClusterNodesList(char **ids);
Available since: 5.0.0
Free the node list you got with ValkeyModule_GetClusterNodesList. Clean up after yourself, bestie. ๐งนโจ
ValkeyModule_GetMyClusterID
const char *ValkeyModule_GetMyClusterID(void);
Available since: 5.0.0
Returns this node's ID (VALKEYMODULE_CLUSTER_ID_LEN bytes) or NULL if the cluster is disabled. ๐
ValkeyModule_GetClusterSize
size_t ValkeyModule_GetClusterSize(void);
Available since: 5.0.0
Returns the number of nodes in the cluster, regardless of their state (handshake, noaddress, ...) โ so the number of active nodes may actually be smaller, but not greater than this number. And if the instance isn't in cluster mode, zero is returned. ๐ธ
ValkeyModule_GetClusterNodeInfo
int ValkeyModule_GetClusterNodeInfo(ValkeyModuleCtx *ctx,
const char *id,
char *ip,
char *primary_id,
int *port,
int *flags);
Available since: 5.0.0
Populate the specified info for the node having as ID the specified id, then returns VALKEYMODULE_OK. Otherwise, if the format of the node ID is invalid or the node ID doesn't exist from the POV of this local node, VALKEYMODULE_ERR is returned. ๐
The arguments ip, primary_id, port and flags can be NULL in case we don't need to populate back certain info. If an ip and primary_id (only populated if the instance is a replica) are specified, they point to buffers holding at least VALKEYMODULE_NODE_ID_LEN bytes. The strings written back as ip and primary_id are not null terminated.
The list of flags reported is the following:
VALKEYMODULE_NODE_MYSELF: This nodeVALKEYMODULE_NODE_PRIMARY: The node is a primaryVALKEYMODULE_NODE_REPLICA: The node is a replicaVALKEYMODULE_NODE_PFAIL: We see the node as failingVALKEYMODULE_NODE_FAIL: The cluster agrees the node is failingVALKEYMODULE_NODE_NOFAILOVER: The replica is configured to never failover
ValkeyModule_GetClusterNodeInfoForClient
int ValkeyModule_GetClusterNodeInfoForClient(ValkeyModuleCtx *ctx,
uint64_t client_id,
const char *node_id,
char *ip,
char *primary_id,
int *port,
int *flags);
Available since: 8.0.0
Like ValkeyModule_GetClusterNodeInfo(), but returns the IP address specifically for the given client, depending on whether the client is connected over IPv4 or IPv6. ๐
See also ValkeyModule_GetClientId().
ValkeyModule_SetClusterFlags
void ValkeyModule_SetClusterFlags(ValkeyModuleCtx *ctx, uint64_t flags);
Available since: 5.0.0
Set Cluster flags to change the normal behavior of Cluster โ especially with the goal of disabling certain functions. This is useful for modules that use the Cluster API to create a different distributed system, but still wanna use the Cluster message bus. ๐
Flags that can be set:
CLUSTER_MODULE_FLAG_NO_FAILOVERCLUSTER_MODULE_FLAG_NO_REDIRECTION
With the following effects:
NO_FAILOVER: prevent Cluster replicas from failing over a dead primary. Also disables the replica migration feature.NO_REDIRECTION: Every node will accept any key, without trying to perform partitioning according to the Cluster algorithm. Slots information will still be propagated across the cluster, but without effect.
ValkeyModule_ClusterKeySlot
unsigned int ValkeyModule_ClusterKeySlot(ValkeyModuleString *key);
Available since: 8.0.0
Returns the cluster slot of a key, similar to the CLUSTER KEYSLOT command. And the iconic part? This function works even if cluster mode is not enabled. โจ
ValkeyModule_ClusterCanonicalKeyNameInSlot
const char *ValkeyModule_ClusterCanonicalKeyNameInSlot(unsigned int slot);
Available since: 8.0.0
Returns a short string that can be used as a key or as a hash tag in a key, such that the key maps to the given cluster slot. Returns NULL if slot is not a valid slot. ๐ฏ
Modules Timers API
Module timers are a 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's used to awake the module timers subsystem in order to process the next event. efficiency ate, no crumbs. ๐
All the timers are stored into a radix tree, ordered by expire time. When the main server 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. ๐ฐ๏ธโจ
And every time the list of active timers drops to zero, we unregister the main event loop timer, so there's no overhead when such feature is not used. periodt.
ValkeyModule_CreateTimer
ValkeyModuleTimerID ValkeyModule_CreateTimer(ValkeyModuleCtx *ctx,
mstime_t period,
ValkeyModuleTimerProc callback,
void *data);
Available since: 5.0.0
okay so you're making a brand-new timer that's gonna fire after period milliseconds, and when it goes off it calls the function you handed it, passing data as the argument. โจ The timer ID you get back is your little receipt โ use it later to grab info about the timer or to stop it before it ever fires.
And listen bestie, there's a lil plot twist for the super-common repeating-timer setup (that's where you re-register the timer from inside the ValkeyModuleTimerProc callback): when you call this API actually matters. ๐
- If you call it at the beginning of
callback, the event triggers everyperiod. ๐ - If you call it at the end of
callback, there'll beperiodmilliseconds of gap between events. ๐
(and fr, if running callback takes basically no time at all, those two things mean the exact same thing, no cap)
๐ ValkeyModule_StopTimer
int ValkeyModule_StopTimer(ValkeyModuleCtx *ctx,
ValkeyModuleTimerID id,
void **data);
Available since: 5.0.0
Stops a timer. It hands you back VALKEYMODULE_OK if the timer was actually found, belonged to the module that's calling, and got stopped โ otherwise you get VALKEYMODULE_ERR. ๐ค And if data isn't NULL, it gets set to whatever the data argument was back when the timer was created. ๐ง
๐ฆ ValkeyModule_GetTimerInfo
int ValkeyModule_GetTimerInfo(ValkeyModuleCtx *ctx,
ValkeyModuleTimerID id,
uint64_t *remaining,
void **data);
Available since: 5.0.0
Grab the tea on a timer: how much time is left before it fires (in milliseconds), plus the private data pointer hanging off it. โ If the timer you asked about doesn't exist, or it belongs to a different module, then nope โ no info for you, and the function returns VALKEYMODULE_ERR. Otherwise it's VALKEYMODULE_OK. ๐ซถ Oh and remaining or data can totally be NULL if you don't care about that particular piece of info.
โจ Modules EventLoop API โจ
๐ ValkeyModule_EventLoopAdd
int ValkeyModule_EventLoopAdd(int fd,
int mask,
ValkeyModuleEventLoopFunc func,
void *user_data);
Available since: 7.0.0
Adds a pipe / socket event to the event loop. ๐
maskhas to be one of these values:VALKEYMODULE_EVENTLOOP_READABLEVALKEYMODULE_EVENTLOOP_WRITABLEVALKEYMODULE_EVENTLOOP_READABLE | VALKEYMODULE_EVENTLOOP_WRITABLE
On success you get VALKEYMODULE_OK, otherwise VALKEYMODULE_ERR comes back and errno gets set to one of these:
ERANGE:fdis negative or higher than themaxclientsserver config. ๐EINVAL:callbackisNULLor themaskvalue is invalid.
and yeah, errno might take other values too if there's some internal error going on, just so you know
Example:
void onReadable(int fd, void *user_data, int mask) {
char buf[32];
int bytes = read(fd,buf,sizeof(buf));
printf("Read %d bytes \n", bytes);
}
ValkeyModule_EventLoopAdd(fd, VALKEYMODULE_EVENTLOOP_READABLE, onReadable, NULL);
๐ ValkeyModule_EventLoopDel
int ValkeyModule_EventLoopDel(int fd, int mask);
Available since: 7.0.0
Deletes a pipe / socket event from the event loop. bye bestie ๐
maskhas to be one of these values:VALKEYMODULE_EVENTLOOP_READABLEVALKEYMODULE_EVENTLOOP_WRITABLEVALKEYMODULE_EVENTLOOP_READABLE | VALKEYMODULE_EVENTLOOP_WRITABLE
On success VALKEYMODULE_OK is returned, otherwise VALKEYMODULE_ERR is returned and errno gets set to:
ERANGE:fdis negative or higher than themaxclientsserver config.EINVAL: themaskvalue is invalid.
โญ ValkeyModule_EventLoopAddOneShot
int ValkeyModule_EventLoopAddOneShot(ValkeyModuleEventLoopOneShotFunc func,
void *user_data);
Available since: 7.0.0
This one's iconic fr โ you can call it from other threads to trigger a callback over on the server main thread. ๐งตโก๏ธ๐ On success you get VALKEYMODULE_OK. But if func is NULL, you get VALKEYMODULE_ERR and errno gets set to EINVAL. ๐
๐ Modules ACL API ๐
Implements a hook into the authentication and authorization within the server. basically it's the bouncer at the club door, but for your server. ๐
๐ ValkeyModule_CreateModuleUser
ValkeyModuleUser *ValkeyModule_CreateModuleUser(const char *name);
Available since: 6.0.0
Makes an ACL user that your module can use to authenticate a client. Once you've got the user, the module should set what that user is allowed to do using the ValkeyModule_SetUserACL() function. And once it's all configured, you can use that user to authenticate a connection โ with those specified ACL rules โ via the ValkeyModule_AuthClientWithUser() function. ๐ซถ
Now, important notes, bestie:
- Users created here are not listed by the
ACLcommand. ๐ - Users created here are not checked for duplicated names, so it's on the module calling this function to make sure it doesn't create users with the same name. ๐
- The created user can be used to authenticate multiple connections. โจ
You can free the user later with ValkeyModule_FreeModuleUser(). And heads up: when that free function is called, if there are still clients authenticated with this user, they get disconnected. So the free-the-user function should only be used when you genuinely want to invalidate the user so you can define a new one with different capabilities. periodt.
๐ ValkeyModule_FreeModuleUser
int ValkeyModule_FreeModuleUser(ValkeyModuleUser *user);
Available since: 6.0.0
Frees a given user and disconnects all the clients that were authenticated with it. ๐ญ For the detailed usage deets, go see ValkeyModule_CreateModuleUser.
๐ ValkeyModule_SetModuleUserACL
int ValkeyModule_SetModuleUserACL(ValkeyModuleUser *user, const char *acl);
Available since: 6.0.0
Sets the permissions of a user that was created through the module interface. The syntax is the same as ACL SETUSER, so go peek at the documentation in acl.c for more info. ๐ And again, ValkeyModule_CreateModuleUser has the detailed usage.
Returns VALKEYMODULE_OK on success, and VALKEYMODULE_ERR on failure โ and on failure it'll set an errno describing why the operation flopped. ๐
๐ธ ValkeyModule_SetModuleUserACLString
int ValkeyModule_SetModuleUserACLString(ValkeyModuleCtx *ctx,
ValkeyModuleUser *user,
const char *acl,
ValkeyModuleString **error);
Available since: 7.0.6
Sets the permission of a user using a complete ACL string โ the kind you'd use on the ACL SETUSER command line API. ๐ This is the difference from ValkeyModule_SetModuleUserACL, which only takes single ACL operations at a time.
Returns VALKEYMODULE_OK on success, and VALKEYMODULE_ERR on failure. And if you provided a ValkeyModuleString in error, a string describing the error will be returned. ๐
๐ฆ ValkeyModule_GetModuleUserACLString
ValkeyModuleString *ValkeyModule_GetModuleUserACLString(ValkeyModuleUser *user);
Available since: 7.0.6
Gets the ACL string for a given user. Returns a ValkeyModuleString. โจ
๐ ValkeyModule_GetCurrentUserName
ValkeyModuleString *ValkeyModule_GetCurrentUserName(ValkeyModuleCtx *ctx);
Available since: 7.0.0
Retrieves the user name of the client connection sitting behind the current context. You can use that user name later to get a ValkeyModuleUser โ more on that in ValkeyModule_GetModuleUserFromUserName. ๐
The returned string must be released with ValkeyModule_FreeString() or by enabling automatic memory management. don't forget, bestie ๐
And if the context isn't associated with a client connection, NULL is returned and errno gets set to EINVAL. ๐
๐ง ValkeyModule_GetModuleUserFromUserName
ValkeyModuleUser *ValkeyModule_GetModuleUserFromUserName(ValkeyModuleString *name);
Available since: 7.0.0
A ValkeyModuleUser can be used to check whether a command, key or channel can be executed or accessed according to the ACL rules tied to that user. ๐ So when a Module wants to do ACL checks on a general ACL user (meaning one not created by ValkeyModule_CreateModuleUser), it can grab the ValkeyModuleUser from this API, based on the user name it got from ValkeyModule_GetCurrentUserName.
But here's the catch: since a general ACL user can be deleted at literally any time, this ValkeyModuleUser should only be used in the context where this function was called. โ ๏ธ If you wanna do ACL checks outside that context, the Module can store the user name instead, and call this API at any other context.
Returns NULL if the user is disabled or the user doesn't exist. And the caller should free the user later using ValkeyModule_FreeModuleUser(). ๐ซถ
๐ ValkeyModule_ACLCheckCommandPermissions
int ValkeyModule_ACLCheckCommandPermissions(ValkeyModuleUser *user,
ValkeyModuleString **argv,
int argc);
Available since: 7.0.0
Checks whether the command can be executed by the user, according to the ACLs associated with it. ๐
On success VALKEYMODULE_OK is returned, otherwise VALKEYMODULE_ERR is returned and errno gets set to:
ENOENT: the specified command doesn't exist.EACCES: the command cannot be executed, according to the ACL rules. ๐ซ
โญ ValkeyModule_ACLCheckKeyPermissions
int ValkeyModule_ACLCheckKeyPermissions(ValkeyModuleUser *user,
ValkeyModuleString *key,
int flags);
Available since: 7.0.0
Checks whether the key can be accessed by the user, according to the ACLs attached to that user and the flags representing the key access. ๐๏ธ Those flags are the same ones used in the keyspec for logical operations โ they're documented in ValkeyModule_SetCommandInfo as the VALKEYMODULE_CMD_KEY_ACCESS, VALKEYMODULE_CMD_KEY_UPDATE, VALKEYMODULE_CMD_KEY_INSERT, and VALKEYMODULE_CMD_KEY_DELETE flags. ๐
If you supply no flags at all, the user is still required to have some access to the key for this command to return successfully. no shortcuts, bestie.
If the user can access the key, then VALKEYMODULE_OK is returned โ otherwise VALKEYMODULE_ERR comes back and errno is set to one of:
EINVAL: the flags you provided are invalid.EACCESS: the user doesn't have permission to access the key. ๐ค
๐ ValkeyModule_ACLCheckChannelPermissions
int ValkeyModule_ACLCheckChannelPermissions(ValkeyModuleUser *user,
ValkeyModuleString *ch,
int flags);
Available since: 7.0.0
Checks whether the pubsub channel can be accessed by the user, based off the given access flags. ๐ก Wanna know what flags you can pass in? Go see ValkeyModule_ChannelAtPosWithFlags for all the deets. โจ
If the user is able to access the pubsub channel then VALKEYMODULE_OK is returned, otherwise VALKEYMODULE_ERR is returned and errno is set to one of these besties:
EINVAL: the flags you passed are invalid, like genuinely not it.EACCESS: the user does not have permission to access the pubsub channel. rude but fair.
ValkeyModule_ACLAddLogEntry
int ValkeyModule_ACLAddLogEntry(ValkeyModuleCtx *ctx,
ValkeyModuleUser *user,
ValkeyModuleString *object,
ValkeyModuleACLLogEntryReason reason);
Available since: 7.0.0
Okay so this one adds a new entry into the ACL log. Returns VALKEYMODULE_OK on success and VALKEYMODULE_ERR on error, simple as.
Wanna know more about the ACL log? Go read acl-log(3valkey), it's all there.
ValkeyModule_ACLAddLogEntryByUserName
int ValkeyModule_ACLAddLogEntryByUserName(ValkeyModuleCtx *ctx,
ValkeyModuleString *username,
ValkeyModuleString *object,
ValkeyModuleACLLogEntryReason reason);
Available since: 7.2.0
Same energy as above but you hand it the username as a ValkeyModuleString and it drops a new entry into the ACL log. Returns VALKEYMODULE_OK on success, VALKEYMODULE_ERR on error.
More ACL log lore lives in acl-log(3valkey).
ValkeyModule_AuthenticateClientWithUser
int ValkeyModule_AuthenticateClientWithUser(ValkeyModuleCtx *ctx,
ValkeyModuleUser *module_user,
ValkeyModuleUserChangedFunc callback,
void *privdata,
uint64_t *client_id);
Available since: 6.0.0
Authenticates the current context's user using the acl user you gave it. Returns VALKEYMODULE_ERR if the user is disabled (rip).
Peep authenticateClientWithUser for the tea on callback, client_id, and how authentication works in general.
ValkeyModule_AuthenticateClientWithACLUser
int ValkeyModule_AuthenticateClientWithACLUser(ValkeyModuleCtx *ctx,
const char *name,
size_t len,
ValkeyModuleUserChangedFunc callback,
void *privdata,
uint64_t *client_id);
Available since: 6.0.0
Authenticates the current context's user with the acl user you provided. Returns VALKEYMODULE_ERR if the user is disabled or if the user simply doesn't exist. ๐
Again, authenticateClientWithUser is where you go for callback, client_id, and the general usage lowdown on authentication.
ValkeyModule_DeauthenticateAndCloseClient
int ValkeyModule_DeauthenticateAndCloseClient(ValkeyModuleCtx *ctx,
uint64_t client_id);
Available since: 6.0.0
Deauthenticates and closes the client. Real talk: the client's resources are not freed right away โ they get cleaned up in a background job later. And this is actually the recommended way to deauthenticate a client, because most clients literally cannot handle a user becoming deauthenticated. Returns VALKEYMODULE_ERR when the client doesn't exist, and VALKEYMODULE_OK when the operation was a slay.
Where does the client ID come from? It's returned from the ValkeyModule_AuthenticateClientWithUser and ValkeyModule_AuthenticateClientWithACLUser APIs, but you can also get it through the CLIENT api or through server events. โจ
Heads up bestie: this function is not thread safe, and it must be executed within the context of a command or a thread safe context. no exceptions.
ValkeyModule_RedactClientCommandArgument
int ValkeyModule_RedactClientCommandArgument(ValkeyModuleCtx *ctx, int pos);
Available since: 7.0.0
Redacts the client command argument sitting at the given position. Redacted arguments get obfuscated in user facing commands like SLOWLOG or MONITOR, and they never get written to server logs. Iconic. You can call this multiple times on the same position, no problem.
Note that the command name, position 0, can not be redacted. that one's off limits.
Returns VALKEYMODULE_OK if the argument was redacted, and VALKEYMODULE_ERR if you passed in an invalid parameter or the position is outside the client argument range.
ValkeyModule_GetClientCertificate
ValkeyModuleString *ValkeyModule_GetClientCertificate(ValkeyModuleCtx *ctx,
uint64_t client_id);
Available since: 6.0.9
Returns the X.509 client-side certificate that the client used to authenticate this connection.
The return value is an allocated ValkeyModuleString that is a X.509 certificate encoded in PEM (Base64) format. You gotta free it (or auto-free it) yourself, bestie โ it's on you.
A NULL value comes back in these situations:
- The connection ID does not exist
- The connection is not a TLS connection
- The connection is a TLS connection but no client certificate was used
๐ธ Modules Dictionary API ๐ธ
So this whole thing implements a sorted dictionary (it's actually backed by a radix tree, fun fact) with the classic get / set / del / num-items API, plus an iterator that can go back and forth like it's doing cardio. ๐ฆ
ValkeyModule_CreateDict
ValkeyModuleDict *ValkeyModule_CreateDict(ValkeyModuleCtx *ctx);
Available since: 5.0.0
Creates a brand new dictionary. The ctx pointer can be the current module context or NULL, depending on what you're trying to do. Follow these rules, fr fr:
- Use a
NULLcontext if you plan to retain a reference to this dictionary that will survive past the module callback where you created it. - Use a
NULLcontext if no context is available at the time you're creating the dictionary (obviously...). - However, use the current callback context as the
ctxargument if the dictionary's time to live is just limited to the callback scope. In that case, if it's enabled, you get to enjoy the automatic memory management that reclaims the dictionary memory, plus the strings returned by the Next / Prev dictionary iterator calls. self-care ๐
ValkeyModule_FreeDict
void ValkeyModule_FreeDict(ValkeyModuleCtx *ctx, ValkeyModuleDict *d);
Available since: 5.0.0
Frees a dictionary that was made with ValkeyModule_CreateDict(). You only need to pass the context pointer ctx if the dictionary was created using the context instead of passing NULL.
ValkeyModule_DictSize
uint64_t ValkeyModule_DictSize(ValkeyModuleDict *d);
Available since: 5.0.0
Returns the size of the dictionary (aka how many keys are in there).
ValkeyModule_DictSetC
int ValkeyModule_DictSetC(ValkeyModuleDict *d,
void *key,
size_t keylen,
void *ptr);
Available since: 5.0.0
Stores the specified key into the dictionary, setting its value to the pointer ptr. If the key was added successfully โ because it did not already exist โ VALKEYMODULE_OK is returned. Otherwise, if the key already exists, the function returns VALKEYMODULE_ERR.
ValkeyModule_DictReplaceC
int ValkeyModule_DictReplaceC(ValkeyModuleDict *d,
void *key,
size_t keylen,
void *ptr);
Available since: 5.0.0
Like ValkeyModule_DictSetC() but it will replace the key with the new value if the key already exists. no drama, just overwrite. ๐ฅ
ValkeyModule_DictSet
int ValkeyModule_DictSet(ValkeyModuleDict *d,
ValkeyModuleString *key,
void *ptr);
Available since: 5.0.0
Like ValkeyModule_DictSetC() but it takes the key as a ValkeyModuleString.
ValkeyModule_DictReplace
int ValkeyModule_DictReplace(ValkeyModuleDict *d,
ValkeyModuleString *key,
void *ptr);
Available since: 5.0.0
Like ValkeyModule_DictReplaceC() but it takes the key as a ValkeyModuleString.
ValkeyModule_DictGetC
void *ValkeyModule_DictGetC(ValkeyModuleDict *d,
void *key,
size_t keylen,
int *nokey);
Available since: 5.0.0
Returns the value stored at the specified key. The function returns NULL both when the key does not exist, or if you actually stored NULL at that key. So, optionally, if the nokey pointer is not NULL, it will be set by reference to 1 if the key does not exist, or to 0 if the key does exist. handy honestly.
ValkeyModule_DictGet
void *ValkeyModule_DictGet(ValkeyModuleDict *d,
ValkeyModuleString *key,
int *nokey);
Available since: 5.0.0
Like ValkeyModule_DictGetC() but it takes the key as a ValkeyModuleString.
ValkeyModule_DictDelC
int ValkeyModule_DictDelC(ValkeyModuleDict *d,
void *key,
size_t keylen,
void *oldval);
Available since: 5.0.0
Removes the specified key from the dictionary, returning VALKEYMODULE_OK if the key was found and deleted, or VALKEYMODULE_ERR if there was no such key in the dictionary instead. When the operation succeeds, if oldval is not NULL, then *oldval is set to the value that was stored at the key before it got deleted. Using this feature you can get a pointer to the value (for instance, so you can release it) without having to call ValkeyModule_DictGet() before deleting the key. efficiency queen ๐
ValkeyModule_DictDel
int ValkeyModule_DictDel(ValkeyModuleDict *d,
ValkeyModuleString *key,
void *oldval);
Available since: 5.0.0
Like ValkeyModule_DictDelC() but it gets the key as a ValkeyModuleString.
ValkeyModule_DictIteratorStartC
ValkeyModuleDictIter *ValkeyModule_DictIteratorStartC(ValkeyModuleDict *d,
const char *op,
void *key,
size_t keylen);
Available since: 5.0.0
Returns an iterator, set up to start iterating from the specified key by applying the operator op, which is just a string specifying the comparison operator to use in order to seek the first element. The operators you can use are:
^โ Seek the first (lexicographically smaller) key.$โ Seek the last (lexicographically bigger) key.>โ Seek the first element greater than the specified key.>=โ seek the first element that's greater than or equal to the key you passed. ๐<โ seek the first element that's smaller than your key.<=โ seek the first element that's smaller than or equal to your key.==โ seek the first element that matches your key exactly. no wiggle room, periodt.
lowkey important: for^and$the key you pass is not used at all, so bestie you can literally just passNULLwith a length of0. it's giving "we don't need it" โจ
If the element you want to start the iteration from can't be seeked based on the key + operator you handed over, then ValkeyModule_DictNext() / Prev() will just return VALKEYMODULE_ERR on that very first call. otherwise? they'll produce elements like normal. ๐
๐ฆ ValkeyModule_DictIteratorStart
ValkeyModuleDictIter *ValkeyModule_DictIteratorStart(ValkeyModuleDict *d,
const char *op,
ValkeyModuleString *key);
Available since: 5.0.0
Exactly like ValkeyModule_DictIteratorStartC, except the key comes in as a ValkeyModuleString. same energy, different wrapper ๐
๐ธ ValkeyModule_DictIteratorStop
void ValkeyModule_DictIteratorStop(ValkeyModuleDictIter *di);
Available since: 5.0.0
Release the iterator you made with ValkeyModule_DictIteratorStart(). and bestie, this call is mandatory โ skip it and you've got yourself a memory leak in the module. not cute, not slay, just leak. ๐ญ
๐ ValkeyModule_DictIteratorReseekC
int ValkeyModule_DictIteratorReseekC(ValkeyModuleDictIter *di,
const char *op,
void *key,
size_t keylen);
Available since: 5.0.0
Once you've created your iterator with ValkeyModule_DictIteratorStart(), you can change which element is currently selected using this API call. the result, based on the operator and key, works exactly like ValkeyModule_DictIteratorStart() โ except here the return value is just VALKEYMODULE_OK if the seeked element was found, or VALKEYMODULE_ERR if it wasn't possible to seek the specified element. and yes, you can reseek an iterator as many times as you want. go off, queen ๐
๐ง ValkeyModule_DictIteratorReseek
int ValkeyModule_DictIteratorReseek(ValkeyModuleDictIter *di,
const char *op,
ValkeyModuleString *key);
Available since: 5.0.0
Like ValkeyModule_DictIteratorReseekC() but the key arrives as a ValkeyModuleString. ๐
โญ ValkeyModule_DictNextC
void *ValkeyModule_DictNextC(ValkeyModuleDictIter *di,
size_t *keylen,
void **dataptr);
Available since: 5.0.0
Return the current item of the dictionary iterator di and step on to the next element. if the iterator already handed you the last element and there's nothing else to return, you get NULL. otherwise you get a pointer to a string representing the key, and the *keylen length gets set by reference (as long as keylen isn't NULL). and *dataptr, if it's not NULL, gets set to the value of the pointer stored at the returned key as auxiliary data โ as set by the ValkeyModule_DictSet API. ๐ซถ
Usage example, bestie:
... create the iterator here ...
char *key;
void *data;
while((key = ValkeyModule_DictNextC(iter,&keylen,&data)) != NULL) {
printf("%.*s %p\n", (int)keylen, key, data);
}
the returned pointer is of type void because sometimes it makes sense to cast it to a char* and sometimes to an unsigned char*, depending on whether it holds binary data or not โ so this API just ends up being more comfy to use. no cap. ๐
The returned pointer is valid until the next call to the next/prev iterator step. also, the pointer stops being valid the moment the iterator is released. so like, don't be holding onto it, bestie ๐
๐ ValkeyModule_DictPrevC
void *ValkeyModule_DictPrevC(ValkeyModuleDictIter *di,
size_t *keylen,
void **dataptr);
Available since: 5.0.0
This function is exactly like ValkeyModule_DictNext() but after returning the currently selected element in the iterator, it selects the previous element (lexicographically smaller) instead of the next one. going backwards is also main character energy ๐
โจ ValkeyModule_DictNext
ValkeyModuleString *ValkeyModule_DictNext(ValkeyModuleCtx *ctx,
ValkeyModuleDictIter *di,
void **dataptr);
Available since: 5.0.0
Like ValkeyModuleNextC(), but instead of handing back an internally allocated buffer and key length, it returns a module string object allocated in the specified context ctx โ which may be NULL exactly like for the main API ValkeyModule_CreateString. ๐ธ
The returned string object should be deallocated after use, either manually or by using a context that has automatic memory management active. free your stuff, bestie ๐
๐ฅ ValkeyModule_DictPrev
ValkeyModuleString *ValkeyModule_DictPrev(ValkeyModuleCtx *ctx,
ValkeyModuleDictIter *di,
void **dataptr);
Available since: 5.0.0
Like ValkeyModule_DictNext() but after returning the currently selected element in the iterator, it selects the previous element (lexicographically smaller) instead of the next one. ๐
๐ฆ ValkeyModule_DictCompareC
int ValkeyModule_DictCompareC(ValkeyModuleDictIter *di,
const char *op,
void *key,
size_t keylen);
Available since: 5.0.0
Compare the element the iterator is currently pointing at to the specified element given by key/keylen, according to the operator op โ and the set of valid operators here is the same set that's valid for ValkeyModule_DictIteratorStart. if the comparison succeeds the command returns VALKEYMODULE_OK, otherwise VALKEYMODULE_ERR comes back. โญ
This is useful when you just want to emit a lexicographical range โ so in the loop, as you iterate elements, you can also check whether you're still in range. iykyk ๐ง
The function also returns VALKEYMODULE_ERR if the iterator has reached the end-of-elements condition. ๐ธ
๐ ValkeyModule_DictCompare
int ValkeyModule_DictCompare(ValkeyModuleDictIter *di,
const char *op,
ValkeyModuleString *key);
Available since: 5.0.0
Like ValkeyModule_DictCompareC but it grabs the key to compare against the current iterator key as a ValkeyModuleString. ๐
๐ Modules Info fields
๐ ValkeyModule_InfoAddSection
int ValkeyModule_InfoAddSection(ValkeyModuleInfoCtx *ctx, const char *name);
Available since: 6.0.0
Used to start a new section, before adding any fields. the section name gets prefixed by <modulename>_ and must only include A-Z, a-z, 0-9. NULL or an empty string means the default section (just <modulename>) is used. and when the return value is VALKEYMODULE_ERR, the section should be โ and will be โ skipped. ๐
๐ ValkeyModule_InfoBeginDictField
int ValkeyModule_InfoBeginDictField(ValkeyModuleInfoCtx *ctx, const char *name);
Available since: 6.0.0
Starts a dict field, similar to the ones in INFO KEYSPACE. use the normal ValkeyModule_InfoAddField* functions to add the items to this field, and terminate with ValkeyModule_InfoEndDictField. โจ
๐ธ ValkeyModule_InfoEndDictField
int ValkeyModule_InfoEndDictField(ValkeyModuleInfoCtx *ctx);
Available since: 6.0.0
Ends a dict field โ see ValkeyModule_InfoBeginDictField. ๐ซถ
๐ ValkeyModule_InfoAddFieldString
int ValkeyModule_InfoAddFieldString(ValkeyModuleInfoCtx *ctx,
const char *field,
ValkeyModuleString *value);
Available since: 6.0.0
Used by ValkeyModuleInfoFunc to add info fields. each field gets automatically prefixed by <modulename>_. and heads up: field names or values must not include \r\n or :. keep it clean bestie ๐
๐ง ValkeyModule_InfoAddFieldCString
int ValkeyModule_InfoAddFieldCString(ValkeyModuleInfoCtx *ctx,
const char *field,
const char *value);
Available since: 6.0.0
See ValkeyModule_InfoAddFieldString(). same vibe, C string edition โจ
โญ ValkeyModule_InfoAddFieldDouble
int ValkeyModule_InfoAddFieldDouble(ValkeyModuleInfoCtx *ctx,
const char *field,
double value);
Available since: 6.0.0
See ValkeyModule_InfoAddFieldString(). ๐
๐ฅ ValkeyModule_InfoAddFieldLongLong
int ValkeyModule_InfoAddFieldLongLong(ValkeyModuleInfoCtx *ctx,
const char *field,
long long value);
Available since: 6.0.0
See ValkeyModule_InfoAddFieldString(). ๐
๐ ValkeyModule_InfoAddFieldULongLong
int ValkeyModule_InfoAddFieldULongLong(ValkeyModuleInfoCtx *ctx,
const char *field,
unsigned long long value);
Available since: 6.0.0
See ValkeyModule_InfoAddFieldString(). ๐
๐ฆ ValkeyModule_RegisterInfoFunc
int ValkeyModule_RegisterInfoFunc(ValkeyModuleCtx *ctx,
ValkeyModuleInfoFunc cb);
Available since: 6.0.0
Registers a callback for the INFO command. the callback should add INFO fields by calling the ValkeyModule_InfoAddField*() functions. ๐
โจ ValkeyModule_GetServerInfo
ValkeyModuleServerInfoData *ValkeyModule_GetServerInfo(ValkeyModuleCtx *ctx,
const char *section);
Available since: 6.0.0
Get information about the server, similar to what comes back 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 ValkeyModule_ServerInfoGetField and its friends to get the individual fields. when you're done, it needs to be freed with ValkeyModule_FreeServerInfo, or via the automatic memory management mechanism if that's enabled. ๐
๐ธ ValkeyModule_FreeServerInfo
void ValkeyModule_FreeServerInfo(ValkeyModuleCtx *ctx,
ValkeyModuleServerInfoData *data);
Available since: 6.0.0
Free data created with ValkeyModule_GetServerInfo(). you only need to pass the context pointer ctx if the dictionary was created using the context instead of passing NULL. ๐ซถ
๐ ValkeyModule_ServerInfoGetField
ValkeyModuleString *ValkeyModule_ServerInfoGetField(ValkeyModuleCtx *ctx,
ValkeyModuleServerInfoData *data,
const char *field);
Available since: 6.0.0
okay so you wanna pull the value of a field out of the data you scooped up with ValkeyModule_GetServerInfo(). ๐ You only need to pass that ctx pointer if you actually wanna use the auto memory mechanism to release the returned string. And if the field just... isn't there? You get NULL. That's it, that's the vibe. ๐
ValkeyModule_ServerInfoGetFieldC
const char *ValkeyModule_ServerInfoGetFieldC(ValkeyModuleServerInfoData *data,
const char *field);
Available since: 6.0.0
Lowkey the same energy as ValkeyModule_ServerInfoGetField, except it hands you back a char* and the caller should NOT free it. Like, hands off bestie, it's not yours to delete. ๐
โโ๏ธ
ValkeyModule_ServerInfoGetFieldSigned
long long ValkeyModule_ServerInfoGetFieldSigned(ValkeyModuleServerInfoData *data,
const char *field,
int *out_err);
Available since: 6.0.0
Grabs the value of a field from the data collected with ValkeyModule_GetServerInfo(). Now here's the tea: if the field isn't found, or it's not numerical, or it's out of range, the return value is 0 and that optional out_err argument gets set to VALKEYMODULE_ERR. So like, check your errors, bestie. ๐ซถ
ValkeyModule_ServerInfoGetFieldUnsigned
unsigned long long ValkeyModule_ServerInfoGetFieldUnsigned(ValkeyModuleServerInfoData *data,
const char *field,
int *out_err);
Available since: 6.0.0
Same deal โ field value from the data collected with ValkeyModule_GetServerInfo(). If the field is missing, not numerical, or out of range, you get 0 back and the optional out_err gets set to VALKEYMODULE_ERR. โจ
ValkeyModule_ServerInfoGetFieldDouble
double ValkeyModule_ServerInfoGetFieldDouble(ValkeyModuleServerInfoData *data,
const char *field,
int *out_err);
Available since: 6.0.0
Field value from the data collected with ValkeyModule_GetServerInfo(), one more time, but make it a double. ๐ If the field isn't found, or it's not a double, return value is 0 and the optional out_err argument gets set to VALKEYMODULE_ERR. It's giving consistency, honestly.
๐ ๏ธ Modules utility APIs โจ
ValkeyModule_GetRandomBytes
void ValkeyModule_GetRandomBytes(unsigned char *dst, size_t len);
Available since: 5.0.0
Returns random bytes using SHA1 in counter mode with a /dev/urandom initialized seed. ๐ This thing is fast, so you can generate a whole bunch of bytes without putting any dent in the operating system entropy pool. Iconic.
โ ๏ธ Heads up: currently this function is not thread safe. Don't be brave about it. ๐
ValkeyModule_GetRandomHexChars
void ValkeyModule_GetRandomHexChars(char *dst, size_t len);
Available since: 5.0.0
Just like ValkeyModule_GetRandomBytes(), except instead of filling the string with random bytes, the string gets filled with random characters from the hex charset [0-9a-f]. Cute, constrained, slay. ๐
๐ค Modules API exporting / importing ๐ฅ
ValkeyModule_ExportSharedAPI
int ValkeyModule_ExportSharedAPI(ValkeyModuleCtx *ctx,
const char *apiname,
void *func);
Available since: 5.0.4
A module calls this function to export some API under a given name. ๐ฆ Then other modules can use that API by calling the symmetrical function ValkeyModule_GetSharedAPI() and casting the return value to the right function pointer.
You get VALKEYMODULE_OK back if the name isn't already taken. If it is taken? You get VALKEYMODULE_ERR and no operation will be performed. Periodt.
๐ IMPORTANT: the apiname argument should be a string literal with static lifetime. The whole API relies on the fact that it will always be valid in the future โ so don't get cute and pass it something temporary.
ValkeyModule_GetSharedAPI
void *ValkeyModule_GetSharedAPI(ValkeyModuleCtx *ctx, const char *apiname);
Available since: 5.0.4
Requests an exported API pointer. The return value is just a void pointer that whoever calls this function is required to cast to the right function pointer, so like... it's a private contract between modules. iykyk. ๐ค
If the requested API isn't available then NULL is returned. And because modules can be loaded at different times in different orders, these function calls should be put inside some module generic API registering step, one that gets called every time a module attempts to execute a command that requires external APIs. If some API can't be resolved, the command should return an error. ๐ธ
Here's an example:
int ... myCommandImplementation(void) {
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 = ValkeyModule_GetSharedAPI("...");
if (myFunctionPointer == NULL) return 0;
return 1;
}
๐๏ธ Module Command Filter API
ValkeyModule_RegisterCommandFilter
ValkeyModuleCommandFilter *ValkeyModule_RegisterCommandFilter(ValkeyModuleCtx *ctx,
ValkeyModuleCommandFilterFunc callback,
int flags);
Available since: 5.0.5
Registers a new command filter function. ๐
Command filtering is how modules get to extend the server by plugging into the execution flow of all commands. Like, all of them. ๐
A registered filter gets called before the server executes any command. That includes both core server commands and commands registered by any module. The filter applies in every execution path, including:
- Invocation by a client.
- Invocation through
ValkeyModule_Call()by any module. - Invocation through Lua
server.call(). - Replication of a command from a primary.
The filter executes in a special filter context, which is different from โ and way more limited than โ a ValkeyModuleCtx. And because the filter affects any command, it has to be implemented in a super efficient way to reduce the performance impact on the server. ๐โโ๏ธ๐จ All Module API calls that require a valid context (like ValkeyModule_Call(), ValkeyModule_OpenKey(), etc.) are not supported in a filter context.
The ValkeyModuleCommandFilterCtx can be used to inspect or modify the executed command and its arguments. And since the filter runs before the server begins processing the command, any change you make affects how the command gets processed. For example, a module can override server commands like this:
- Register a
MODULE.SETcommand which implements an extended version of theSETcommand. - Register a command filter which detects invocation of
SETon a specific pattern of keys. Once detected, the filter will replace the first argument fromSETtoMODULE.SET. - When filter execution is complete, the server 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 ValkeyModule_Call(), the filter will be applied on that call as well. ๐ If that's not what you want, the VALKEYMODULE_CMDFILTER_NOSELF flag can be set when registering the filter.
The VALKEYMODULE_CMDFILTER_NOSELF flag prevents execution flows that originate from the module's own ValkeyModule_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. So like, be aware.
If multiple filters are registered (by the same module or by different ones), they execute in the order of registration. First come, first filtered. ๐
ValkeyModule_UnregisterCommandFilter
int ValkeyModule_UnregisterCommandFilter(ValkeyModuleCtx *ctx,
ValkeyModuleCommandFilter *filter);
Available since: 5.0.5
Unregisters a command filter. That's the whole thing. Bye bestie. ๐
ValkeyModule_CommandFilterArgsCount
int ValkeyModule_CommandFilterArgsCount(ValkeyModuleCommandFilterCtx *fctx);
Available since: 5.0.5
Returns the number of arguments a filtered command has. And yes, the number of arguments includes the command itself. Don't forget that part or your math will be off. ๐งฎ
ValkeyModule_CommandFilterArgGet
ValkeyModuleString *ValkeyModule_CommandFilterArgGet(ValkeyModuleCommandFilterCtx *fctx,
int pos);
Available since: 5.0.5
Returns the specified command argument. The first argument (position 0) is the command itself, and everything after that is user-provided args. โญ
ValkeyModule_CommandFilterArgInsert
int ValkeyModule_CommandFilterArgInsert(ValkeyModuleCommandFilterCtx *fctx,
int pos,
ValkeyModuleString *arg);
Available since: 5.0.5
Modifies the filtered command by inserting a new argument at the specified position. ๐ฆ
๐จ Okay listen: the specified ValkeyModuleString argument may be used by the server after the filter context is destroyed, so it must not be auto-memory allocated, freed, or used elsewhere. Respect the lifetime, bestie.
ValkeyModule_CommandFilterArgReplace
int ValkeyModule_CommandFilterArgReplace(ValkeyModuleCommandFilterCtx *fctx,
int pos,
ValkeyModuleString *arg);
Available since: 5.0.5
Modifies the filtered command by replacing an existing argument with a new one. Out with the old, in with the new. ๐
๐จ Same warning as above, fr fr: the specified ValkeyModuleString argument may be used by the server after the filter context is destroyed, so it must not be auto-memory allocated, freed, or used elsewhere.
ValkeyModule_CommandFilterArgDelete
int ValkeyModule_CommandFilterArgDelete(ValkeyModuleCommandFilterCtx *fctx,
int pos);
Available since: 5.0.5
Modifies the filtered command by deleting an argument at the specified position. Gone. Poof. ๐
ValkeyModule_CommandFilterGetClientId
unsigned long long ValkeyModule_CommandFilterGetClientId(ValkeyModuleCommandFilterCtx *fctx);
Available since: 7.2.0
Gets the Client ID for the client that issued the command we're filtering. ๐ Know your audience.
ValkeyModule_MallocSize
size_t ValkeyModule_MallocSize(void *ptr);
Available since: 6.0.0
For a given pointer allocated via ValkeyModule_Alloc() or ValkeyModule_Realloc(), returns 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. The allocator is doing its own thing, and honestly? Let it.
ValkeyModule_MallocUsableSize
size_t ValkeyModule_MallocUsableSize(void *ptr);
Available since: 7.0.1
Okay so it's basically the sibling of ValkeyModule_MallocSize ๐ โ the one difference is that ValkeyModule_MallocUsableSize gives you back the usable size of memory used by the module.
ValkeyModule_MallocSizeString
size_t ValkeyModule_MallocSizeString(ValkeyModuleString *str);
Available since: 7.0.0
Same energy as ValkeyModule_MallocSize, except it works on ValkeyModuleString pointers. ๐
ValkeyModule_MallocSizeDict
size_t ValkeyModule_MallocSizeDict(ValkeyModuleDict *dict);
Available since: 7.0.0
Same as ValkeyModule_MallocSize, except it works on ValkeyModuleDict pointers. Heads up bestie: the value it returns is only the overhead of the underlying structures โ it does not include the allocation size of the keys and values. ๐
ValkeyModule_GetUsedMemoryRatio
float ValkeyModule_GetUsedMemoryRatio(void);
Available since: 6.0.0
Hands you a number between 0 and 1 telling you how much memory is being used right now, relative to the server's "maxmemory" configuration. โจ
- 0 โ no memory limit configured.
- Between 0 and 1 โ the percentage of the memory used, normalized into the 0-1 range.
- Exactly 1 โ memory limit reached.
- Greater 1 โ more memory used than the configured limit. ๐
๐ฆ Scanning keyspace and hashes ๐ฆ
ValkeyModule_ScanCursorCreate
ValkeyModuleScanCursor *ValkeyModule_ScanCursorCreate(void);
Available since: 6.0.0
Makes you a brand-new cursor to be used with ValkeyModule_Scan. ๐
ValkeyModule_ScanCursorRestart
void ValkeyModule_ScanCursorRestart(ValkeyModuleScanCursor *cursor);
Available since: 6.0.0
Restarts an existing cursor. The keys will be rescanned. ๐
ValkeyModule_ScanCursorDestroy
void ValkeyModule_ScanCursorDestroy(ValkeyModuleScanCursor *cursor);
Available since: 6.0.0
Destroys the cursor struct. bye bestie ๐
ValkeyModule_Scan
int ValkeyModule_Scan(ValkeyModuleCtx *ctx,
ValkeyModuleScanCursor *cursor,
ValkeyModuleScanCB fn,
void *privdata);
Available since: 6.0.0
The scan API that lets a module scan all the keys and values in the selected db. ๐
Callback for scan implementation:
void scan_callback(ValkeyModuleCtx *ctx, ValkeyModuleString *keyname,
ValkeyModuleKey *key, void *privdata);
ctxโ the module context provided for the scan.keynameโ owned by the caller, and it needs to be retained if you're gonna use it after this function.keyโ holds info on the key and value, provided as best effort. In some cases it might beNULL, and then you should (and can) useValkeyModule_OpenKey()(andCloseKeytoo). When it is provided, it's owned by the caller and will be freed when the callback returns.privdataโ the user data provided toValkeyModule_Scan().
The way it should be used:
ValkeyModuleScanCursor *c = ValkeyModule_ScanCursorCreate();
while(ValkeyModule_Scan(ctx, c, callback, privateData));
ValkeyModule_ScanCursorDestroy(c);
It's also possible to use this API from another thread as long as the lock is acquired during the actual call to ValkeyModule_Scan:
ValkeyModuleScanCursor *c = ValkeyModule_ScanCursorCreate();
ValkeyModule_ThreadSafeContextLock(ctx);
while(ValkeyModule_Scan(ctx, c, callback, privateData)){
ValkeyModule_ThreadSafeContextUnlock(ctx);
// do some background job
ValkeyModule_ThreadSafeContextLock(ctx);
}
ValkeyModule_ScanCursorDestroy(c);
The function returns 1 if there are more elements to scan and 0 otherwise, possibly setting errno if the call failed. ๐ญ
It's also possible to restart an existing cursor using ValkeyModule_ScanCursorRestart.
IMPORTANT ๐จ This API is very similar to the SCAN command from the point of view of the guarantees it provides. That means the API may report duplicated keys, but it 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 inside the callback, be aware that the internal state of the database may change. For instance it's safe to delete or modify the current key, but it may not be safe to delete any other key. Also, playing with the keyspace while iterating may have the effect of returning more duplicates. A safe pattern is to store the key names you want to modify elsewhere and do the actions on those keys later, once the iteration is complete. But that 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 that's safe. ๐
ValkeyModule_ScanKey
int ValkeyModule_ScanKey(ValkeyModuleKey *key,
ValkeyModuleScanCursor *cursor,
ValkeyModuleScanKeyCB fn,
void *privdata);
Available since: 6.0.0
Scan API that lets a module scan the elements in a hash, set or sorted set key. ๐ธ
Callback for scan implementation:
void scan_callback(ValkeyModuleKey *key, ValkeyModuleString* field, ValkeyModuleString* value, void *privdata);
keyโ the key context provided for the scan.fieldโ field name, owned by the caller, needs to be retained if used after this function.valueโ value string, orNULLfor the set type, owned by the caller, needs to be retained if used after this function.privdataโ the user data provided toValkeyModule_ScanKey.
The way it should be used:
ValkeyModuleScanCursor *c = ValkeyModule_ScanCursorCreate();
ValkeyModuleKey *key = ValkeyModule_OpenKey(...)
while(ValkeyModule_ScanKey(key, c, callback, privateData));
ValkeyModule_CloseKey(key);
ValkeyModule_ScanCursorDestroy(c);
It's also possible to use this API from another thread while the lock is acquired during the actual call to ValkeyModule_ScanKey, re-opening the key each time:
ValkeyModuleScanCursor *c = ValkeyModule_ScanCursorCreate();
ValkeyModule_ThreadSafeContextLock(ctx);
ValkeyModuleKey *key = ValkeyModule_OpenKey(...)
while(ValkeyModule_ScanKey(ctx, c, callback, privateData)){
ValkeyModule_CloseKey(key);
ValkeyModule_ThreadSafeContextUnlock(ctx);
// do some background job
ValkeyModule_ThreadSafeContextLock(ctx);
ValkeyModuleKey *key = ValkeyModule_OpenKey(...)
}
ValkeyModule_CloseKey(key);
ValkeyModule_ScanCursorDestroy(c);
The function returns 1 if there are more elements to scan and 0 otherwise, possibly setting errno if the call failed. It's also possible to restart an existing cursor using ValkeyModule_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.
๐ฅ Module fork API ๐ฅ
ValkeyModule_Fork
int ValkeyModule_Fork(ValkeyModuleForkDoneHandler cb, void *user_data);
Available since: 6.0.0
Creates a background child process with the current frozen snapshot of the main process, where you can do some processing in the background without affecting or freezing the traffic โ and with no need for threads and GIL locking. ๐ฆ
Note that the server allows for only one concurrent fork. ๐
When the child wants to exit, it should call ValkeyModule_ExitFromChild. If the parent wants to kill the child it should call ValkeyModule_KillForkChild. The done handler callback will be executed on the parent process when the child exited (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.
ValkeyModule_SendChildHeartbeat
void ValkeyModule_SendChildHeartbeat(double progress);
Available since: 6.2.0
The module is advised to call this function from the fork child once in a while, so it can report progress and COW memory to the parent, which will be reported in INFO. The progress argument should be between 0 and 1, or -1 when not available. ๐
ValkeyModule_ExitFromChild
int ValkeyModule_ExitFromChild(int retcode);
Available since: 6.0.0
Call this from the child process when you want to terminate it. retcode will be provided to the done handler executed on the parent process. โจ
ValkeyModule_KillForkChild
int ValkeyModule_KillForkChild(int child_pid);
Available since: 6.0.0
Can be used to kill the forked child process from the parent process. child_pid would be the return value of ValkeyModule_Fork. ๐
๐ Server hooks implementation ๐
ValkeyModule_SubscribeToServerEvent
int ValkeyModule_SubscribeToServerEvent(ValkeyModuleCtx *ctx,
ValkeyModuleEvent event,
ValkeyModuleEventCallback callback);
Available since: 6.0.0
Register to be notified, via a callback, when the specified server event happens. ๐ซถ The callback is called with the event as argument, plus an additional argument which is a void pointer and should be cast to a specific type that is event-specific (but many events will just use NULL since they don't 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 (*ValkeyModuleEventCallback)(ValkeyModuleCtx *ctx,
ValkeyModuleEvent eid,
uint64_t subevent,
void *data);
The ctx is a normal module context that the callback can use in order to call other module APIs. The eid is the event itself, and this is only useful in the case where the module subscribed to multiple events: using the id field of this structure it's 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:
ValkeyModuleEvent_ReplicationRoleChanged๐ซ
Fired when the instance flips from primary to replica or the other way round โ but bestie, it also fires when the replica stays a replica and just starts replicating with a different primary. So it's giving "role change" but sometimes it's giving "situationship change" ๐
Sub events available:
VALKEYMODULE_SUBEVENT_REPLROLECHANGED_NOW_PRIMARYVALKEYMODULE_SUBEVENT_REPLROLECHANGED_NOW_REPLICA
The data field can be casted by the callback to a ValkeyModuleReplicationInfo structure with the following fields:
int primary; // true if primary, false if replica
char *primary_host; // primary instance hostname for NOW_REPLICA
int primary_port; // primary 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
ValkeyModuleEvent_Persistence๐
Fired when RDB saving or AOF rewriting starts and ends. Sub events available:
VALKEYMODULE_SUBEVENT_PERSISTENCE_RDB_STARTVALKEYMODULE_SUBEVENT_PERSISTENCE_AOF_STARTVALKEYMODULE_SUBEVENT_PERSISTENCE_SYNC_RDB_STARTVALKEYMODULE_SUBEVENT_PERSISTENCE_SYNC_AOF_STARTVALKEYMODULE_SUBEVENT_PERSISTENCE_ENDEDVALKEYMODULE_SUBEVENT_PERSISTENCE_FAILED
Okay so these aren't only triggered when the user calls the relevant commands like BGSAVE โ they also fire when a saving operation or AOF rewriting happens because of internal server triggers. Periodt. The SYNC_RDB_START sub events happen in the foreground due to the SAVE command, FLUSHALL, or server shutdown. The other RDB and AOF sub events run in a background fork child, so any action the module takes can only affect the generated AOF or RDB โ it will not be reflected in the parent process, and won't affect connected clients and commands. Also lowkey important: the AOF_START sub event may end up saving RDB content in case of an AOF with rdb-preamble. iykyk ๐
ValkeyModuleEvent_FlushDB๐
FLUSHALL, FLUSHDB or an internal flush happened (for instance because of replication, after the replica synchronization). Sub events available:
VALKEYMODULE_SUBEVENT_FLUSHDB_STARTVALKEYMODULE_SUBEVENT_FLUSHDB_END
The data pointer can be casted to a ValkeyModuleFlushInfo 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 initiated, which is iconic because it lets the callback call DBSIZE or other operations on the yet-to-free keyspace. Get your info before it's gone bestie ๐ซถ
ValkeyModuleEvent_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 primary. Sub events available:
VALKEYMODULE_SUBEVENT_LOADING_RDB_STARTVALKEYMODULE_SUBEVENT_LOADING_AOF_STARTVALKEYMODULE_SUBEVENT_LOADING_REPL_STARTVALKEYMODULE_SUBEVENT_LOADING_ENDEDVALKEYMODULE_SUBEVENT_LOADING_FAILED
Heads up: AOF loading may start with an RDB data in case of rdb-preamble, in which case you'll only receive an AOF_START event. Not me warning you in advance ๐
ValkeyModuleEvent_ClientChange๐ฆ
Called when a client connects or disconnects. The data pointer can be casted to a ValkeyModuleClientInfo structure, documented in ValkeyModule_GetClientInfoById(). Sub events available:
VALKEYMODULE_SUBEVENT_CLIENT_CHANGE_CONNECTEDVALKEYMODULE_SUBEVENT_CLIENT_CHANGE_DISCONNECTED
ValkeyModuleEvent_Shutdown๐
The server is shutting down. No subevents are available. That's it, that's the whole vibe.
ValkeyModuleEvent_ReplicaChange๐ธ
Called when the instance (which can be both a primary or a replica) gets a new online replica, or loses a replica because it got disconnected. Sub events available:
VALKEYMODULE_SUBEVENT_REPLICA_CHANGE_ONLINEVALKEYMODULE_SUBEVENT_REPLICA_CHANGE_OFFLINE
No additional information is available so far: future versions of the server will have an API in order to enumerate the replicas connected and their state. So like... manifesting that for future us ๐
ValkeyModuleEvent_CronLoopโฐ
Called every time the server 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 the server 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 ValkeyModuleCronLoop structure with the following fields:
int32_t hz; // Approximate number of events per second.
ValkeyModuleEvent_PrimaryLinkChange๐
Called for replicas in order to notify when the replication link becomes functional (up) with our primary, or when it goes down. Note that the link is not considered up when we just connected to the primary โ only if the replication is actually happening correctly. No fake news here. Sub events available:
VALKEYMODULE_SUBEVENT_PRIMARY_LINK_UPVALKEYMODULE_SUBEVENT_PRIMARY_LINK_DOWN
ValkeyModuleEvent_ModuleChange๐
Called when a new module is loaded or one is unloaded. Sub events available:
VALKEYMODULE_SUBEVENT_MODULE_LOADEDVALKEYMODULE_SUBEVENT_MODULE_UNLOADED
The data pointer can be casted to a ValkeyModuleModuleChange structure with the following fields:
const char* module_name; // Name of module loaded or unloaded.
int32_t module_version; // Module version.
ValkeyModuleEvent_LoadingProgress๐
Called repeatedly while an RDB or AOF file is being loaded. Sub events available:
VALKEYMODULE_SUBEVENT_LOADING_PROGRESS_RDBVALKEYMODULE_SUBEVENT_LOADING_PROGRESS_AOF
The data pointer can be casted to a ValkeyModuleLoadingProgress 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.
ValkeyModuleEvent_SwapDB๐
Called when a SWAPDB command has been successfully executed. For this event call, currently there are no subevents available.
The data pointer can be casted to a ValkeyModuleSwapDbInfo structure with the following fields:
int32_t dbnum_first; // Swap Db first dbnum
int32_t dbnum_second; // Swap Db second dbnum
ValkeyModuleEvent_ReplBackupโ ๏ธ
WARNING: Replication Backup events are deprecated since Redis OSS 7.0 and are never fired. See ValkeyModuleEvent_ReplAsyncLoad for understanding how Async Replication Loading events are now triggered when repl-diskless-load is set to swapdb.
Called when repl-diskless-load config is set to swapdb, and the server needs to backup the current database for the possibility to be restored later. A module with global data and maybe with aux_load and aux_save callbacks may need to use this notification to backup / restore / discard its globals. Sub events available:
VALKEYMODULE_SUBEVENT_REPL_BACKUP_CREATEVALKEYMODULE_SUBEVENT_REPL_BACKUP_RESTOREVALKEYMODULE_SUBEVENT_REPL_BACKUP_DISCARD
ValkeyModuleEvent_ReplAsyncLoad๐
Called when repl-diskless-load config is set to swapdb and a replication with a primary of same data set history (matching replication ID) occurs. In which case the server serves the current data set while loading a new database in memory from the socket. Modules must have declared they support this mechanism in order to activate it, through the VALKEYMODULE_OPTIONS_HANDLE_REPL_ASYNC_LOAD flag. Sub events available:
VALKEYMODULE_SUBEVENT_REPL_ASYNC_LOAD_STARTEDVALKEYMODULE_SUBEVENT_REPL_ASYNC_LOAD_ABORTEDVALKEYMODULE_SUBEVENT_REPL_ASYNC_LOAD_COMPLETED
ValkeyModuleEvent_ForkChild๐ด
Called when a fork child (AOFRW, RDBSAVE, module fork...) is born/dies. Sub events available:
VALKEYMODULE_SUBEVENT_FORK_CHILD_BORNVALKEYMODULE_SUBEVENT_FORK_CHILD_DIED
ValkeyModuleEvent_EventLoop๐
Called on each event loop iteration, once just before the event loop goes to sleep or just after it wakes up. Sub events available:
VALKEYMODULE_SUBEVENT_EVENTLOOP_BEFORE_SLEEPVALKEYMODULE_SUBEVENT_EVENTLOOP_AFTER_SLEEP
ValkeyModuleEvent_Configโ๏ธ
Called when a configuration event happens. Sub events available:
VALKEYMODULE_SUBEVENT_CONFIG_CHANGE
The data pointer can be casted to a ValkeyModuleConfigChange structure with the following fields:
const char **config_names; // An array of C string pointers containing the
// name of each modified configuration item
uint32_t num_changes; // The number of elements in the config_names array
ValkeyModuleEvent_Key๐
Called when a key is removed from the keyspace. We can't modify any key in the event. Sub events available:
VALKEYMODULE_SUBEVENT_KEY_DELETEDVALKEYMODULE_SUBEVENT_KEY_EXPIREDVALKEYMODULE_SUBEVENT_KEY_EVICTEDVALKEYMODULE_SUBEVENT_KEY_OVERWRITTEN
The data pointer can be casted to a ValkeyModuleKeyInfo structure with the following fields:
ValkeyModuleKey *key; // Key name
ValkeyModuleEvent_AuthenticationAttempt๐ต๏ธ
Called when an authentication attempt is made, either successful or not.
The data pointer can be casted to a ValkeyModuleAuthenticationInfo structure with the following fields:
uint64_t client_id; // Client ID.
const char *username; // Username used for authentication.
const char *module_name; // Name of the module that is handling the
// authentication. It is NULL if the
// authentication is handled by the core.
ValkeyModuleAuthenticationResult result; // Result of the authentication:
// VALKEYMODULE_AUTH_RESULT_GRANTED or
// VALKEYMODULE_AUTH_RESULT_DENIED
- ValkeyModuleEvent_AtomicSlotMigration
okay bestie this one is the whole cluster tea โโจ It fires when an atomic slot migration (CLUSTER MIGRATESLOTS) is started or ended on this node. And plot twist โ this node might be the target, might be the source, or the target/source might be this node's primary. Everyone's involved, no one's safe ๐
. The subevents you can get are:
VALKEYMODULE_SUBEVENT_ATOMIC_SLOT_MIGRATION_IMPORT_STARTEDVALKEYMODULE_SUBEVENT_ATOMIC_SLOT_MIGRATION_EXPORT_STARTEDVALKEYMODULE_SUBEVENT_ATOMIC_SLOT_MIGRATION_IMPORT_ABORTEDVALKEYMODULE_SUBEVENT_ATOMIC_SLOT_MIGRATION_EXPORT_ABORTEDVALKEYMODULE_SUBEVENT_ATOMIC_SLOT_MIGRATION_IMPORT_COMPLETEDVALKEYMODULE_SUBEVENT_ATOMIC_SLOT_MIGRATION_EXPORT_COMPLETED
That data pointer? Cast it to a ValkeyModuleAtomicSlotMigrationInfo structure, which has these fields:
char *job_name; // Unique ID for the operation (40 chars)
ValkeyModuleSlotRange *slot_ranges; // Array of slot ranges involved in the operation
uint32_t num_slot_ranges; // Number of slot ranges in slot_ranges array
And the ValkeyModuleSlotRange structure is giving:
int start; // First slot in this range, inclusive
int end; // Last slot in this range, inclusive
Modules can use these notifications to track the start and end of slot migrations. Here's the lore: slot migrations will start with a STARTED subevent and end with a COMPLETED subevent if they were successful and ownership is transferred โ or an ABORTED subevent if they were not successful and no ownership change was made. While a slot migration is active, modules will see incoming commands and keyspace notifications for importing keys. Important bestie: importing keys will not be accessible to clients unless the slot migration is COMPLETED. Not before. Not ever. Periodt ๐
The function returns VALKEYMODULE_OK if the module was successfully subscribed for the specified event. If the API is called from a wrong context or an unsupported event is given, then VALKEYMODULE_ERR is returned. wrong context? err. bye. ๐
๐ธ ValkeyModule_IsSubEventSupported ๐ธ
int ValkeyModule_IsSubEventSupported(ValkeyModuleEvent event, int64_t subevent);
Available since: 6.0.9
For a given server event and subevent, return zero if the subevent is not supported and non-zero otherwise. That's it. That's the whole function. A little vibe check for your subevents โจ
๐ Module Configurations API ๐
okay this is the big one, the config girlies are gonna EAT ๐
๐ ValkeyModule_RegisterStringConfig ๐
int ValkeyModule_RegisterStringConfig(ValkeyModuleCtx *ctx,
const char *name,
const char *default_val,
unsigned int flags,
ValkeyModuleConfigGetStringFunc getfn,
ValkeyModuleConfigSetStringFunc setfn,
ValkeyModuleConfigApplyFunc applyfn,
void *privdata);
Available since: 7.0.0
Create a string config that users can interact with via the server config file, CONFIG SET, CONFIG GET, and CONFIG REWRITE commands. she's everywhere, she's iconic โจ
The actual config value is owned by the module, and the getfn, setfn and optional applyfn callbacks that are provided to the server in order to access or manipulate the value. breakdown: the getfn callback retrieves the value from the module, while the setfn callback provides a value to be stored into the module config. The optional applyfn callback is called after a CONFIG SET command modified one or more configs using the setfn callback, and can be used to atomically apply a config after several configs were changed together. If there are multiple configs with applyfn callbacks set by a single CONFIG SET command, they will be deduplicated if their applyfn function and privdata pointers are identical, and the callback will only be run once. dedupe era ๐
Both the setfn and applyfn can return an error if the provided value is invalid or cannot be used. The config also declares a type for the value that is validated by the server and provided to the module. The config system provides the following types:
- String: Binary safe string data.
- Enum: One of a finite number of string tokens, provided during registration.
- Numeric: 64 bit signed integer, which also supports min and max values.
- Bool: Yes or no value.
The setfn callback is expected to return VALKEYMODULE_OK when the value is successfully applied. It can also return VALKEYMODULE_ERR if the value can't be applied, and the *err pointer can be set with a ValkeyModuleString error message to provide to the client. This ValkeyModuleString will be freed by the server after returning from the set callback. noted, don't touch it after, it's gone โจ
All configs are registered with a name, a type, a default value, private data that is made available in the callbacks, as well as several flags that modify the behavior of the config. The name must only contain alphanumeric characters or dashes. The supported flags are:
VALKEYMODULE_CONFIG_DEFAULT: The default flags for a config. This creates a config that can be modified after startup.VALKEYMODULE_CONFIG_IMMUTABLE: This config can only be provided loading time.VALKEYMODULE_CONFIG_SENSITIVE: The value stored in this config is redacted from all logging. shy config, respect her privacy ๐VALKEYMODULE_CONFIG_HIDDEN: The name is hidden fromCONFIG GETwith pattern matching.VALKEYMODULE_CONFIG_PROTECTED: This config will be only be modifiable based off the value ofenable-protected-configs.VALKEYMODULE_CONFIG_DENY_LOADING: This config is not modifiable while the server is loading data.VALKEYMODULE_CONFIG_MEMORY: For numeric configs, this config will convert data unit notations into their byte equivalent.VALKEYMODULE_CONFIG_BITFLAGS: For enum configs, this config will allow multiple entries to be combined as bit flags.
Default values are used on startup to set the value if it is not provided via the config file or command line. Default values are also used to compare to on a config rewrite.
Notes:
- On string config sets that the string passed to the set callback will be freed after execution and the module must retain it.
- On string config gets the string will not be consumed and will be valid after execution.
Example implementation:
ValkeyModuleString *strval;
int adjustable = 1;
ValkeyModuleString *getStringConfigCommand(const char *name, void *privdata) {
return strval;
}
int setStringConfigCommand(const char *name, ValkeyModuleString *new, void *privdata, ValkeyModuleString **err) {
if (adjustable) {
ValkeyModule_Free(strval);
ValkeyModule_RetainString(NULL, new);
strval = new;
return VALKEYMODULE_OK;
}
*err = ValkeyModule_CreateString(NULL, "Not adjustable.", 15);
return VALKEYMODULE_ERR;
}
...
ValkeyModule_RegisterStringConfig(ctx, "string", NULL, VALKEYMODULE_CONFIG_DEFAULT, getStringConfigCommand,
setStringConfigCommand, NULL, NULL);
If the registration fails, VALKEYMODULE_ERR is returned and one of the following errno is set:
EBUSY: Registering the Config outside ofValkeyModule_OnLoad.EINVAL: The provided flags are invalid for the registration or the name of the config contains invalid characters.EALREADY: The provided configuration name is already used.
so basically: load time only, valid flags only, unique name only. the three strikes rule ๐
๐ฆ ValkeyModule_RegisterBoolConfig ๐ฆ
int ValkeyModule_RegisterBoolConfig(ValkeyModuleCtx *ctx,
const char *name,
int default_val,
unsigned int flags,
ValkeyModuleConfigGetBoolFunc getfn,
ValkeyModuleConfigSetBoolFunc setfn,
ValkeyModuleConfigApplyFunc applyfn,
void *privdata);
Available since: 7.0.0
Create a bool config that server clients can interact with via the CONFIG SET, CONFIG GET, and CONFIG REWRITE commands. See ValkeyModule_RegisterStringConfig for detailed information about configs. yes/no girlies rise ๐ซถ
๐ ValkeyModule_RegisterEnumConfig ๐
int ValkeyModule_RegisterEnumConfig(ValkeyModuleCtx *ctx,
const char *name,
int default_val,
unsigned int flags,
const char **enum_values,
const int *int_values,
int num_enum_vals,
ValkeyModuleConfigGetEnumFunc getfn,
ValkeyModuleConfigSetEnumFunc setfn,
ValkeyModuleConfigApplyFunc applyfn,
void *privdata);
Available since: 7.0.0
Create an enum config that server clients can interact with via the CONFIG SET, CONFIG GET, and CONFIG REWRITE commands. Enum configs are a set of string tokens to corresponding integer values, where the string value is exposed to clients but the inter value is passed to the server and the module. so the outside world sees cute words, the inside world gets numbers. it's giving translator ๐
These values are defined in enum_values, an array of null-terminated c strings, and int_vals, an array of enum values who has an index partner in enum_values.
Example Implementation:
const char *enum_vals[3] = {โfirstโ, โsecondโ, โthirdโ};
const int int_vals[3] = {0, 2, 4};
int enum_val = 0;
int getEnumConfigCommand(const char *name, void *privdata) {
return enum_val;
}
int setEnumConfigCommand(const char *name, int val, void *privdata, const char **err) {
enum_val = val;
return VALKEYMODULE_OK;
}
...
ValkeyModule_RegisterEnumConfig(ctx, "enum", 0, VALKEYMODULE_CONFIG_DEFAULT, enum_vals, int_vals, 3,
getEnumConfigCommand, setEnumConfigCommand, NULL, NULL);
โจ and that's the config trio, bestie โ string, bool, enum, all registered, all iconic โจ
okay so quick lil note before we move on โ you can totally use VALKEYMODULE_CONFIG_BITFLAGS so that multiple enum strings get smooshed together into one single integer as bit flags. iconic. And if you do that, you probs wanna sort your enums so the combinations you actually want show up first. ๐
Also, go read ValkeyModule_RegisterStringConfig if you want the whole general rundown on how configs work, bestie, because we're not re-explaining all that here.
ValkeyModule_RegisterNumericConfig
int ValkeyModule_RegisterNumericConfig(ValkeyModuleCtx *ctx,
const char *name,
long long default_val,
unsigned int flags,
long long min,
long long max,
ValkeyModuleConfigGetNumericFunc getfn,
ValkeyModuleConfigSetNumericFunc setfn,
ValkeyModuleConfigApplyFunc applyfn,
void *privdata);
Available since: 7.0.0
Makes an integer config that server clients can poke at with CONFIG SET, CONFIG GET, and CONFIG REWRITE. ๐งฎ Again, ValkeyModule_RegisterStringConfig has the full tea on configs, so go there for the details.
ValkeyModule_LoadConfigs
int ValkeyModule_LoadConfigs(ValkeyModuleCtx *ctx);
Available since: 7.0.0
Applies all the pending configs when the module loads. Call this after you've registered every single config for your module, inside ValkeyModule_OnLoad. โจ
It returns VALKEYMODULE_ERR if you call it outside of ValkeyModule_OnLoad. not allowed, babes.
You need to call this API when configs are provided either in MODULE LOADEX or handed over as startup arguments. ๐ซถ
๐ RDB load/save API ๐
ValkeyModule_RdbStreamCreateFromFile
ValkeyModuleRdbStream *ValkeyModule_RdbStreamCreateFromFile(const char *filename);
Available since: 7.2.0
Creates a stream object so you can save/load RDB to/from a file. ๐
This function hands you back a pointer to ValkeyModuleRdbStream, and that thing is owned by you. Which means you have to call ValkeyModule_RdbStreamFree() to free it. No leaking, bestie. ๐ซง
ValkeyModule_RdbStreamFree
void ValkeyModule_RdbStreamFree(ValkeyModuleRdbStream *stream);
Available since: 7.2.0
Releases an RDB stream object. bye bye ๐
ValkeyModule_RdbLoad
int ValkeyModule_RdbLoad(ValkeyModuleCtx *ctx,
ValkeyModuleRdbStream *stream,
int flags);
Available since: 7.2.0
Loads an RDB file from the stream. Heads up: the dataset gets cleared first, and then the RDB file loads. ๐ so like, be aware.
flags must be zero. It's there for future use, iykyk.
On success VALKEYMODULE_OK comes back. Otherwise you get VALKEYMODULE_ERR and errno is set accordingly. ๐ญ
Example:
ValkeyModuleRdbStream *s = ValkeyModule_RdbStreamCreateFromFile("exp.rdb");
ValkeyModule_RdbLoad(ctx, s, 0);
ValkeyModule_RdbStreamFree(s);
ValkeyModule_RdbSave
int ValkeyModule_RdbSave(ValkeyModuleCtx *ctx,
ValkeyModuleRdbStream *stream,
int flags);
Available since: 7.2.0
Saves the dataset to the RDB stream. ๐พ
flags must be zero. Future use again, no cap.
On success VALKEYMODULE_OK is returned, otherwise VALKEYMODULE_ERR is returned and errno is set accordingly. ๐
Example:
ValkeyModuleRdbStream *s = ValkeyModule_RdbStreamCreateFromFile("exp.rdb");
ValkeyModule_RdbSave(ctx, s, 0);
ValkeyModule_RdbStreamFree(s);
ValkeyModule_RegisterScriptingEngine
int ValkeyModule_RegisterScriptingEngine(ValkeyModuleCtx *module_ctx,
const char *engine_name,
ValkeyModuleScriptingEngineCtx *engine_ctx,
ValkeyModuleScriptingEngineMethods *engine_methods);
Available since: 8.1.0
Registers a brand new scripting engine in the server. main character energy ๐ฅ
module_ctx: the module context object.engine_name: the name of the scripting engine. This name gets matched against the engine name specified in the script header using a shebang.engine_ctx: engine specific context pointer.engine_methods: the struct with the scripting engine callback function pointers.
Returns VALKEYMODULE_OK if the engine registered successfully, and VALKEYMODULE_ERR if something went wrong. And if something did go wrong, an error message gets logged. ๐ซ
ValkeyModule_UnregisterScriptingEngine
int ValkeyModule_UnregisterScriptingEngine(ValkeyModuleCtx *ctx,
const char *engine_name);
Available since: 8.1.0
Removes the scripting engine from the server. ๐ช
engine_name is the name of the scripting engine.
Returns VALKEYMODULE_OK.
ValkeyModule_GetFunctionExecutionState
ValkeyModuleScriptingEngineExecutionState ValkeyModule_GetFunctionExecutionState( ValkeyModuleScriptingEngineServerRuntimeCtx *server_ctx);
Available since: 8.1.0
Returns the state of the function that's currently being executed by the scripting engine. ๐
server_ctx is the server runtime context.
It'll return VMSE_STATE_KILLED if the function was already killed, either by a SCRIPT KILL or a FUNCTION KILL. rip to that function fr.
๐๏ธ Key eviction API ๐จ
ValkeyModule_SetLRU
int ValkeyModule_SetLRU(ValkeyModuleKey *key, mstime_t lru_idle);
Available since: 6.0.0
Sets the key last access time for LRU based eviction. Not relevant if the server's maxmemory policy is LFU based, so like, don't bother. The value is idle time in milliseconds. Returns VALKEYMODULE_OK if the LRU was updated, VALKEYMODULE_ERR otherwise. โฐ
ValkeyModule_GetLRU
int ValkeyModule_GetLRU(ValkeyModuleKey *key, mstime_t *lru_idle);
Available since: 6.0.0
Gets the key last access time. The value is idletime in milliseconds, or -1 if the server's eviction policy is LFU based. Returns VALKEYMODULE_OK if when key is valid. ๐ฐ๏ธ
ValkeyModule_SetLFU
int ValkeyModule_SetLFU(ValkeyModuleKey *key, long long lfu_freq);
Available since: 6.0.0
Sets the key access frequency. Only relevant if the server's maxmemory policy is LFU based. The frequency is a logarithmic counter that gives you an indication of the access frequency โ and it must be <= 255. Returns VALKEYMODULE_OK if the LFU was updated, VALKEYMODULE_ERR otherwise. ๐
ValkeyModule_GetLFU
int ValkeyModule_GetLFU(ValkeyModuleKey *key, long long *lfu_freq);
Available since: 6.0.0
Gets the key access frequency, or -1 if the server's eviction policy is not LFU based. Returns VALKEYMODULE_OK if when key is valid. ๐
๐ Miscellaneous APIs ๐
ValkeyModule_GetModuleOptionsAll
int ValkeyModule_GetModuleOptionsAll(void);
Available since: 7.2.0
Returns the full module options flags mask. Using that return value, your module can check whether a certain set of module options is supported by the server version in use. ๐ต๏ธ Example:
int supportedFlags = ValkeyModule_GetModuleOptionsAll();
if (supportedFlags & VALKEYMODULE_OPTIONS_ALLOW_NESTED_KEYSPACE_NOTIFICATIONS) {
// VALKEYMODULE_OPTIONS_ALLOW_NESTED_KEYSPACE_NOTIFICATIONS is supported
} else{
// VALKEYMODULE_OPTIONS_ALLOW_NESTED_KEYSPACE_NOTIFICATIONS is not supported
}
ValkeyModule_GetContextFlagsAll
int ValkeyModule_GetContextFlagsAll(void);
Available since: 6.0.9
Returns the full ContextFlags mask. With that return value, your module can check if a certain set of flags is supported by the server version in use. ๐ Example:
int supportedFlags = ValkeyModule_GetContextFlagsAll();
if (supportedFlags & VALKEYMODULE_CTX_FLAGS_MULTI) {
// VALKEYMODULE_CTX_FLAGS_MULTI is supported
} else{
// VALKEYMODULE_CTX_FLAGS_MULTI is not supported
}
ValkeyModule_GetKeyspaceNotificationFlagsAll
int ValkeyModule_GetKeyspaceNotificationFlagsAll(void);
Available since: 6.0.9
Returns the full KeyspaceNotification mask. Using the return value, the module can check if a certain set of flags is supported by the server version in use. ๐ Example:
int supportedFlags = ValkeyModule_GetKeyspaceNotificationFlagsAll();
if (supportedFlags & VALKEYMODULE_NOTIFY_LOADED) {
// VALKEYMODULE_NOTIFY_LOADED is supported
} else{
// VALKEYMODULE_NOTIFY_LOADED is not supported
}
ValkeyModule_GetServerVersion
int ValkeyModule_GetServerVersion(void);
Available since: 6.0.9
Returns the server version in the format 0x00MMmmpp. So like, for 6.0.7 the return value will be 0x00060007. ๐ท๏ธ math but make it hex, slay.
ValkeyModule_GetTypeMethodVersion
int ValkeyModule_GetTypeMethodVersion(void);
Available since: 6.2.0
Returns the current server runtime value of VALKEYMODULE_TYPE_METHOD_VERSION. You can use that when you're calling ValkeyModule_CreateDataType, so you know which fields of ValkeyModuleTypeMethods are gonna be supported and which ones are just gonna get ignored. ๐
ValkeyModule_ModuleTypeReplaceValue
int ValkeyModule_ModuleTypeReplaceValue(ValkeyModuleKey *key,
moduleType *mt,
void *new_value,
void **old_value);
Available since: 6.0.0
Replaces 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 the caller specified. No substitutions bestie.
Unlike ValkeyModule_ModuleTypeSetValue(), which will free the old value, this function just swaps the old value with the new value. Cute and simple. ๐
The function returns VALKEYMODULE_OK on success, VALKEYMODULE_ERR on errors such as:
- Key is not opened for writing.
- Key is not a module data type key.
- Key is a module datatype other than
`mt'.
If old_value is non-NULL, the old value is returned by reference. ๐ซด
ValkeyModule_GetCommandKeysWithFlags
int *ValkeyModule_GetCommandKeysWithFlags(ValkeyModuleCtx *ctx,
ValkeyModuleString **argv,
int argc,
int *num_keys,
int **out_flags);
Available since: 7.0.0
For a specified command, this parses its arguments and returns an array containing the indexes of all the key name arguments. ๐๏ธ Basically it's a way more efficient way to do COMMAND GETKEYS, so like, glow up your key parsing. โจ
The out_flags argument is optional, bestie โ you can literally just set it to NULL if you don't care. But if you do pass it in, it gets filled with VALKEYMODULE_CMD_KEY_ flags, sitting at the same indexes as the key indexes of the array you got back. Matching indexes, matching vibes. ๐
If you get a NULL back? That means the command you asked about has no keys at all, or something went wrong. And when something goes wrong, it tells you by setting errno like this:
ENOENTโ the command you specified does not exist. It's giving ghost. ๐ปEINVALโ you gave an invalid command arity.
NOTE: the array you get back is not a Module object, so it does not get automatically freed โ not even when you're using auto-memory. Nope. You, the caller, have to explicitly callValkeyModule_Free()to free it. Same deal for theout_flagspointer, if you used one. Don't forget, fr fr. ๐ซถ
ValkeyModule_GetCommandKeys
int *ValkeyModule_GetCommandKeys(ValkeyModuleCtx *ctx,
ValkeyModuleString **argv,
int argc,
int *num_keys);
Available since: 6.0.9
Exactly the same as ValkeyModule_GetCommandKeysWithFlags for when you don't need the flags. Iconic shortcut behaviour. โจ
ValkeyModule_GetCurrentCommandName
const char *ValkeyModule_GetCurrentCommandName(ValkeyModuleCtx *ctx);
Available since: 6.2.5
Returns the name of the command that's currently running. Main character identification, basically. ๐
๐ง Defrag API ๐ง
ValkeyModule_RegisterDefragFunc
int ValkeyModule_RegisterDefragFunc(ValkeyModuleCtx *ctx,
ValkeyModuleDefragFunc cb);
Available since: 6.2.0
Registers a defrag callback for your global data โ meaning literally anything the module allocates that isn't tied to one specific data type. Global girlies only. ๐
ValkeyModule_DefragShouldStop
int ValkeyModule_DefragShouldStop(ValkeyModuleDefragCtx *ctx);
Available since: 6.2.0
When your data type's defrag callback is iterating through complex structures, you should call this function periodically. Think of it as a vibe check for your loop. ๐
A zero (false) return means the callback can keep going. A non-zero value (true) means it should stop. Periodt.
When it does stop, the callback can use ValkeyModule_DefragCursorSet() to save its position, so later it can use ValkeyModule_DefragCursorGet() to resume defragging from where it left off. Save point unlocked. ๐พ
When it's stopped and there's still more work left to do, the callback should return 1. Otherwise, it should return 0.
NOTE: modules should be mindful of how frequently this function gets called, so it generally makes sense to do small batches of work in between the calls. Snack-sized chunks, not the whole meal. ๐
ValkeyModule_DefragCursorSet
int ValkeyModule_DefragCursorSet(ValkeyModuleDefragCtx *ctx,
unsigned long cursor);
Available since: 6.2.0
Stores an arbitrary cursor value for future re-use. Bookmark it, bestie. ๐
This should only be called if ValkeyModule_DefragShouldStop() has returned a non-zero value and the defrag callback is about to exit without fully iterating its data type.
This behaviour is reserved for cases where late defrag is performed. Late defrag gets selected for keys that implement the free_effort callback and return a free_effort value that is larger than the defrag active-defrag-max-scan-fields configuration directive.
Smaller keys, keys that don't implement free_effort, or the global defrag callback are not called in late-defrag mode. In those cases, calling this function will return VALKEYMODULE_ERR. Sorry babes, wrong door. ๐ช
The cursor may be used by the module to represent some progress into the module's data type. Modules may also store additional cursor-related information locally and use the cursor as a flag that indicates when traversal of a new key begins. This is possible because the API makes a guarantee that concurrent defragmentation of multiple keys will not be performed. One key at a time, no chaos. ๐
ValkeyModule_DefragCursorGet
int ValkeyModule_DefragCursorGet(ValkeyModuleDefragCtx *ctx,
unsigned long *cursor);
Available since: 6.2.0
Fetches a cursor value that was previously stored using ValkeyModule_DefragCursorSet(). Retrieving your save file. ๐
If it's not called for a late defrag operation, VALKEYMODULE_ERR will be returned and the cursor should be ignored. See ValkeyModule_DefragCursorSet() for more details on defrag cursors โ it's all there, go look. ๐
ValkeyModule_DefragAlloc
void *ValkeyModule_DefragAlloc(ValkeyModuleDefragCtx *ctx, void *ptr);
Available since: 6.2.0
Defrags a memory allocation that was previously allocated by ValkeyModule_Alloc, ValkeyModule_Calloc, etc. The defragmentation process involves allocating a new memory block and copying the contents over to it โ like realloc() but make it a glow-up. โจ
If defragmentation wasn't necessary, NULL is returned and the operation has no other effect. Nothing happened, no notes. ๐
If a non-NULL value is returned, the caller should use the new pointer instead of the old one and update any reference to the old pointer โ which must not be used again. Like, at all. Ever. It's over. ๐ซ
ValkeyModule_DefragValkeyModuleString
ValkeyModuleString *ValkeyModule_DefragValkeyModuleString(ValkeyModuleDefragCtx *ctx,
ValkeyModuleString *str);
Available since: 7.2.5
Defrags a ValkeyModuleString previously allocated by ValkeyModule_Alloc, ValkeyModule_Calloc, etc. See ValkeyModule_DefragAlloc() for more information on how the defragmentation process works.
NOTE: it is only possible to defrag strings that have a single reference. Typically this means strings retained withValkeyModule_RetainStringorValkeyModule_HoldStringmay not be defragmentable. One exception is command argvs which, if retained by the module, will end up with a single reference โ because the reference on the server side is dropped as soon as the command callback returns. Solo strings only, no plus-ones. ๐ซ
ValkeyModule_GetKeyNameFromDefragCtx
const ValkeyModuleString *ValkeyModule_GetKeyNameFromDefragCtx(ValkeyModuleDefragCtx *ctx);
Available since: 7.0.0
Returns the name of the key currently being processed. There is no guarantee that the key name is always available, so this may return NULL. Sometimes the info just isn't there, and that's not a you problem. ๐
ValkeyModule_GetDbIdFromDefragCtx
int ValkeyModule_GetDbIdFromDefragCtx(ValkeyModuleDefragCtx *ctx);
Available since: 7.0.0
Returns the database id of the key currently being processed. There is no guarantee that this info is always available, so this may return -1. Same energy as above โ no promises. ๐คท
โญ Function index โญ
The whole squad, alphabetically-ish, in order:
ValkeyModule_ACLAddLogEntryValkeyModule_ACLAddLogEntryByUserNameValkeyModule_ACLCheckChannelPermissionsValkeyModule_ACLCheckCommandPermissionsValkeyModule_ACLCheckKeyPermissionsValkeyModule_AbortBlockValkeyModule_AddACLCategoryValkeyModule_AddPostNotificationJobValkeyModule_AllocValkeyModule_AuthenticateClientWithACLUserValkeyModule_AuthenticateClientWithUserValkeyModule_AutoMemoryValkeyModule_AvoidReplicaTrafficValkeyModule_BlockClientValkeyModule_BlockClientGetPrivateDataValkeyModule_BlockClientOnAuthValkeyModule_BlockClientOnKeysValkeyModule_BlockClientOnKeysWithFlagsValkeyModule_BlockClientSetPrivateDataValkeyModule_BlockedClientDisconnectedValkeyModule_BlockedClientMeasureTimeEndValkeyModule_BlockedClientMeasureTimeStartValkeyModule_CachedMicrosecondsValkeyModule_CallValkeyModule_CallReplyArrayElementValkeyModule_CallReplyAttributeValkeyModule_CallReplyAttributeElementValkeyModule_CallReplyBigNumberValkeyModule_CallReplyBoolValkeyModule_CallReplyDoubleValkeyModule_CallReplyIntegerValkeyModule_CallReplyLengthValkeyModule_CallReplyMapElementValkeyModule_CallReplyPromiseAbortValkeyModule_CallReplyPromiseSetUnblockHandlerValkeyModule_CallReplyProtoValkeyModule_CallReplySetElementValkeyModule_CallReplyStringPtrValkeyModule_CallReplyTypeValkeyModule_CallReplyVerbatimValkeyModule_CallocValkeyModule_ChannelAtPosWithFlagsValkeyModule_CloseKeyValkeyModule_ClusterCanonicalKeyNameInSlotValkeyModule_ClusterKeySlotValkeyModule_CommandFilterArgDeleteValkeyModule_CommandFilterArgGetValkeyModule_CommandFilterArgInsertValkeyModule_CommandFilterArgReplaceValkeyModule_CommandFilterArgsCountValkeyModule_CommandFilterGetClientIdValkeyModule_CreateCommandValkeyModule_CreateDataTypeValkeyModule_CreateDictValkeyModule_CreateModuleUserValkeyModule_CreateStringValkeyModule_CreateStringFromCallReplyValkeyModule_CreateStringFromDoubleValkeyModule_CreateStringFromLongDoubleValkeyModule_CreateStringFromLongLongValkeyModule_CreateStringFromStreamIDValkeyModule_CreateStringFromStringValkeyModule_CreateStringFromULongLongValkeyModule_CreateStringPrintfValkeyModule_CreateSubcommandValkeyModule_CreateTimerValkeyModule_DbSizeValkeyModule_DeauthenticateAndCloseClientValkeyModule_DefragAllocValkeyModule_DefragCursorGetValkeyModule_DefragCursorSetValkeyModule_DefragShouldStopValkeyModule_DefragValkeyModuleStringValkeyModule_DeleteKeyValkeyModule_DictCompareValkeyModule_DictCompareCValkeyModule_DictDelValkeyModule_DictDelCValkeyModule_DictGetValkeyModule_DictGetCValkeyModule_DictIteratorReseekValkeyModule_DictIteratorReseekCValkeyModule_DictIteratorStartValkeyModule_DictIteratorStartCValkeyModule_DictIteratorStopValkeyModule_DictNextValkeyModule_DictNextCValkeyModule_DictPrevValkeyModule_DictPrevCValkeyModule_DictReplaceValkeyModule_DictReplaceCValkeyModule_DictSetValkeyModule_DictSetCValkeyModule_DictSizeValkeyModule_DigestAddLongLongValkeyModule_DigestAddStringBufferValkeyModule_DigestEndSequenceValkeyModule_EmitAOFValkeyModule_EventLoopAddValkeyModule_EventLoopAddOneShotValkeyModule_EventLoopDelValkeyModule_ExitFromChildValkeyModule_ExportSharedAPIValkeyModule_ForkValkeyModule_FreeValkeyModule_FreeCallReplyValkeyModule_FreeClusterNodesListValkeyModule_FreeDictValkeyModule_FreeModuleUserValkeyModule_FreeServerInfoValkeyModule_FreeStringValkeyModule_FreeThreadSafeContextValkeyModule_GetAbsExpireValkeyModule_GetBlockedClientHandleValkeyModule_GetBlockedClientPrivateDataValkeyModule_GetBlockedClientReadyKeyValkeyModule_GetClientCertificateValkeyModule_GetClientIdValkeyModule_GetClientInfoByIdValkeyModule_GetClientNameByIdValkeyModule_GetClientUserNameByIdValkeyModule_GetClusterNodeInfo
okay bestie we're back and we are picking up mid-alphabet like it's absolutely nothing ๐ this stretch is the big roster โ every single API function, all of them, in order, no skips, no edits, exactly as they are โจ
ValkeyModule_GetClusterNodeInfoForClientValkeyModule_GetClusterNodesListValkeyModule_GetClusterSizeValkeyModule_GetCommandValkeyModule_GetCommandKeysValkeyModule_GetCommandKeysWithFlagsValkeyModule_GetContextFlagsValkeyModule_GetContextFlagsAllValkeyModule_GetCurrentCommandNameValkeyModule_GetCurrentUserNameValkeyModule_GetDbIdFromDefragCtxValkeyModule_GetDbIdFromDigestValkeyModule_GetDbIdFromIOValkeyModule_GetDbIdFromModuleKeyValkeyModule_GetDbIdFromOptCtxValkeyModule_GetDetachedThreadSafeContextValkeyModule_GetExpireValkeyModule_GetFunctionExecutionStateValkeyModule_GetKeyNameFromDefragCtxValkeyModule_GetKeyNameFromDigestValkeyModule_GetKeyNameFromIOValkeyModule_GetKeyNameFromModuleKeyValkeyModule_GetKeyNameFromOptCtxValkeyModule_GetKeyspaceNotificationFlagsAllValkeyModule_GetLFUValkeyModule_GetLRUValkeyModule_GetModuleOptionsAllValkeyModule_GetModuleUserACLStringValkeyModule_GetModuleUserFromUserNameValkeyModule_GetMyClusterIDValkeyModule_GetNotifyKeyspaceEventsValkeyModule_GetOpenKeyModesAllValkeyModule_GetRandomBytesValkeyModule_GetRandomHexCharsValkeyModule_GetSelectedDbValkeyModule_GetServerInfoValkeyModule_GetServerVersionValkeyModule_GetSharedAPIValkeyModule_GetThreadSafeContextValkeyModule_GetTimerInfoValkeyModule_GetToDbIdFromOptCtxValkeyModule_GetToKeyNameFromOptCtxValkeyModule_GetTypeMethodVersionValkeyModule_GetUsedMemoryRatioValkeyModule_HashGetValkeyModule_HashSetValkeyModule_HoldStringValkeyModule_InfoAddFieldCStringValkeyModule_InfoAddFieldDoubleValkeyModule_InfoAddFieldLongLongValkeyModule_InfoAddFieldStringValkeyModule_InfoAddFieldULongLongValkeyModule_InfoAddSectionValkeyModule_InfoBeginDictFieldValkeyModule_InfoEndDictFieldValkeyModule_IsBlockedReplyRequestValkeyModule_IsBlockedTimeoutRequestValkeyModule_IsChannelsPositionRequestValkeyModule_IsIOErrorValkeyModule_IsKeysPositionRequestValkeyModule_IsModuleNameBusyValkeyModule_IsSubEventSupportedValkeyModule_KeyAtPosValkeyModule_KeyAtPosWithFlagsValkeyModule_KeyExistsValkeyModule_KeyTypeValkeyModule_KillForkChildValkeyModule_LatencyAddSampleValkeyModule_ListDeleteValkeyModule_ListGetValkeyModule_ListInsertValkeyModule_ListPopValkeyModule_ListPushValkeyModule_ListSetValkeyModule_LoadConfigsValkeyModule_LoadDataTypeFromStringValkeyModule_LoadDataTypeFromStringEncverValkeyModule_LoadDoubleValkeyModule_LoadFloatValkeyModule_LoadLongDoubleValkeyModule_LoadSignedValkeyModule_LoadStringValkeyModule_LoadStringBufferValkeyModule_LoadUnsignedValkeyModule_LogValkeyModule_LogIOErrorValkeyModule_MallocSizeValkeyModule_MallocSizeDictValkeyModule_MallocSizeStringValkeyModule_MallocUsableSizeValkeyModule_MicrosecondsValkeyModule_MillisecondsValkeyModule_ModuleTypeGetTypeValkeyModule_ModuleTypeGetValueValkeyModule_ModuleTypeReplaceValueValkeyModule_ModuleTypeSetValueValkeyModule_MonotonicMicrosecondsValkeyModule_MustObeyClientValkeyModule_NotifyKeyspaceEventValkeyModule_OpenKeyValkeyModule_PoolAllocValkeyModule_PublishMessageValkeyModule_PublishMessageShardValkeyModule_RandomKeyValkeyModule_RdbLoadValkeyModule_RdbSaveValkeyModule_RdbStreamCreateFromFileValkeyModule_RdbStreamFreeValkeyModule_ReallocValkeyModule_RedactClientCommandArgumentValkeyModule_RegisterAuthCallbackValkeyModule_RegisterBoolConfigValkeyModule_RegisterClusterMessageReceiverValkeyModule_RegisterCommandFilterValkeyModule_RegisterDefragFuncValkeyModule_RegisterEnumConfigValkeyModule_RegisterInfoFuncValkeyModule_RegisterNumericConfigValkeyModule_RegisterScriptingEngineValkeyModule_RegisterStringConfigValkeyModule_ReplicateValkeyModule_ReplicateVerbatimValkeyModule_ReplySetArrayLengthValkeyModule_ReplySetAttributeLengthValkeyModule_ReplySetMapLengthValkeyModule_ReplySetSetLengthValkeyModule_ReplyWithArrayValkeyModule_ReplyWithAttributeValkeyModule_ReplyWithBigNumberValkeyModule_ReplyWithBoolValkeyModule_ReplyWithCStringValkeyModule_ReplyWithCallReplyValkeyModule_ReplyWithDoubleValkeyModule_ReplyWithEmptyArrayValkeyModule_ReplyWithEmptyStringValkeyModule_ReplyWithErrorValkeyModule_ReplyWithErrorFormatValkeyModule_ReplyWithLongDoubleValkeyModule_ReplyWithLongLongValkeyModule_ReplyWithMapValkeyModule_ReplyWithNullValkeyModule_ReplyWithNullArrayValkeyModule_ReplyWithSetValkeyModule_ReplyWithSimpleStringValkeyModule_ReplyWithStringValkeyModule_ReplyWithStringBufferValkeyModule_ReplyWithVerbatimStringValkeyModule_ReplyWithVerbatimStringTypeValkeyModule_ResetDatasetValkeyModule_RetainStringValkeyModule_SaveDataTypeToStringValkeyModule_SaveDoubleValkeyModule_SaveFloatValkeyModule_SaveLongDoubleValkeyModule_SaveSignedValkeyModule_SaveStringValkeyModule_SaveStringBufferValkeyModule_SaveUnsignedValkeyModule_ScanValkeyModule_ScanCursorCreateValkeyModule_ScanCursorDestroyValkeyModule_ScanCursorRestartValkeyModule_ScanKeyValkeyModule_SelectDbValkeyModule_SendChildHeartbeatValkeyModule_SendClusterMessageValkeyModule_ServerInfoGetFieldValkeyModule_ServerInfoGetFieldCValkeyModule_ServerInfoGetFieldDoubleValkeyModule_ServerInfoGetFieldSignedValkeyModule_ServerInfoGetFieldUnsignedValkeyModule_SetAbsExpireValkeyModule_SetClientNameByIdValkeyModule_SetClusterFlagsValkeyModule_SetCommandACLCategoriesValkeyModule_SetCommandInfoValkeyModule_SetContextUserValkeyModule_SetDisconnectCallbackValkeyModule_SetExpireValkeyModule_SetLFUValkeyModule_SetLRUValkeyModule_SetModuleOptionsValkeyModule_SetModuleUserACLValkeyModule_SetModuleUserACLStringValkeyModule_SignalKeyAsReadyValkeyModule_SignalModifiedKeyValkeyModule_StopTimerValkeyModule_StrdupValkeyModule_StreamAddValkeyModule_StreamDeleteValkeyModule_StreamIteratorDeleteValkeyModule_StreamIteratorNextFieldValkeyModule_StreamIteratorNextIDValkeyModule_StreamIteratorStartValkeyModule_StreamIteratorStopValkeyModule_StreamTrimByIDValkeyModule_StreamTrimByLengthValkeyModule_StringAppendBufferValkeyModule_StringCompareValkeyModule_StringDMAValkeyModule_StringPtrLenValkeyModule_StringSetValkeyModule_StringToDoubleValkeyModule_StringToLongDoubleValkeyModule_StringToLongLongValkeyModule_StringToStreamIDValkeyModule_StringToULongLongValkeyModule_StringTruncateValkeyModule_SubscribeToKeyspaceEventsValkeyModule_SubscribeToServerEventValkeyModule_ThreadSafeContextLockValkeyModule_ThreadSafeContextTryLockValkeyModule_ThreadSafeContextUnlockValkeyModule_TrimStringAllocationValkeyModule_TryAllocValkeyModule_TryCallocValkeyModule_TryReallocValkeyModule_UnblockClientValkeyModule_UnlinkKeyValkeyModule_UnregisterCommandFilterValkeyModule_UnregisterScriptingEngineValkeyModule_UpdateRuntimeArgsValkeyModule_ValueLengthValkeyModule_WrongArityValkeyModule_YieldValkeyModule_ZsetAddValkeyModule_ZsetFirstInLexRangeValkeyModule_ZsetFirstInScoreRangeValkeyModule_ZsetIncrbyValkeyModule_ZsetLastInLexRangeValkeyModule_ZsetLastInScoreRangeValkeyModule_ZsetRangeCurrentElementValkeyModule_ZsetRangeEndReachedValkeyModule_ZsetRangeNextValkeyModule_ZsetRangePrevValkeyModule_ZsetRangeStopValkeyModule_ZsetRemValkeyModule_ZsetScoreValkeyModule__Assert
and that's the whole roster bestie, every last one, no cap ๐ if you actually read through all of these you are genuinely the main character of module dev, periodt ๐ your fave is built different fr fr.
this page is from Valkey9.0.0, dated2025-10-21, and it lives in section 7 of the manual asVALKEY-MODULES-API-REF(7). now go build something iconic ๐ซถโจ
๐ฆ where this page comes from
- fedora 44: valkey-doc 9.0.3-2.fc44 ยท CC-BY-SA-4.0
- fedora 44: valkey-doc 9.0.6-1.fc44 ยท CC-BY-SA-4.0
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms