2. ● A way to extend Redis with native code/libraries
● For compiling dynamically loaded libraries
● Distributed as a C header file (redismodule.h)
● C++ and Rust are easily possible, others perhaps
● ABI backwards compatible
● An isolated interface, decoupled from internals
● Made of a high-level API, and the low-level APIs
● Available as of v4
The Redis Module API in a nutshell is
3. Who We Are
Open source. The leading in-memory database platform,
supporting any high performance operational, analytics or
hybrid use case.
The open source home and commercial provider of Redis
Enterprise (Redise
) technology, platform, products & services.
Itamar Haber @itamarhaber, Evangely Technicalist, formerly
Chief Developer Advocate & Chief OSS Education Officerhello I am
4. No
The Decision Chart: when to develop a module
Gave serious
thought about
what you need
to solve?
Is there a core
Redis capability
that does it?
Do that!
Can you do it in
the app?
Is Lua good
enough?
Is there an
existing GA
module that
already does it?
Yes
No
No No
Honestly
factored cost of
taking on a new
software
project?
Yes Yes Yes
No
Is it a valid
feature request
to the core?
Yes
Yes
No
mebi
Roll
out
your
own
Try that first.
Hackers love
helping each
other 3:-)
5. ctx is the call's context.
argv and argc are the arguments and their count.
A module is a C file with commands
#include "redismodule.h"
int MyCommand(RedisModuleCtx *ctx,
RedisModuleString **argv, int argc) {
// My code here
// ...
return REDISMODULE_OK;
}
6.
7. Will yield a standard error when condition is met.
Implementing the ZPOP command
/**
ZPOP <key>
*/
int ZPop(RedisModuleCtx *ctx,
RedisModuleString **argv, int argc) {
if (argc != 2) {
RedisModule_WrongArity(ctx);
return REDISMODULE_OK;
}
8. Off by default, turn on by calling before the return.
Automatically keeps track of things like opened keys,
allocated high level API and Redis objects.
Frees everything after the call to function returns, so you
don't have to.
Activate AutomajikMemory
RedisModule_AutoMemory(ctx);
9. Performing a call to Redis via the high-level API
RedisModuleCallReply *rep = RedisModule_Call(ctx,
"ZRANGE", "!sllc", argv[1], 0, 0, "WITHSCORES");
if (RedisModule_CallReplyType(rep) ==
REDISMODULE_REPLY_ERROR) {
RedisModule_ReplyWithCallReply(ctx, rep);
return REDISMODULE_OK;
}
10. • vis a vis Lua's redis.call()
• Variadic arguments via printf format-style
• "!..." means don't replicate to AOF and/or slaves
• RedisModule_Replicate() is exactly like
RedisModule_Call(), only that it replicates rather
than actually calling the command
RedisModule_Call()
11. Extract the element, a call to ZREM & reply
RedisModuleString *ele =
RedisModule_CreateStringFromCallReply(
RedisModule_CallReplyArrayElement(arr, 0));
RedisModule_Call(ctx, "ZREM", "ss",
key, ele);
RedisModule_ReplyWithCallReply(ctx, rep);
13. // Registering the module and its commands
int RedisModule_OnLoad(RedisModuleCtx *ctx,
RedisModuleString **argv, int argc) {
if (RedisModule_Init(ctx, "example", 1,
REDISMODULE_APIVER_1) == REDISMODULE_ERR) {
return REDISMODULE_ERR;
}
if (RedisModule_CreateCommand(ctx,
"example.zpop", ZPop, "Write", 1, 1, 1) ==
REDISMODULE_ERR) {
return REDISMODULE_ERR;
}
return REDISMODULE_OK;
}
14. # Compile it on Linux:
$ gcc -fPIC -std=gnu99 -c -o zpop.o zpop.c
$ ld -o zpop.so zpop.o -shared -Bsymbolic -lc
# Compile it on OSX:
$ gcc -dynamic -fno-common -std=gnu99 -c -o zpop.o zpop.c
$ ld -o zpop.so zpop.o -bundle -undefined dynamic_lookup
-lc
# Run it:
$ redis-server --loadmodule ./zpop.so
# Use it:
$ redis-cli
redis> ZADD z 0 a 1 b 3 c
(integer) 3
redis> EXAMPLE.ZPOP z
"a"
15. That's almost it.
There are several other lower-level functions to deal with
examining, extracting from and iterating over the
RedisModule_CallReply type.
It is easy to use the high-level API, and it can invoke (almost)
any Redis command.
And it is not that slow. Where did you hear that? :)
How high can you get?
16. The low-level APIs provide more and some less exciting
capabilities.
For example, RedisModule_WrongArity() belongs to
the low level API. It is just helper for replying with standard
error.
But since it has nothing to do with RedisModule_Call(),
it is considered "low-level".
The Low-level APIs
17. ● RedisModule_ReplyWith*
● Common keyspace, e.g. RedisModule_DeleteKey()
● Memory management, non-automatic
● RedisModuleString API
● Hash operations (RedisModule_HashGet(), HashSet())
● Sorted Set operations, including range iteration
● Replication control
● Module unload hook (experimental)
Some "groups" of the low-level APIs
18. ● String Direct Memory Access (DMA)
● Blocking commands (a-la BLPOP)
● Callback on keyspace notifications (triggers, WAT?!?)
● Threading API (put them extra 31 cores to use)
● Key locking API (ATM in experimentational state)
● Cluster API (planned)
● @antirez' current goal - make the API robust enough for
implementing Disque as a module <- that's good enough
for me:)
The lower it gets, the cooler it becomes
19. Do/don't use RedisModule_ReplicateVerbatim()
and/or RedisModule_Replicate()
Call RedisModule_CreateDataType()
The custom data type API
void MyTypeLoad(RedisModuleIO *rdb, int encver);
void MyTypeSave(RedisModuleIO *rdb, void *value);
void MyTypeRewrite(RedisModuleIO *aof,
RedisModuleString *key, void *value);
size_t MyTypeMemUsage(const void *value);
void MyTypeFree(void *value);
20. ● Deep dive into low-level APIs
● How to debug with gdb/lldb
● How to write unit/integration tests
● Calling module commands from clients
Do not fear though! Most is available in the documentation,
existing modules repos and online.
Also, check out ze module: github.com/itamarhaber/zpop
So much more important stuff left to cover...
21. You can get started with a template HGETSET project, a
skeleton makefile and a bunch of make-my-life-easier
utilities, simply by cloning:
https://github.com/RedisLabs/RedisModulesSDK
The Modules SDK