VALKEY-MODULES-API-REF(7)

๐Ÿ“ฆ
version 9.0.6 ยท 2 versions
, in fedora 44

๐Ÿ’… 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 with malloc() inside your module using ValkeyModule_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 the PSUBSCRIBE and PUNSUBSCRIBE commands. not intended to be used with PUBLISH permissions.

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 on monitor. 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 than O(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, see ValkeyModule_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. Position 0 is always the command name itself. 0 for 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 (-1 means the last argument provided). 0 for commands with no keys.
  • keystep โ€” step between first and last key indexes. 0 for 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 pass 0,0,0 and use ValkeyModule_SetCommandInfo to set key specs using a more advanced scheme, and use ValkeyModule_SetCommandACLCategories to 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-cluster but cluster mode is enabled
  • parent is already a subcommand (we do not allow more than one level of command nesting)
  • parent is a command with an implementation (ValkeyModuleCmdFunc) (a parent command should be a pure container of subcommands)
  • parent already has a subcommand called name
  • 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_OK on successfully adding the new ACL category.
  • VALKEYMODULE_ERR on failure.

On error the errno is set to:

  • EINVAL if the name contains invalid characters.
  • EBUSY if the category name already exists.
  • ENOMEM if the number of categories reached the max limit of 64 categories.

๐ŸŽ€ 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 to VALKEYMODULE_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 of ValkeyModuleCommandHistoryEntry (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. See valkey-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 -N to 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 of 0 (or an omitted arity field) is equivalent to -2 if the command has sub commands and -1 otherwise. ๐Ÿ”ข
  • key_specs โ€” An array of ValkeyModuleCommandKeySpec, terminated by an element memset to zero. This is a scheme that tries to describe the positions of key arguments better than the old ValkeyModule_CreateCommand arguments firstkey, lastkey, keystep and 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 the begin_search_type field.
    • bs.index.pos โ€” the index from which we start the search for keys. (VALKEYMODULE_KSPEC_BS_INDEX only!)
    • bs.keyword.keyword โ€” the keyword (a string) that indicates the beginning of the key arguments. (VALKEYMODULE_KSPEC_BS_KEYWORD only!)
    • bs.keyword.startfrom โ€” an index in argv from which to start searching. it can be negative, which means start the search from the end, in reverse. example: -2 means start in reverse from the penultimate argument. (VALKEYMODULE_KSPEC_BS_KEYWORD only!)
  • 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_type and fk can 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 the find_keys_type field.
    • fk.range (for VALKEYMODULE_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. -1 indicates the last argument, -2 one 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 โ€” if lastkey is -1, we use limit to stop the search by a factor. 0 and 1 mean no limit. 2 means 1/2 of the remaining args, 3 means 1/3, and so on.
    • fk.keynum (for VALKEYMODULE_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 after keynumidx, in which case it should be set to keynumidx + 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 of ValkeyModuleCommandArg, terminated by an element memset to zero. ValkeyModuleCommandArg is 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 types VALKEYMODULE_ARG_TYPE_ONEOF and VALKEYMODULE_ARG_TYPE_BLOCK require an argument to have sub-arguments, aka subargs.
  • key_spec_index โ€” if the type is VALKEYMODULE_ARG_TYPE_KEY you must provide the index of the key-spec associated with this argument. see key_specs above. if the argument is not a key, you may specify -1.
  • token โ€” the token preceding the argument (optional). example: the argument seconds in SET has a token EX. if the argument consists of only a token (for example NX in SET) the type should be VALKEYMODULE_ARG_TYPE_PURE_TOKEN and value should be NULL.
  • summary โ€” a short description of the argument (optional).
  • since โ€” the first version which included this argument (optional).
  • flags โ€” a bitwise or of the macros VALKEYMODULE_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 of COMMAND. if token is not NULL, 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: the KEEPTTL option of the SET command.
  • VALKEYMODULE_ARG_TYPE_ONEOF โ€” used when the user can choose only one of a few sub-arguments. requires subargs. example: the NX and XX options of SET.
  • 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". requires subargs. example: the LIMIT offset count parameters in ZRANGE.

Explanation of the command argument flags ๐Ÿ”ฅ

  • VALKEYMODULE_CMD_ARG_OPTIONAL โ€” the argument is optional (like GET in the SET command).
  • VALKEYMODULE_CMD_ARG_MULTIPLE โ€” the argument may repeat itself (like key in DEL).
  • VALKEYMODULE_CMD_ARG_MULTIPLE_TOKEN โ€” the argument may repeat itself, and so does its token (like GET pattern in SORT).

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 allow repl-diskless-load to work if it's enabled. The module should use ValkeyModule_IsIOError after 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 โ€” see ValkeyModule_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 with LOADING status.
  • 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_Replicate and ValkeyModule_EmitAOF are 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 and CLUSTER MIGRATESLOTS will 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:

  1. ValkeyModule_CloseKey()
  2. ValkeyModule_FreeCallReply()
  3. 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:

  1. You have automatic memory management enabled.
  2. You want to create string objects.
  3. 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:

  1. Future versions of the server may abandon auto-trimming entirely. ๐Ÿ‘€
  2. 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: err has 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: fmt must 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 pass 0 as the parameter to the fmt argument of ValkeyModule_Call so that the ValkeyModuleCallReply comes 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: ๐Ÿ‘‘

  1. 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. ๐Ÿ”
  2. 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:

  • ENOENT if the client does not exist
  • EINVAL if 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 script
  • VALKEYMODULE_CTX_FLAGS_MULTI: the command is running inside a transaction
  • VALKEYMODULE_CTX_FLAGS_REPLICATED: the command was sent over the replication link by the PRIMARY
  • VALKEYMODULE_CTX_FLAGS_PRIMARY: the instance is a primary
  • VALKEYMODULE_CTX_FLAGS_REPLICA: the instance is a replica
  • VALKEYMODULE_CTX_FLAGS_READONLY: the instance is read-only
  • VALKEYMODULE_CTX_FLAGS_CLUSTER: the instance is in cluster mode
  • VALKEYMODULE_CTX_FLAGS_AOF: the instance has AOF enabled
  • VALKEYMODULE_CTX_FLAGS_RDB: the instance has RDB enabled
  • VALKEYMODULE_CTX_FLAGS_MAXMEMORY: the instance has Maxmemory set
  • VALKEYMODULE_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 setting
  • VALKEYMODULE_CTX_FLAGS_OOM_WARNING: less than 25% of memory remains before reaching the maxmemory level
  • VALKEYMODULE_CTX_FLAGS_LOADING: server is loading RDB/AOF
  • VALKEYMODULE_CTX_FLAGS_REPLICA_IS_STALE: no active link with the primary
  • VALKEYMODULE_CTX_FLAGS_REPLICA_IS_CONNECTING: the replica is trying to connect with the primary
  • VALKEYMODULE_CTX_FLAGS_REPLICA_IS_TRANSFERRING: primary -> replica RDB transfer is in progress
  • VALKEYMODULE_CTX_FLAGS_REPLICA_IS_ONLINE: the replica has an active link with its primary โ€” literally the opposite of the STALE state
  • VALKEYMODULE_CTX_FLAGS_ACTIVE_CHILD: there's currently some background process active (RDB, AUX or module)
  • VALKEYMODULE_CTX_FLAGS_MULTI_DIRTY: the next EXEC will fail due to dirty CAS (touched keys) ๐Ÿ’€
  • VALKEYMODULE_CTX_FLAGS_IS_CHILD: the server is currently running inside a background child process
  • VALKEYMODULE_CTX_FLAGS_RESP3: indicates that the client attached to this context is using RESP3
  • VALKEYMODULE_CTX_FLAGS_SERVER_STARTUP: the instance is starting
  • VALKEYMODULE_CTX_FLAGS_SLOT_IMPORT_CLIENT: indicates that the client attached to this context is the slot import client
  • VALKEYMODULE_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 opened
  • VALKEYMODULE_OPEN_KEY_NONOTIFY โ€” don't trigger keyspace event on key misses
  • VALKEYMODULE_OPEN_KEY_NOSTATS โ€” don't update keyspace hits/misses counters
  • VALKEYMODULE_OPEN_KEY_NOEXPIRE โ€” avoid deleting lazy expired keys
  • VALKEYMODULE_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 access
  • VALKEYMODULE_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:

  1. 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.
  2. 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.
  3. 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 a ValkeyModule_StringTruncate() call should be used if there is to enlarge the string, and later call StringDMA() 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:

  • EINVAL if key or ele is NULL.
  • ENOTSUP if the key is of another type than list.
  • EBADF if the key is not opened for writing.
Note: Before Redis OSS 7.0, errno was 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:

  • EINVAL if key is NULL.
  • ENOTSUP if the key is empty or of another type than list.
  • EBADF if the key is not opened for writing.
Note: Before Redis OSS 7.0, errno was 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:

  • EINVAL if key is NULL.
  • ENOTSUP if the key is not a list.
  • EBADF if the key is not opened for reading.
  • EDOM if 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:

  • EINVAL if key or value is NULL.
  • ENOTSUP if the key is not a list.
  • EBADF if the key is not opened for writing.
  • EDOM if 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:

  • EINVAL if key or value is NULL.
  • ENOTSUP if the key of another type than list.
  • EBADF if the key is not opened for writing.
  • EDOM if 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:

  • EINVAL if key or value is NULL.
  • ENOTSUP if the key is not a list.
  • EBADF if the key is not opened for writing.
  • EDOM if 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 with XX.
  • VALKEYMODULE_ZADD_LT: If element exists, new score must be less than the current score. Do nothing otherwise. Can optionally be combined with XX.

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 the XX or NX flags. 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 score double 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 ele in 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:

  1. 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_INFINITE for a positive infinite value โ™พ๏ธ
  • VALKEYMODULE_NEGATIVE_INFINITE for 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:

  1. The value stored at key is not a sorted set, or the key is empty.
  2. The lexicographical range min and max format 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 of ValkeyModuleString objects.
  • 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:

  • EINVAL if any unknown flags are set, or if key is NULL.
  • ENOTSUP if the key is associated with a non Hash value.
  • EBADF if the key was not opened for writing.
  • ENOENT if 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 the COUNT_ALL flag was unset, or if changes were held back due to the NX and XX flags.
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 a ValkeyModuleString pointer-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 of
    • VALKEYMODULE_STREAM_ADD_AUTOID: Assign a stream ID automatically, like * in the XADD command.
  • id: If the AUTOID flag is set, this is where the assigned ID is returned. Can be NULL if AUTOID is set, if you don't care to receive the ID. If AUTOID is not set, this is the requested ID.
  • argv: A pointer to an array of size numfields * 2 containing the fields and values.
  • numfields: The number of field-value pairs in argv.

Returns VALKEYMODULE_OK if an entry has been added. On failure, VALKEYMODULE_ERR is returned and errno is set as follows:

  • EINVAL if called with invalid arguments
  • ENOTSUP if the key refers to a value of a type other than stream
  • EBADF if the key was not opened for writing
  • EDOM if the given ID was 0-0 or not greater than all other IDs in the stream (only if the AUTOID flag is unset)
  • EFBIG if the stream has reached the last possible ID
  • ERANGE if 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:

  • EINVAL if called with invalid arguments
  • ENOTSUP if the key refers to a value of a type other than stream or if the key is empty
  • EBADF if the key was not opened for writing or if a stream iterator is associated with the key
  • ENOENT if 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 using ValkeyModule_OpenKey().
  • flags:
    • VALKEYMODULE_STREAM_ITERATOR_EXCLUSIVE: Don't include start and end in 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. Use NULL for the beginning of the stream.
  • end: The upper bound of the range. Use NULL for the end of the stream.

Returns VALKEYMODULE_OK on success. On failure, VALKEYMODULE_ERR is returned and errno is set as follows:

  • EINVAL if called with invalid arguments
  • ENOTSUP if the key refers to a value of a type other than stream or if the key is empty
  • EBADF if the key was not opened for writing or if a stream iterator is already associated with the key
  • EDOM if start or end is 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:

  • EINVAL if called with a NULL key
  • ENOTSUP if the key refers to a value of a type other than stream or if the key is empty
  • EBADF if 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 using ValkeyModule_StreamIteratorStart().
  • id: The stream ID returned. NULL if you don't care.
  • numfields: The number of fields in the found stream entry. NULL if 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:

  • EINVAL if called with a NULL key
  • ENOTSUP if the key refers to a value of a type other than stream or if the key is empty
  • EBADF if no stream iterator is associated with the key
  • ENOENT if 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:

  • EINVAL if called with a NULL key
  • ENOTSUP if the key refers to a value of a type other than stream or if the key is empty
  • EBADF if no stream iterator is associated with the key
  • ENOENT if 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:

  • EINVAL if key is NULL
  • ENOTSUP if the key is empty or is of another type than stream
  • EBADF if the key is not opened for writing, if no iterator has been started
  • ENOENT if 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, like XTRIM with ~.
  • 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:

  • EINVAL if called with invalid arguments
  • ENOTSUP if the key is empty or of a type other than stream
  • EBADF if 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, like XTRIM with ~.
  • 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:

  • EINVAL if called with invalid arguments
  • ENOTSUP if the key is empty or of a type other than stream
  • EBADF if 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_UNKNOWN
  • VALKEYMODULE_REPLY_STRING
  • VALKEYMODULE_REPLY_ERROR
  • VALKEYMODULE_REPLY_INTEGER
  • VALKEYMODULE_REPLY_ARRAY
  • VALKEYMODULE_REPLY_NULL
  • VALKEYMODULE_REPLY_MAP
  • VALKEYMODULE_REPLY_SET
  • VALKEYMODULE_REPLY_BOOL
  • VALKEYMODULE_REPLY_DOUBLE
  • VALKEYMODULE_REPLY_BIG_NUMBER
  • VALKEYMODULE_REPLY_VERBATIM_STRING
  • VALKEYMODULE_REPLY_ATTRIBUTE
  • VALKEYMODULE_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_OK on success.
  • VALKEYMODULE_ERR if idx out 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_OK on success.
  • VALKEYMODULE_ERR if idx out 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, 3 and R, 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 a long long integer.
    • s โ€” the argument is a ValkeyModuleString.
    • v โ€” the argument(s) is a vector of ValkeyModuleString.
    • ! โ€” 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., HGETALL returns 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 given ValkeyModuleCtx. This will probably used when you want to pass the reply directly to the client.
Heads up bestie: A and R both 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 via ValkeyModule_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 via ValkeyModule_SetContextUser), then ValkeyModule_Call will 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 the deny-script flag, like SHUTDOWN ๐Ÿ˜ณ). Also, in script mode, write commands are not allowed if there aren't enough good replicas (as configured with min-replicas-to-write) or when the server can't persist to disk.
  • W โ€” do not allow running any write command (any command flagged with the write flag) ๐Ÿ™…โ€โ™€๏ธ.
  • M โ€” do not allow deny-oom flagged commands when you're over the memory limit ๐Ÿง ๐Ÿ’ฅ.
  • E โ€” return errors as a ValkeyModuleCallReply. If there's an error before invoking the command, the error comes back through the errno mechanism. This flag is what lets you get the error also as an error CallReply with the relevant error message, periodt.
  • D' โ€” a โ€œDry Runโ€ mode ๐Ÿƒโ€โ™€๏ธ๐Ÿ’จ. It returns before executing the underlying call(). If everything succeeded, it returns a NULL; otherwise it returns a CallReply object denoting the error, exactly as if it had been called with the E' code.
  • K' โ€” allow running blocking commands ๐Ÿ›‘. If it's enabled and the command gets blocked, a special VALKEYMODULE_REPLY_PROMISE gets 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, using ValkeyModule_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 use ValkeyModule_CallReplyPromiseAbort to abort the command invocation if it wasn't yet finished (see the ValkeyModule_CallReplyPromiseAbort documentation 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 blocking ValkeyModule_Call caused the module to also block some real client (using ValkeyModule_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
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 or deny-oom command 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 charset A-Z a-z 9-0, plus the two -_ characters. A good idea is to use, for example <typename>-<vendor>. For example, tree-AntZ may 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 the name matches, the RDB loading gets dispatched to the type callbacks whatever encver is used, however the module can understand if the encoding it must load is of an older version of the module. For example, the module tree-AntZ initially used encver=0. Later, after an upgrade, it started to serialize data in a different format and to register the type with encver=1. However, this module may still load old data produced by an older version if the rdb_load callback is able to check the encver value and act accordingly. The encver must be a positive value between 0 and 1023.
  • typemethods_ptr is a pointer to a ValkeyModuleTypeMethods structure 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 for DEBUG 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. The when argument is either VALKEYMODULE_AUX_BEFORE_RDB or VALKEYMODULE_AUX_AFTER_RDB.
  • aux_load: a callback function pointer that loads out-of-keyspace data from RDB files. Similar to aux_save, it returns VALKEYMODULE_OK on success, and ERR otherwise.
  • 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 returns 0, 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 on FLUSHALL/FLUSHDB (both sync and async), and the module can use the ValkeyModuleEvent_FlushDB to 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. A NULL return 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 relevant ValkeyModule_Defrag*() functions to defragment pointers or complex types. The module should keep iterating as long as ValkeyModule_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() and ValkeyModule_DefragCursorGet() can be used to track this work across different calls. Normally, the defrag mechanism invokes the callback without a time limit, so ValkeyModule_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 the free_effort callback and the active-defrag-max-scan-fields config directive. NOTE: the value is passed as a void** and the function is expected to update the pointer if the top-level value pointer is defragmented and consequently changes. ๐Ÿ‘€
  • mem_usage2 โ€” same vibe as mem_usage, but it also hands you the ValkeyModuleKeyOptCtx parameter so you can get at meta information like key name and db id, plus the sample_size for size estimation (peep the MEMORY USAGE command). โœจ
  • free_effort2 โ€” same vibe as free_effort, but it also hands you the ValkeyModuleKeyOptCtx parameter so you can get at meta information like key name and db id. ๐Ÿฆ‹
  • unlink2 โ€” same vibe as unlink, but it also hands you the ValkeyModuleKeyOptCtx parameter so you can get at meta information like key name and db id. ๐ŸŒธ
  • copy2 โ€” same vibe as copy, but it also hands you the ValkeyModuleKeyOptCtx parameter so you can get at meta information like key names and db ids. ๐Ÿ’–
  • aux_save2 โ€” same vibe as aux_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 the VALKEYMODULE_OPTIONS_HANDLE_IO_ERRORS flag and make sure the de-serialization code properly checks and handles IO errors (freeing allocated buffers and returning a NULL).

If this is NOT done, 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:

  1. Authenticate โ€” Use the ValkeyModule_AuthenticateClient* API and return VALKEYMODULE_AUTH_HANDLED. This will immediately end the auth chain as successful and add the OK reply. โœ…
  2. Deny Authentication โ€” Return VALKEYMODULE_AUTH_HANDLED without authenticating or blocking the client. Optionally, err can be set to a custom error message, and err will be automatically freed by the server. This will immediately end the auth chain as unsuccessful and add the ERR reply. โŒ
  3. Block a client on authentication โ€” Use the ValkeyModule_BlockClientOnAuth API and return VALKEYMODULE_AUTH_HANDLED. Here, the client will be blocked until the ValkeyModule_UnblockClient API is used, which will trigger the auth reply callback (provided through the ValkeyModule_BlockClientOnAuth). In this reply callback, the Module should authenticate, deny, or skip handling authentication. โณ
  4. Skip handling Authentication โ€” Return VALKEYMODULE_AUTH_NOT_HANDLED without 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 the AUTH or HELLO command will not be tracked in the INFO command 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 successful ValkeyModule_UnblockClient() call in order to reply to the client and unblock it. ๐Ÿซถ
  • timeout_callback: called when the timeout is reached or if CLIENT UNBLOCK is invoked, in order to send an error to the client. ๐Ÿ˜ญ
  • free_privdata: called in order to free the private data that is passed by the ValkeyModule_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:

  1. If the client is executing a script.
  2. If the client is executing a MULTI block.
  3. If the client is a temporary module client.
  4. 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:

  1. 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?

  1. If you block on a key of a type that has blocking operations associated, like a list, a sorted set, a stream, and so forth, the client may be unblocked once the relevant key is targeted by an operation that normally unblocks the native blocking operations for that type. So if we block on a list key, an RPUSH command may unblock our client and so forth.
  2. If you are implementing your native data type, or if you want to add new unblocking conditions in addition to "1", you can call the modules API 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 circumstances ValkeyModule_UnblockClient should 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 call ValkeyModule_UnblockClient it 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 (like XREADGROUP) ๐Ÿ‘‘

๐ŸŒˆ 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:

  • EINVAL if bc is NULL.
  • ENOTSUP if bc contains 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 API ValkeyModule_BlockClientOnKeys(), the privdata argument 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 (The privdata provided in ValkeyModule_BlockClientOnKeys() is accessible from the timeout callback via ValkeyModule_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:

  1. It is not safe to call Reply* family functions here, it is also useless since the client is gone.
  2. This callback is not called if the client disconnects because of a timeout. In such a case, the client is unblocked automatically and the timeout callback is called.

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 (bc is NULL), consider using ValkeyModule_GetDetachedThreadSafeContext which 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 like DEL, EXPIRE, RENAME
  • VALKEYMODULE_NOTIFY_STRING: String events
  • VALKEYMODULE_NOTIFY_LIST: List events
  • VALKEYMODULE_NOTIFY_SET: Set events
  • VALKEYMODULE_NOTIFY_HASH: Hash events
  • VALKEYMODULE_NOTIFY_ZSET: Sorted Set events
  • VALKEYMODULE_NOTIFY_EXPIRED: Expiration events
  • VALKEYMODULE_NOTIFY_EVICTED: Eviction events
  • VALKEYMODULE_NOTIFY_STREAM: Stream events
  • VALKEYMODULE_NOTIFY_MODULE: Module types events
  • VALKEYMODULE_NOTIFY_KEYMISS: Key-miss events Notice, key-miss event is the only type of event that is fired from within a read command. Performing ValkeyModule_Call with 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 (Excluding VALKEYMODULE_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, use ValkeyModule_CreateStringFromString instead.

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:

  1. It is safe to perform any write operation. โœจ
  2. 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 node
  • VALKEYMODULE_NODE_PRIMARY: The node is a primary
  • VALKEYMODULE_NODE_REPLICA: The node is a replica
  • VALKEYMODULE_NODE_PFAIL: We see the node as failing
  • VALKEYMODULE_NODE_FAIL: The cluster agrees the node is failing
  • VALKEYMODULE_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_FAILOVER
  • CLUSTER_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 every period. ๐ŸŽ€
  • If you call it at the end of callback, there'll be period milliseconds 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. ๐ŸŽ€

  • mask has to be one of these values:
    • VALKEYMODULE_EVENTLOOP_READABLE
    • VALKEYMODULE_EVENTLOOP_WRITABLE
    • VALKEYMODULE_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: fd is negative or higher than the maxclients server config. ๐Ÿ’€
  • EINVAL: callback is NULL or the mask value 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 ๐Ÿ‘‹

  • mask has to be one of these values:
    • VALKEYMODULE_EVENTLOOP_READABLE
    • VALKEYMODULE_EVENTLOOP_WRITABLE
    • VALKEYMODULE_EVENTLOOP_READABLE | VALKEYMODULE_EVENTLOOP_WRITABLE

On success VALKEYMODULE_OK is returned, otherwise VALKEYMODULE_ERR is returned and errno gets set to:

  • ERANGE: fd is negative or higher than the maxclients server config.
  • EINVAL: the mask value 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 ACL command. ๐Ÿ™ˆ
  • 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:

  1. Use a NULL context if you plan to retain a reference to this dictionary that will survive past the module callback where you created it.
  2. Use a NULL context if no context is available at the time you're creating the dictionary (obviously...).
  3. However, use the current callback context as the ctx argument 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 pass NULL with a length of 0. 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:

  1. Invocation by a client.
  2. Invocation through ValkeyModule_Call() by any module.
  3. Invocation through Lua server.call().
  4. 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:

  1. Register a MODULE.SET command which implements an extended version of the SET command.
  2. Register a command filter which detects invocation of SET on a specific pattern of keys. Once detected, the filter will replace the first argument from SET to MODULE.SET.
  3. When filter execution is complete, 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 be NULL, and then you should (and can) use ValkeyModule_OpenKey() (and CloseKey too). When it is provided, it's owned by the caller and will be freed when the callback returns.
  • privdata โ€” the user data provided to ValkeyModule_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, or NULL for the set type, owned by the caller, needs to be retained if used after this function.
  • privdata โ€” the user data provided to ValkeyModule_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_PRIMARY
  • VALKEYMODULE_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_START
  • VALKEYMODULE_SUBEVENT_PERSISTENCE_AOF_START
  • VALKEYMODULE_SUBEVENT_PERSISTENCE_SYNC_RDB_START
  • VALKEYMODULE_SUBEVENT_PERSISTENCE_SYNC_AOF_START
  • VALKEYMODULE_SUBEVENT_PERSISTENCE_ENDED
  • VALKEYMODULE_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_START
  • VALKEYMODULE_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_START
  • VALKEYMODULE_SUBEVENT_LOADING_AOF_START
  • VALKEYMODULE_SUBEVENT_LOADING_REPL_START
  • VALKEYMODULE_SUBEVENT_LOADING_ENDED
  • VALKEYMODULE_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_CONNECTED
  • VALKEYMODULE_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_ONLINE
  • VALKEYMODULE_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_UP
  • VALKEYMODULE_SUBEVENT_PRIMARY_LINK_DOWN
  • ValkeyModuleEvent_ModuleChange ๐ŸŽ

Called when a new module is loaded or one is unloaded. Sub events available:

  • VALKEYMODULE_SUBEVENT_MODULE_LOADED
  • VALKEYMODULE_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_RDB
  • VALKEYMODULE_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_CREATE
  • VALKEYMODULE_SUBEVENT_REPL_BACKUP_RESTORE
  • VALKEYMODULE_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_STARTED
  • VALKEYMODULE_SUBEVENT_REPL_ASYNC_LOAD_ABORTED
  • VALKEYMODULE_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_BORN
  • VALKEYMODULE_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_SLEEP
  • VALKEYMODULE_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_DELETED
  • VALKEYMODULE_SUBEVENT_KEY_EXPIRED
  • VALKEYMODULE_SUBEVENT_KEY_EVICTED
  • VALKEYMODULE_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_STARTED
  • VALKEYMODULE_SUBEVENT_ATOMIC_SLOT_MIGRATION_EXPORT_STARTED
  • VALKEYMODULE_SUBEVENT_ATOMIC_SLOT_MIGRATION_IMPORT_ABORTED
  • VALKEYMODULE_SUBEVENT_ATOMIC_SLOT_MIGRATION_EXPORT_ABORTED
  • VALKEYMODULE_SUBEVENT_ATOMIC_SLOT_MIGRATION_IMPORT_COMPLETED
  • VALKEYMODULE_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 from CONFIG GET with pattern matching.
  • VALKEYMODULE_CONFIG_PROTECTED: This config will be only be modifiable based off the value of enable-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:

  1. On string config sets that the string passed to the set callback will be freed after execution and the module must retain it.
  2. 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 of ValkeyModule_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:

  1. Key is not opened for writing.
  2. Key is not a module data type key.
  3. Key is a module datatype other than `mt'.

If old_value is non-NULL, the old value is returned by reference. ๐Ÿซด


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 call ValkeyModule_Free() to free it. Same deal for the out_flags pointer, 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 with ValkeyModule_RetainString or ValkeyModule_HoldString may 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_ACLAddLogEntry
  • ValkeyModule_ACLAddLogEntryByUserName
  • ValkeyModule_ACLCheckChannelPermissions
  • ValkeyModule_ACLCheckCommandPermissions
  • ValkeyModule_ACLCheckKeyPermissions
  • ValkeyModule_AbortBlock
  • ValkeyModule_AddACLCategory
  • ValkeyModule_AddPostNotificationJob
  • ValkeyModule_Alloc
  • ValkeyModule_AuthenticateClientWithACLUser
  • ValkeyModule_AuthenticateClientWithUser
  • ValkeyModule_AutoMemory
  • ValkeyModule_AvoidReplicaTraffic
  • ValkeyModule_BlockClient
  • ValkeyModule_BlockClientGetPrivateData
  • ValkeyModule_BlockClientOnAuth
  • ValkeyModule_BlockClientOnKeys
  • ValkeyModule_BlockClientOnKeysWithFlags
  • ValkeyModule_BlockClientSetPrivateData
  • ValkeyModule_BlockedClientDisconnected
  • ValkeyModule_BlockedClientMeasureTimeEnd
  • ValkeyModule_BlockedClientMeasureTimeStart
  • ValkeyModule_CachedMicroseconds
  • ValkeyModule_Call
  • ValkeyModule_CallReplyArrayElement
  • ValkeyModule_CallReplyAttribute
  • ValkeyModule_CallReplyAttributeElement
  • ValkeyModule_CallReplyBigNumber
  • ValkeyModule_CallReplyBool
  • ValkeyModule_CallReplyDouble
  • ValkeyModule_CallReplyInteger
  • ValkeyModule_CallReplyLength
  • ValkeyModule_CallReplyMapElement
  • ValkeyModule_CallReplyPromiseAbort
  • ValkeyModule_CallReplyPromiseSetUnblockHandler
  • ValkeyModule_CallReplyProto
  • ValkeyModule_CallReplySetElement
  • ValkeyModule_CallReplyStringPtr
  • ValkeyModule_CallReplyType
  • ValkeyModule_CallReplyVerbatim
  • ValkeyModule_Calloc
  • ValkeyModule_ChannelAtPosWithFlags
  • ValkeyModule_CloseKey
  • ValkeyModule_ClusterCanonicalKeyNameInSlot
  • ValkeyModule_ClusterKeySlot
  • ValkeyModule_CommandFilterArgDelete
  • ValkeyModule_CommandFilterArgGet
  • ValkeyModule_CommandFilterArgInsert
  • ValkeyModule_CommandFilterArgReplace
  • ValkeyModule_CommandFilterArgsCount
  • ValkeyModule_CommandFilterGetClientId
  • ValkeyModule_CreateCommand
  • ValkeyModule_CreateDataType
  • ValkeyModule_CreateDict
  • ValkeyModule_CreateModuleUser
  • ValkeyModule_CreateString
  • ValkeyModule_CreateStringFromCallReply
  • ValkeyModule_CreateStringFromDouble
  • ValkeyModule_CreateStringFromLongDouble
  • ValkeyModule_CreateStringFromLongLong
  • ValkeyModule_CreateStringFromStreamID
  • ValkeyModule_CreateStringFromString
  • ValkeyModule_CreateStringFromULongLong
  • ValkeyModule_CreateStringPrintf
  • ValkeyModule_CreateSubcommand
  • ValkeyModule_CreateTimer
  • ValkeyModule_DbSize
  • ValkeyModule_DeauthenticateAndCloseClient
  • ValkeyModule_DefragAlloc
  • ValkeyModule_DefragCursorGet
  • ValkeyModule_DefragCursorSet
  • ValkeyModule_DefragShouldStop
  • ValkeyModule_DefragValkeyModuleString
  • ValkeyModule_DeleteKey
  • ValkeyModule_DictCompare
  • ValkeyModule_DictCompareC
  • ValkeyModule_DictDel
  • ValkeyModule_DictDelC
  • ValkeyModule_DictGet
  • ValkeyModule_DictGetC
  • ValkeyModule_DictIteratorReseek
  • ValkeyModule_DictIteratorReseekC
  • ValkeyModule_DictIteratorStart
  • ValkeyModule_DictIteratorStartC
  • ValkeyModule_DictIteratorStop
  • ValkeyModule_DictNext
  • ValkeyModule_DictNextC
  • ValkeyModule_DictPrev
  • ValkeyModule_DictPrevC
  • ValkeyModule_DictReplace
  • ValkeyModule_DictReplaceC
  • ValkeyModule_DictSet
  • ValkeyModule_DictSetC
  • ValkeyModule_DictSize
  • ValkeyModule_DigestAddLongLong
  • ValkeyModule_DigestAddStringBuffer
  • ValkeyModule_DigestEndSequence
  • ValkeyModule_EmitAOF
  • ValkeyModule_EventLoopAdd
  • ValkeyModule_EventLoopAddOneShot
  • ValkeyModule_EventLoopDel
  • ValkeyModule_ExitFromChild
  • ValkeyModule_ExportSharedAPI
  • ValkeyModule_Fork
  • ValkeyModule_Free
  • ValkeyModule_FreeCallReply
  • ValkeyModule_FreeClusterNodesList
  • ValkeyModule_FreeDict
  • ValkeyModule_FreeModuleUser
  • ValkeyModule_FreeServerInfo
  • ValkeyModule_FreeString
  • ValkeyModule_FreeThreadSafeContext
  • ValkeyModule_GetAbsExpire
  • ValkeyModule_GetBlockedClientHandle
  • ValkeyModule_GetBlockedClientPrivateData
  • ValkeyModule_GetBlockedClientReadyKey
  • ValkeyModule_GetClientCertificate
  • ValkeyModule_GetClientId
  • ValkeyModule_GetClientInfoById
  • ValkeyModule_GetClientNameById
  • ValkeyModule_GetClientUserNameById
  • ValkeyModule_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_GetClusterNodeInfoForClient
  • ValkeyModule_GetClusterNodesList
  • ValkeyModule_GetClusterSize
  • ValkeyModule_GetCommand
  • ValkeyModule_GetCommandKeys
  • ValkeyModule_GetCommandKeysWithFlags
  • ValkeyModule_GetContextFlags
  • ValkeyModule_GetContextFlagsAll
  • ValkeyModule_GetCurrentCommandName
  • ValkeyModule_GetCurrentUserName
  • ValkeyModule_GetDbIdFromDefragCtx
  • ValkeyModule_GetDbIdFromDigest
  • ValkeyModule_GetDbIdFromIO
  • ValkeyModule_GetDbIdFromModuleKey
  • ValkeyModule_GetDbIdFromOptCtx
  • ValkeyModule_GetDetachedThreadSafeContext
  • ValkeyModule_GetExpire
  • ValkeyModule_GetFunctionExecutionState
  • ValkeyModule_GetKeyNameFromDefragCtx
  • ValkeyModule_GetKeyNameFromDigest
  • ValkeyModule_GetKeyNameFromIO
  • ValkeyModule_GetKeyNameFromModuleKey
  • ValkeyModule_GetKeyNameFromOptCtx
  • ValkeyModule_GetKeyspaceNotificationFlagsAll
  • ValkeyModule_GetLFU
  • ValkeyModule_GetLRU
  • ValkeyModule_GetModuleOptionsAll
  • ValkeyModule_GetModuleUserACLString
  • ValkeyModule_GetModuleUserFromUserName
  • ValkeyModule_GetMyClusterID
  • ValkeyModule_GetNotifyKeyspaceEvents
  • ValkeyModule_GetOpenKeyModesAll
  • ValkeyModule_GetRandomBytes
  • ValkeyModule_GetRandomHexChars
  • ValkeyModule_GetSelectedDb
  • ValkeyModule_GetServerInfo
  • ValkeyModule_GetServerVersion
  • ValkeyModule_GetSharedAPI
  • ValkeyModule_GetThreadSafeContext
  • ValkeyModule_GetTimerInfo
  • ValkeyModule_GetToDbIdFromOptCtx
  • ValkeyModule_GetToKeyNameFromOptCtx
  • ValkeyModule_GetTypeMethodVersion
  • ValkeyModule_GetUsedMemoryRatio
  • ValkeyModule_HashGet
  • ValkeyModule_HashSet
  • ValkeyModule_HoldString
  • ValkeyModule_InfoAddFieldCString
  • ValkeyModule_InfoAddFieldDouble
  • ValkeyModule_InfoAddFieldLongLong
  • ValkeyModule_InfoAddFieldString
  • ValkeyModule_InfoAddFieldULongLong
  • ValkeyModule_InfoAddSection
  • ValkeyModule_InfoBeginDictField
  • ValkeyModule_InfoEndDictField
  • ValkeyModule_IsBlockedReplyRequest
  • ValkeyModule_IsBlockedTimeoutRequest
  • ValkeyModule_IsChannelsPositionRequest
  • ValkeyModule_IsIOError
  • ValkeyModule_IsKeysPositionRequest
  • ValkeyModule_IsModuleNameBusy
  • ValkeyModule_IsSubEventSupported
  • ValkeyModule_KeyAtPos
  • ValkeyModule_KeyAtPosWithFlags
  • ValkeyModule_KeyExists
  • ValkeyModule_KeyType
  • ValkeyModule_KillForkChild
  • ValkeyModule_LatencyAddSample
  • ValkeyModule_ListDelete
  • ValkeyModule_ListGet
  • ValkeyModule_ListInsert
  • ValkeyModule_ListPop
  • ValkeyModule_ListPush
  • ValkeyModule_ListSet
  • ValkeyModule_LoadConfigs
  • ValkeyModule_LoadDataTypeFromString
  • ValkeyModule_LoadDataTypeFromStringEncver
  • ValkeyModule_LoadDouble
  • ValkeyModule_LoadFloat
  • ValkeyModule_LoadLongDouble
  • ValkeyModule_LoadSigned
  • ValkeyModule_LoadString
  • ValkeyModule_LoadStringBuffer
  • ValkeyModule_LoadUnsigned
  • ValkeyModule_Log
  • ValkeyModule_LogIOError
  • ValkeyModule_MallocSize
  • ValkeyModule_MallocSizeDict
  • ValkeyModule_MallocSizeString
  • ValkeyModule_MallocUsableSize
  • ValkeyModule_Microseconds
  • ValkeyModule_Milliseconds
  • ValkeyModule_ModuleTypeGetType
  • ValkeyModule_ModuleTypeGetValue
  • ValkeyModule_ModuleTypeReplaceValue
  • ValkeyModule_ModuleTypeSetValue
  • ValkeyModule_MonotonicMicroseconds
  • ValkeyModule_MustObeyClient
  • ValkeyModule_NotifyKeyspaceEvent
  • ValkeyModule_OpenKey
  • ValkeyModule_PoolAlloc
  • ValkeyModule_PublishMessage
  • ValkeyModule_PublishMessageShard
  • ValkeyModule_RandomKey
  • ValkeyModule_RdbLoad
  • ValkeyModule_RdbSave
  • ValkeyModule_RdbStreamCreateFromFile
  • ValkeyModule_RdbStreamFree
  • ValkeyModule_Realloc
  • ValkeyModule_RedactClientCommandArgument
  • ValkeyModule_RegisterAuthCallback
  • ValkeyModule_RegisterBoolConfig
  • ValkeyModule_RegisterClusterMessageReceiver
  • ValkeyModule_RegisterCommandFilter
  • ValkeyModule_RegisterDefragFunc
  • ValkeyModule_RegisterEnumConfig
  • ValkeyModule_RegisterInfoFunc
  • ValkeyModule_RegisterNumericConfig
  • ValkeyModule_RegisterScriptingEngine
  • ValkeyModule_RegisterStringConfig
  • ValkeyModule_Replicate
  • ValkeyModule_ReplicateVerbatim
  • ValkeyModule_ReplySetArrayLength
  • ValkeyModule_ReplySetAttributeLength
  • ValkeyModule_ReplySetMapLength
  • ValkeyModule_ReplySetSetLength
  • ValkeyModule_ReplyWithArray
  • ValkeyModule_ReplyWithAttribute
  • ValkeyModule_ReplyWithBigNumber
  • ValkeyModule_ReplyWithBool
  • ValkeyModule_ReplyWithCString
  • ValkeyModule_ReplyWithCallReply
  • ValkeyModule_ReplyWithDouble
  • ValkeyModule_ReplyWithEmptyArray
  • ValkeyModule_ReplyWithEmptyString
  • ValkeyModule_ReplyWithError
  • ValkeyModule_ReplyWithErrorFormat
  • ValkeyModule_ReplyWithLongDouble
  • ValkeyModule_ReplyWithLongLong
  • ValkeyModule_ReplyWithMap
  • ValkeyModule_ReplyWithNull
  • ValkeyModule_ReplyWithNullArray
  • ValkeyModule_ReplyWithSet
  • ValkeyModule_ReplyWithSimpleString
  • ValkeyModule_ReplyWithString
  • ValkeyModule_ReplyWithStringBuffer
  • ValkeyModule_ReplyWithVerbatimString
  • ValkeyModule_ReplyWithVerbatimStringType
  • ValkeyModule_ResetDataset
  • ValkeyModule_RetainString
  • ValkeyModule_SaveDataTypeToString
  • ValkeyModule_SaveDouble
  • ValkeyModule_SaveFloat
  • ValkeyModule_SaveLongDouble
  • ValkeyModule_SaveSigned
  • ValkeyModule_SaveString
  • ValkeyModule_SaveStringBuffer
  • ValkeyModule_SaveUnsigned
  • ValkeyModule_Scan
  • ValkeyModule_ScanCursorCreate
  • ValkeyModule_ScanCursorDestroy
  • ValkeyModule_ScanCursorRestart
  • ValkeyModule_ScanKey
  • ValkeyModule_SelectDb
  • ValkeyModule_SendChildHeartbeat
  • ValkeyModule_SendClusterMessage
  • ValkeyModule_ServerInfoGetField
  • ValkeyModule_ServerInfoGetFieldC
  • ValkeyModule_ServerInfoGetFieldDouble
  • ValkeyModule_ServerInfoGetFieldSigned
  • ValkeyModule_ServerInfoGetFieldUnsigned
  • ValkeyModule_SetAbsExpire
  • ValkeyModule_SetClientNameById
  • ValkeyModule_SetClusterFlags
  • ValkeyModule_SetCommandACLCategories
  • ValkeyModule_SetCommandInfo
  • ValkeyModule_SetContextUser
  • ValkeyModule_SetDisconnectCallback
  • ValkeyModule_SetExpire
  • ValkeyModule_SetLFU
  • ValkeyModule_SetLRU
  • ValkeyModule_SetModuleOptions
  • ValkeyModule_SetModuleUserACL
  • ValkeyModule_SetModuleUserACLString
  • ValkeyModule_SignalKeyAsReady
  • ValkeyModule_SignalModifiedKey
  • ValkeyModule_StopTimer
  • ValkeyModule_Strdup
  • ValkeyModule_StreamAdd
  • ValkeyModule_StreamDelete
  • ValkeyModule_StreamIteratorDelete
  • ValkeyModule_StreamIteratorNextField
  • ValkeyModule_StreamIteratorNextID
  • ValkeyModule_StreamIteratorStart
  • ValkeyModule_StreamIteratorStop
  • ValkeyModule_StreamTrimByID
  • ValkeyModule_StreamTrimByLength
  • ValkeyModule_StringAppendBuffer
  • ValkeyModule_StringCompare
  • ValkeyModule_StringDMA
  • ValkeyModule_StringPtrLen
  • ValkeyModule_StringSet
  • ValkeyModule_StringToDouble
  • ValkeyModule_StringToLongDouble
  • ValkeyModule_StringToLongLong
  • ValkeyModule_StringToStreamID
  • ValkeyModule_StringToULongLong
  • ValkeyModule_StringTruncate
  • ValkeyModule_SubscribeToKeyspaceEvents
  • ValkeyModule_SubscribeToServerEvent
  • ValkeyModule_ThreadSafeContextLock
  • ValkeyModule_ThreadSafeContextTryLock
  • ValkeyModule_ThreadSafeContextUnlock
  • ValkeyModule_TrimStringAllocation
  • ValkeyModule_TryAlloc
  • ValkeyModule_TryCalloc
  • ValkeyModule_TryRealloc
  • ValkeyModule_UnblockClient
  • ValkeyModule_UnlinkKey
  • ValkeyModule_UnregisterCommandFilter
  • ValkeyModule_UnregisterScriptingEngine
  • ValkeyModule_UpdateRuntimeArgs
  • ValkeyModule_ValueLength
  • ValkeyModule_WrongArity
  • ValkeyModule_Yield
  • ValkeyModule_ZsetAdd
  • ValkeyModule_ZsetFirstInLexRange
  • ValkeyModule_ZsetFirstInScoreRange
  • ValkeyModule_ZsetIncrby
  • ValkeyModule_ZsetLastInLexRange
  • ValkeyModule_ZsetLastInScoreRange
  • ValkeyModule_ZsetRangeCurrentElement
  • ValkeyModule_ZsetRangeEndReached
  • ValkeyModule_ZsetRangeNext
  • ValkeyModule_ZsetRangePrev
  • ValkeyModule_ZsetRangeStop
  • ValkeyModule_ZsetRem
  • ValkeyModule_ZsetScore
  • ValkeyModule__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 Valkey 9.0.0, dated 2025-10-21, and it lives in section 7 of the manual as VALKEY-MODULES-API-REF(7). now go build something iconic ๐Ÿซถโœจ
๐Ÿ“ฆ where this page comes from