Movatterモバイル変換


[0]ホーム

URL:


Products
Resources
DocsPricing
LoginBook a meetingTry Redis

Redis modules API

Introduction to writing Redis modules

The modules documentation is composed of the following pages:

  • Introduction to Redis modules (this file). An overview about Redis Modules system and API. It's a good idea to start your reading here.
  • Implementing native data types covers the implementation of native data types into modules.
  • Blocking operations shows how to write blocking commands that will not reply immediately, but will block the client, without blocking the Redis server, and will provide a reply whenever will be possible.
  • Redis modules API reference is generated from module.c top comments of RedisModule functions. It is a good reference in order to understand how each function works.

Redis modules make it possible to extend Redis functionality using externalmodules, rapidly implementing new Redis commands with featuressimilar to what can be done inside the core itself.

Redis modules are dynamic libraries that can be loaded into Redis atstartup, or using theMODULE LOAD command. Redis exports a C API, in theform of a single C header file calledredismodule.h. Modules are meantto be written in C, however it will be possible to use C++ or other languagesthat have C binding functionalities.

Modules are designed in order to be loaded into different versions of Redis,so a given module does not need to be designed, or recompiled, in order torun with a specific version of Redis. For this reason, the module willregister to the Redis core using a specific API version. The current APIversion is "1".

This document is about an alpha version of Redis modules. API, functionalitiesand other details may change in the future.

Loading modules

In order to test the module you are developing, you can load the moduleusing the followingredis.conf configuration directive:

loadmodule /path/to/mymodule.so

It is also possible to load a module at runtime using the following command:

MODULE LOAD /path/to/mymodule.so

In order to list all loaded modules, use:

MODULE LIST

Finally, you can unload (and later reload if you wish) a module using thefollowing command:

MODULE UNLOAD mymodule

Note thatmymodule above is not the filename without the.so suffix, butinstead, the name the module used to register itself into the Redis core.The name can be obtained usingMODULE LIST. However it is good practicethat the filename of the dynamic library is the same as the name the moduleuses to register itself into the Redis core.

The simplest module you can write

In order to show the different parts of a module, here we'll show a verysimple module that implements a command that outputs a random number.

#include "redismodule.h"#include <stdlib.h>int HelloworldRand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {    RedisModule_ReplyWithLongLong(ctx,rand());    return REDISMODULE_OK;}int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {    if (RedisModule_Init(ctx,"helloworld",1,REDISMODULE_APIVER_1)        == REDISMODULE_ERR) return REDISMODULE_ERR;    if (RedisModule_CreateCommand(ctx,"helloworld.rand",        HelloworldRand_RedisCommand, "fast random",        0, 0, 0) == REDISMODULE_ERR)        return REDISMODULE_ERR;    return REDISMODULE_OK;}

The example module has two functions. One implements a command calledHELLOWORLD.RAND. This function is specific of that module. However theother function calledRedisModule_OnLoad() must be present in eachRedis module. It is the entry point for the module to be initialized,register its commands, and potentially other private data structuresit uses.

Note that it is a good idea for modules to call commands with thename of the module followed by a dot, and finally the command name,like in the case ofHELLOWORLD.RAND. This way it is less likely tohave collisions.

Note that if different modules have colliding commands, they'll not beable to work in Redis at the same time, since the functionRedisModule_CreateCommand will fail in one of the modules, so the moduleloading will abort returning an error condition.

Module initialization

The above example shows the usage of the functionRedisModule_Init().It should be the first function called by the moduleOnLoad function.The following is the function prototype:

int RedisModule_Init(RedisModuleCtx *ctx, const char *modulename,                     int module_version, int api_version);

TheInit function announces the Redis core that the module has a givenname, its version (that is reported byMODULE LIST), and that is willingto use a specific version of the API.

If the API version is wrong, the name is already taken, or there are othersimilar errors, the function will returnREDISMODULE_ERR, and the moduleOnLoad function should return ASAP with an error.

Before theInit function is called, no other API function can be called,otherwise the module will segfault and the Redis instance will crash.

The second function called,RedisModule_CreateCommand, is used in orderto register commands into the Redis core. The following is the prototype:

int RedisModule_CreateCommand(RedisModuleCtx *ctx, const char *name,                              RedisModuleCmdFunc cmdfunc, const char *strflags,                              int firstkey, int lastkey, int keystep);

As you can see, most Redis modules API calls all take as first argumentthecontext of the module, so that they have a reference to the modulecalling it, to the command and client executing a given command, and so forth.

To create a new command, the above function needs the context, the command'sname, a pointer to the function implementing the command, the command's flagsand the positions of key names in the command's arguments.

The function that implements the command must have the following prototype:

int mycommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);

The command function arguments are just the context, that will be passedto all the other API calls, the command argument vector, and total numberof arguments, as passed by the user.

As you can see, the arguments are provided as pointers to a specific datatype, theRedisModuleString. This is an opaque data type you have APIfunctions to access and use, direct access to its fields is never needed.

Zooming into the example command implementation, we can find another call:

int RedisModule_ReplyWithLongLong(RedisModuleCtx *ctx, long long integer);

This function returns an integer to the client that invoked the command,exactly like other Redis commands do, like for exampleINCR orSCARD.

Module cleanup

In most cases, there is no need for special cleanup.When a module is unloaded, Redis will automatically unregister commands andunsubscribe from notifications.However in the case where a module contains some persistent memory orconfiguration, a module may include an optionalRedisModule_OnUnloadfunction.If a module provides this function, it will be invoked during the module unloadprocess.The following is the function prototype:

int RedisModule_OnUnload(RedisModuleCtx *ctx);

TheOnUnload function may prevent module unloading by returningREDISMODULE_ERR.Otherwise,REDISMODULE_OK should be returned.

Setup and dependencies of a Redis module

Redis modules don't depend on Redis or some other library, nor theyneed to be compiled with a specificredismodule.h file. In orderto create a new module, just copy a recent version ofredismodule.hin your source tree, link all the libraries you want, and createa dynamic library having theRedisModule_OnLoad() function symbolexported.

The module will be able to load into different versions of Redis.

A module can be designed to support both newer and older Redis versions where certain API functions are not available in all versions.If an API function is not implemented in the currently running Redis version, the function pointer is set to NULL.This allows the module to check if a function exists before using it:

if (RedisModule_SetCommandInfo != NULL) {    RedisModule_SetCommandInfo(cmd, &info);}

In recent versions ofredismodule.h, a convenience macroRMAPI_FUNC_SUPPORTED(funcname) is defined.Using the macro or just comparing with NULL is a matter of personal preference.

Passing configuration parameters to Redis modules

When the module is loaded with theMODULE LOAD command, or using theloadmodule directive in theredis.conf file, the user is able to passconfiguration parameters to the module by adding arguments after the modulefile name:

loadmodule mymodule.so foo bar 1234

In the above example the stringsfoo,bar and1234 will be passedto the moduleOnLoad() function in theargv argument as an arrayof RedisModuleString pointers. The number of arguments passed is intoargc.

The way you can access those strings will be explained in the rest of thisdocument. Normally the module will store the module configuration parametersin somestatic global variable that can be accessed module wide, so thatthe configuration can change the behavior of different commands.

Working with RedisModuleString objects

The command argument vectorargv passed to module commands, and thereturn value of other module APIs functions, are of typeRedisModuleString.

Usually you directly pass module strings to other API calls, however sometimesyou may need to directly access the string object.

There are a few functions in order to work with string objects:

const char *RedisModule_StringPtrLen(RedisModuleString *string, size_t *len);

The above function accesses a string by returning its pointer and setting itslength inlen.You should never write to a string object pointer, as you can see from theconst pointer qualifier.

However, if you want, you can create new string objects using the followingAPI:

RedisModuleString *RedisModule_CreateString(RedisModuleCtx *ctx, const char *ptr, size_t len);

The string returned by the above command must be freed using a correspondingcall toRedisModule_FreeString():

void RedisModule_FreeString(RedisModuleString *str);

However if you want to avoid having to free strings, the automatic memorymanagement, covered later in this document, can be a good alternative, bydoing it for you.

Note that the strings provided via the argument vectorargv never needto be freed. You only need to free new strings you create, or new stringsreturned by other APIs, where it is specified that the returned string mustbe freed.

Creating strings from numbers or parsing strings as numbers

Creating a new string from an integer is a very common operation, so thereis a function to do this:

RedisModuleString *mystr = RedisModule_CreateStringFromLongLong(ctx,10);

Similarly in order to parse a string as a number:

long long myval;if (RedisModule_StringToLongLong(ctx,argv[1],&myval) == REDISMODULE_OK) {    /* Do something with 'myval' */}

Accessing Redis keys from modules

Most Redis modules, in order to be useful, have to interact with the Redisdata space (this is not always true, for example an ID generator maynever touch Redis keys). Redis modules have two different APIs in order toaccess the Redis data space, one is a low level API that provides veryfast access and a set of functions to manipulate Redis data structures.The other API is more high level, and allows to call Redis commands andfetch the result, similarly to how Lua scripts access Redis.

The high level API is also useful in order to access Redis functionalitiesthat are not available as APIs.

In general modules developers should prefer the low level API, because commandsimplemented using the low level API run at a speed comparable to the speedof native Redis commands. However there are definitely use cases for thehigher level API. For example often the bottleneck could be processing thedata and not accessing it.

Also note that sometimes using the low level API is not harder compared tothe higher level one.

Calling Redis commands

The high level API to access Redis is the sum of theRedisModule_Call()function, together with the functions needed in order to access thereply object returned byCall().

RedisModule_Call uses a special calling convention, with a format specifierthat is used to specify what kind of objects you are passing as argumentsto the function.

Redis commands are invoked just using a command name and a list of arguments.However when calling commands, the arguments may originate from differentkind of strings: null-terminated C strings, RedisModuleString objects asreceived from theargv parameter in the command implementation, binarysafe C buffers with a pointer and a length, and so forth.

For example if I want to callINCRBY using a first argument (the key)a string received in the argument vectorargv, which is an arrayof RedisModuleString object pointers, and a C string representing thenumber "10" as second argument (the increment), I'll use the followingfunction call:

RedisModuleCallReply *reply;reply = RedisModule_Call(ctx,"INCRBY","sc",argv[1],"10");

The first argument is the context, and the second is always a null terminatedC string with the command name. The third argument is the format specifierwhere each character corresponds to the type of the arguments that will follow.In the above case"sc" means a RedisModuleString object, and a nullterminated C string. The other arguments are just the two arguments asspecified. In factargv[1] is a RedisModuleString and"10" is a nullterminated C string.

This is the full list of format specifiers:

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

The function returns aRedisModuleCallReply object on success, onerror NULL is returned.

NULL is returned when the command name is invalid, the format specifier usescharacters that are not recognized, or when the command is called with thewrong number of arguments. In the above cases theerrno var is set toEINVAL. NULL is also returned when, in an instance with Cluster enabled, the targetkeys are about non local hash slots. In this caseerrno is set toEPERM.

Working with RedisModuleCallReply objects.

RedisModuleCall returns reply objects that can be accessed using theRedisModule_CallReply* family of functions.

In order to obtain the type or reply (corresponding to one of the data typessupported by the Redis protocol), the functionRedisModule_CallReplyType()is used:

reply = RedisModule_Call(ctx,"INCRBY","sc",argv[1],"10");if (RedisModule_CallReplyType(reply) == REDISMODULE_REPLY_INTEGER) {    long long myval = RedisModule_CallReplyInteger(reply);    /* Do something with myval. */}

Valid reply types are:

  • REDISMODULE_REPLY_STRING Bulk string or status replies.
  • REDISMODULE_REPLY_ERROR Errors.
  • REDISMODULE_REPLY_INTEGER Signed 64 bit integers.
  • REDISMODULE_REPLY_ARRAY Array of replies.
  • REDISMODULE_REPLY_NULL NULL reply.

Strings, errors and arrays have an associated length. For strings and errorsthe length corresponds to the length of the string. For arrays the lengthis the number of elements. To obtain the reply length the following functionis used:

size_t reply_len = RedisModule_CallReplyLength(reply);

In order to obtain the value of an integer reply, the following function is used, as already shown in the example above:

long long reply_integer_val = RedisModule_CallReplyInteger(reply);

Called with a reply object of the wrong type, the above function alwaysreturnsLLONG_MIN.

Sub elements of array replies are accessed this way:

RedisModuleCallReply *subreply;subreply = RedisModule_CallReplyArrayElement(reply,idx);

The above function returns NULL if you try to access out of range elements.

Strings and errors (which are like strings but with a different type) canbe accessed using in the following way, making sure to never write tothe resulting pointer (that is returned as aconst pointer so thatmisusing must be pretty explicit):

size_t len;char *ptr = RedisModule_CallReplyStringPtr(reply,&len);

If the reply type is not a string or an error, NULL is returned.

RedisCallReply objects are not the same as module string objects(RedisModuleString types). However sometimes you may need to pass repliesof type string or integer, to API functions expecting a module string.

When this is the case, you may want to evaluate if using the low levelAPI could be a simpler way to implement your command, or you can usethe following function in order to create a new string object from acall reply of type string, error or integer:

RedisModuleString *mystr = RedisModule_CreateStringFromCallReply(myreply);

If the reply is not of the right type, NULL is returned.The returned string object should be released withRedisModule_FreeString()as usually, or by enabling automatic memory management (see correspondingsection).

Releasing call reply objects

Reply objects must be freed usingRedisModule_FreeCallReply. For arrays,you need to free only the top level reply, not the nested replies.Currently the module implementation provides a protection in order to avoidcrashing if you free a nested reply object for error, however this featureis not guaranteed to be here forever, so should not be considered partof the API.

If you use automatic memory management (explained later in this document)you don't need to free replies (but you still could if you wish to releasememory ASAP).

Returning values from Redis commands

Like normal Redis commands, new commands implemented via modules must beable to return values to the caller. The API exports a set of functions forthis goal, in order to return the usual types of the Redis protocol, andarrays of such types as elements. Also errors can be returned with anyerror string and code (the error code is the initial uppercase letters inthe error message, like the "BUSY" string in the "BUSY the sever is busy" errormessage).

All the functions to send a reply to the client are calledRedisModule_ReplyWith<something>.

To return an error, use:

RedisModule_ReplyWithError(RedisModuleCtx *ctx, const char *err);

There is a predefined error string for key of wrong type errors:

REDISMODULE_ERRORMSG_WRONGTYPE

Example usage:

RedisModule_ReplyWithError(ctx,"ERR invalid arguments");

We already saw how to reply with along long in the examples above:

RedisModule_ReplyWithLongLong(ctx,12345);

To reply with a simple string, that can't contain binary values or newlines,(so it's suitable to send small words, like "OK") we use:

RedisModule_ReplyWithSimpleString(ctx,"OK");

It's possible to reply with "bulk strings" that are binary safe, usingtwo different functions:

int RedisModule_ReplyWithStringBuffer(RedisModuleCtx *ctx, const char *buf, size_t len);int RedisModule_ReplyWithString(RedisModuleCtx *ctx, RedisModuleString *str);

The first function gets a C pointer and length. The second a RedisModuleStringobject. Use one or the other depending on the source type you have at hand.

In order to reply with an array, you just need to use a function to emit thearray length, followed by as many calls to the above functions as the numberof elements of the array are:

RedisModule_ReplyWithArray(ctx,2);RedisModule_ReplyWithStringBuffer(ctx,"age",3);RedisModule_ReplyWithLongLong(ctx,22);

To return nested arrays is easy, your nested array element just uses anothercall toRedisModule_ReplyWithArray() followed by the calls to emit thesub array elements.

Returning arrays with dynamic length

Sometimes it is not possible to know beforehand the number of items ofan array. As an example, think of a Redis module implementing a FACTORcommand that given a number outputs the prime factors. Instead offactorializing the number, storing the prime factors into an array, andlater produce the command reply, a better solution is to start an arrayreply where the length is not known, and set it later. This is accomplishedwith a special argument toRedisModule_ReplyWithArray():

RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_LEN);

The above call starts an array reply so we can use otherReplyWith callsin order to produce the array items. Finally in order to set the length,use the following call:

RedisModule_ReplySetArrayLength(ctx, number_of_items);

In the case of the FACTOR command, this translates to some code similarto this:

RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_LEN);number_of_factors = 0;while(still_factors) {    RedisModule_ReplyWithLongLong(ctx, some_factor);    number_of_factors++;}RedisModule_ReplySetArrayLength(ctx, number_of_factors);

Another common use case for this feature is iterating over the arrays ofsome collection and only returning the ones passing some kind of filtering.

It is possible to have multiple nested arrays with postponed reply.Each call toSetArray() will set the length of the latest correspondingcall toReplyWithArray():

RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_LEN);... generate 100 elements ...RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_LEN);... generate 10 elements ...RedisModule_ReplySetArrayLength(ctx, 10);RedisModule_ReplySetArrayLength(ctx, 100);

This creates a 100 items array having as last element a 10 items array.

Arity and type checks

Often commands need to check that the number of arguments and type of the keyis correct. In order to report a wrong arity, there is a specific functioncalledRedisModule_WrongArity(). The usage is trivial:

if (argc != 2) return RedisModule_WrongArity(ctx);

Checking for the wrong type involves opening the key and checking the type:

RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],    REDISMODULE_READ|REDISMODULE_WRITE);int keytype = RedisModule_KeyType(key);if (keytype != REDISMODULE_KEYTYPE_STRING &&    keytype != REDISMODULE_KEYTYPE_EMPTY){    RedisModule_CloseKey(key);    return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);}

Note that you often want to proceed with a command both if the keyis of the expected type, or if it's empty.

Low level access to keys

Low level access to keys allow to perform operations on value objects associatedto keys directly, with a speed similar to what Redis uses internally toimplement the built-in commands.

Once a key is opened, a key pointer is returned that will be used with all theother low level API calls in order to perform operations on the key or itsassociated value.

Because the API is meant to be very fast, it cannot do too many run-timechecks, so the user must be aware of certain rules to follow:

  • Opening the same key multiple times where at least one instance is opened for writing, is undefined and may lead to crashes.
  • While a key is open, it should only be accessed via the low level key API. For example opening a key, then calling DEL on the same key using theRedisModule_Call() API will result into a crash. However it is safe to open a key, perform some operation with the low level API, closing it, then using other APIs to manage the same key, and later opening it again to do some more work.

In order to open a key theRedisModule_OpenKey function is used. It returnsa key pointer, that we'll use with all the next calls to access and modifythe value:

RedisModuleKey *key;key = RedisModule_OpenKey(ctx,argv[1],REDISMODULE_READ);

The second argument is the key name, that must be aRedisModuleString object.The third argument is the mode:REDISMODULE_READ orREDISMODULE_WRITE.It is possible to use| to bitwise OR the two modes to open the key inboth modes. Currently a key opened for writing can also be accessed for readingbut this is to be considered an implementation detail. The right mode shouldbe used in sane modules.

You can open non existing keys for writing, since the keys will be createdwhen an attempt to write to the key is performed. However when opening keysjust for reading,RedisModule_OpenKey will return NULL if the key does notexist.

Once you are done using a key, you can close it with:

RedisModule_CloseKey(key);

Note that if automatic memory management is enabled, you are not forced toclose keys. When the module function returns, Redis will take care to closeall the keys which are still open.

Getting the key type

In order to obtain the value of a key, use theRedisModule_KeyType() function:

int keytype = RedisModule_KeyType(key);

It returns one of the following values:

REDISMODULE_KEYTYPE_EMPTYREDISMODULE_KEYTYPE_STRINGREDISMODULE_KEYTYPE_LISTREDISMODULE_KEYTYPE_HASHREDISMODULE_KEYTYPE_SETREDISMODULE_KEYTYPE_ZSET

The above are just the usual Redis key types, with the addition of an emptytype, that signals the key pointer is associated with an empty key thatdoes not yet exists.

Creating new keys

To create a new key, open it for writing and then write to it using oneof the key writing functions. Example:

RedisModuleKey *key;key = RedisModule_OpenKey(ctx,argv[1],REDISMODULE_WRITE);if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_EMPTY) {    RedisModule_StringSet(key,argv[2]);}

Deleting keys

Just use:

RedisModule_DeleteKey(key);

The function returnsREDISMODULE_ERR if the key is not open for writing.Note that after a key gets deleted, it is setup in order to be targetedby new key commands. For exampleRedisModule_KeyType() will return it isan empty key, and writing to it will create a new key, possibly of anothertype (depending on the API used).

Managing key expires (TTLs)

To control key expires two functions are provided, that are able to set,modify, get, and unset the time to live associated with a key.

One function is used in order to query the current expire of an open key:

mstime_t RedisModule_GetExpire(RedisModuleKey *key);

The function returns the time to live of the key in milliseconds, orREDISMODULE_NO_EXPIRE as a special value to signal the key has no associatedexpire or does not exist at all (you can differentiate the two cases checkingif the key type isREDISMODULE_KEYTYPE_EMPTY).

In order to change the expire of a key the following function is used instead:

int RedisModule_SetExpire(RedisModuleKey *key, mstime_t expire);

When called on a non existing key,REDISMODULE_ERR is returned, becausethe function can only associate expires to existing open keys (non existingopen keys are only useful in order to create new values with data typespecific write operations).

Again theexpire time is specified in milliseconds. If the key has currentlyno expire, a new expire is set. If the key already have an expire, it isreplaced with the new value.

If the key has an expire, and the special valueREDISMODULE_NO_EXPIRE isused as a new expire, the expire is removed, similarly to the RedisPERSIST command. In case the key was already persistent, no operation isperformed.

Obtaining the length of values

There is a single function in order to retrieve the length of the valueassociated to an open key. The returned length is value-specific, and isthe string length for strings, and the number of elements for the aggregateddata types (how many elements there is in a list, set, sorted set, hash).

size_t len = RedisModule_ValueLength(key);

If the key does not exist, 0 is returned by the function:

String type API

Setting a new string value, like the RedisSET command does, is performedusing:

int RedisModule_StringSet(RedisModuleKey *key, RedisModuleString *str);

The function works exactly like the RedisSET command itself, that is, ifthere is a prior value (of any type) it will be deleted.

Accessing existing string values is performed using DMA (direct memoryaccess) for speed. The API will return a pointer and a length, so that'spossible to access and, if needed, modify the string directly.

size_t len, j;char *myptr = RedisModule_StringDMA(key,&len,REDISMODULE_WRITE);for (j = 0; j < len; j++) myptr[j] = 'A';

In the above example we write directly on the string. Note that if you wantto write, you must be sure to ask forWRITE mode.

DMA pointers are only valid if no other operations are performed with the keybefore using the pointer, after the DMA call.

Sometimes when we want to manipulate strings directly, we need to changetheir size as well. For this scope, theRedisModule_StringTruncate functionis used. Example:

RedisModule_StringTruncate(mykey,1024);

The function truncates, or enlarges the string as needed, padding it withzero bytes if the previous length is smaller than the new length we request.If the string does not exist sincekey is associated to an open empty key,a string value is created and associated to the key.

Note that every timeStringTruncate() is called, we need to re-obtainthe DMA pointer again, since the old may be invalid.

List type API

It's possible to push and pop values from list values:

int RedisModule_ListPush(RedisModuleKey *key, int where, RedisModuleString *ele);RedisModuleString *RedisModule_ListPop(RedisModuleKey *key, int where);

In both the APIs thewhere argument specifies if to push or pop from tailor head, using the following macros:

REDISMODULE_LIST_HEADREDISMODULE_LIST_TAIL

Elements returned byRedisModule_ListPop() are like strings created withRedisModule_CreateString(), they must be released withRedisModule_FreeString() or by enabling automatic memory management.

Set type API

Work in progress.

Sorted set type API

Documentation missing, please refer to the top comments insidemodule.cfor the following functions:

  • RedisModule_ZsetAdd
  • RedisModule_ZsetIncrby
  • RedisModule_ZsetScore
  • RedisModule_ZsetRem

And for the sorted set iterator:

  • RedisModule_ZsetRangeStop
  • RedisModule_ZsetFirstInScoreRange
  • RedisModule_ZsetLastInScoreRange
  • RedisModule_ZsetFirstInLexRange
  • RedisModule_ZsetLastInLexRange
  • RedisModule_ZsetRangeCurrentElement
  • RedisModule_ZsetRangeNext
  • RedisModule_ZsetRangePrev
  • RedisModule_ZsetRangeEndReached

Hash type API

Documentation missing, please refer to the top comments insidemodule.cfor the following functions:

  • RedisModule_HashSet
  • RedisModule_HashGet

Iterating aggregated values

Work in progress.

Replicating commands

If you want to use module commands exactly like normal Redis commands, in thecontext of replicated Redis instances, or using the AOF file for persistence,it is important for module commands to handle their replication in a consistentway.

When using the higher level APIs to invoke commands, replication happensautomatically if you use the "!" modifier in the format string ofRedisModule_Call() as in the following example:

reply = RedisModule_Call(ctx,"INCRBY","!sc",argv[1],"10");

As you can see the format specifier is"!sc". The bang is not parsed as aformat specifier, but it internally flags the command as "must replicate".

If you use the above programming style, there are no problems.However sometimes things are more complex than that, and you use the low levelAPI. In this case, if there are no side effects in the command execution, andit consistently always performs the same work, what is possible to do is toreplicate the command verbatim as the user executed it. To do that, you justneed to call the following function:

RedisModule_ReplicateVerbatim(ctx);

When you use the above API, you should not use any other replication functionsince they are not guaranteed to mix well.

However this is not the only option. It's also possible to exactly tellRedis what commands to replicate as the effect of the command execution, usingan API similar toRedisModule_Call() but that instead of calling the commandsends it to the AOF / replicas stream. Example:

RedisModule_Replicate(ctx,"INCRBY","cl","foo",my_increment);

It's possible to callRedisModule_Replicate multiple times, and eachwill emit a command. All the sequence emitted is wrapped between aMULTI/EXEC transaction, so that the AOF and replication effects are thesame as executing a single command.

Note that before Redis version 7.0,Call() replication andReplicate() replication have a rule,in case you want to mix both forms of replication (not necessarily a goodidea if there are simpler approaches). Commands replicated withCall()are always the first emitted in the finalMULTI/EXEC block, while allthe commands emitted withReplicate() will follow.

Automatic memory management

Normally when writing programs in the C language, programmers need to managememory manually. This is why the Redis modules API has functions to releasestrings, close open keys, free replies, and so forth.

However given that commands are executed in a contained environment andwith a set of strict APIs, Redis is able to provide automatic memory managementto modules, at the cost of some performance (most of the time, a very lowcost).

When automatic memory management is enabled:

  1. You don't need to close open keys.
  2. You don't need to free replies.
  3. You don't need to free RedisModuleString objects.

However you can still do it, if you want. For example, automatic memorymanagement may be active, but inside a loop allocating a lot of strings,you may still want to free strings no longer used.

In order to enable automatic memory management, just call the followingfunction at the start of the command implementation:

RedisModule_AutoMemory(ctx);

Automatic memory management is usually the way to go, however experiencedC programmers may not use it in order to gain some speed and memory usagebenefit.

Allocating memory into modules

Normal C programs usemalloc() andfree() in order to allocate andrelease memory dynamically. While in Redis modules the use of malloc isnot technically forbidden, it is a lot better to use the Redis Modulesspecific functions, that are exact replacements formalloc,free,realloc andstrdup. These functions are:

void *RedisModule_Alloc(size_t bytes);void* RedisModule_Realloc(void *ptr, size_t bytes);void RedisModule_Free(void *ptr);void RedisModule_Calloc(size_t nmemb, size_t size);char *RedisModule_Strdup(const char *str);

They work exactly like theirlibc equivalent calls, however they usethe same allocator Redis uses, and the memory allocated using thesefunctions is reported by theINFO command in the memory section, isaccounted when enforcing themaxmemory policy, and in general isa first citizen of the Redis executable. On the contrary, the methodallocated inside modules with libcmalloc() is transparent to Redis.

Another reason to use the modules functions in order to allocate memoryis that, when creating native data types inside modules, the RDB loadingfunctions can return deserialized strings (from the RDB file) directlyasRedisModule_Alloc() allocations, so they can be used directly topopulate data structures after loading, instead of having to copy themto the data structure.

Pool allocator

Sometimes in commands implementations, it is required to perform manysmall allocations that will be not retained at the end of the commandexecution, but are just functional to execute the command itself.

This work can be more easily accomplished using the Redis pool allocator:

void *RedisModule_PoolAlloc(RedisModuleCtx *ctx, size_t bytes);

It works similarly tomalloc(), and returns memory aligned to thenext power of two of greater or equal tobytes (for a maximum alignmentof 8 bytes). However it allocates memory in blocks, so it the overheadof the allocations is small, and more important, the memory allocatedis automatically released when the command returns.

So in general short living allocations are a good candidates for the poolallocator.

Writing commands compatible with Redis Cluster

Documentation missing, please check the following functions insidemodule.c:

RedisModule_IsKeysPositionRequest(ctx);RedisModule_KeyAtPos(ctx,pos);
RATE THIS PAGE
Back to top ↑

On this page


[8]ページ先頭

©2009-2026 Movatter.jp