diff options
author | Ozan Tezcan <ozantezcan@gmail.com> | 2023-04-09 12:07:32 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-09 12:07:32 +0300 |
commit | e55568edb58c1437d6a84e1baa103dcf5b6153ef (patch) | |
tree | 04377f49bbdbba0be4c6e5514a0a274ec8b05d89 /src/module.c | |
parent | f263b6daf3a2672acc383dc34ed1ff1fe19da5a7 (diff) | |
download | redis-e55568edb58c1437d6a84e1baa103dcf5b6153ef.tar.gz |
Add RM_RdbLoad and RM_RdbSave module API functions (#11852)
Add `RM_RdbLoad()` and `RM_RdbSave()` to load/save RDB files from the module API.
In our use case, we have our clustering implementation as a module. As part of this
implementation, the module needs to trigger RDB save operation at specific points.
Also, this module delivers RDB files to other nodes (not using Redis' replication).
When a node receives an RDB file, it should be able to load the RDB. Currently,
there is no module API to save/load RDB files.
This PR adds four new APIs:
```c
RedisModuleRdbStream *RM_RdbStreamCreateFromFile(const char *filename);
void RM_RdbStreamFree(RedisModuleRdbStream *stream);
int RM_RdbLoad(RedisModuleCtx *ctx, RedisModuleRdbStream *stream, int flags);
int RM_RdbSave(RedisModuleCtx *ctx, RedisModuleRdbStream *stream, int flags);
```
The first step is to create a `RedisModuleRdbStream` object. This PR provides a function to
create RedisModuleRdbStream from the filename. (You can load/save RDB with the filename).
In the future, this API can be extended if needed:
e.g., `RM_RdbStreamCreateFromFd()`, `RM_RdbStreamCreateFromSocket()` to save/load
RDB from an `fd` or a `socket`.
Usage:
```c
/* Save RDB */
RedisModuleRdbStream *stream = RedisModule_RdbStreamCreateFromFile("example.rdb");
RedisModule_RdbSave(ctx, stream, 0);
RedisModule_RdbStreamFree(stream);
/* Load RDB */
RedisModuleRdbStream *stream = RedisModule_RdbStreamCreateFromFile("example.rdb");
RedisModule_RdbLoad(ctx, stream, 0);
RedisModule_RdbStreamFree(stream);
```
Diffstat (limited to 'src/module.c')
-rw-r--r-- | src/module.c | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/src/module.c b/src/module.c index 066786976..ae75b3dc2 100644 --- a/src/module.c +++ b/src/module.c @@ -12751,6 +12751,137 @@ int RM_LoadConfigs(RedisModuleCtx *ctx) { return REDISMODULE_OK; } +/* -------------------------------------------------------------------------- + * ## RDB load/save API + * -------------------------------------------------------------------------- */ + +#define REDISMODULE_RDB_STREAM_FILE 1 + +typedef struct RedisModuleRdbStream { + int type; + + union { + char *filename; + } data; +} RedisModuleRdbStream; + +/* Create a stream object to save/load RDB to/from a file. + * + * This function returns a pointer to RedisModuleRdbStream which is owned + * by the caller. It requires a call to RM_RdbStreamFree() to free + * the object. */ +RedisModuleRdbStream *RM_RdbStreamCreateFromFile(const char *filename) { + RedisModuleRdbStream *stream = zmalloc(sizeof(*stream)); + stream->type = REDISMODULE_RDB_STREAM_FILE; + stream->data.filename = zstrdup(filename); + return stream; +} + +/* Release an RDB stream object. */ +void RM_RdbStreamFree(RedisModuleRdbStream *stream) { + switch (stream->type) { + case REDISMODULE_RDB_STREAM_FILE: + zfree(stream->data.filename); + break; + default: + serverAssert(0); + break; + } + zfree(stream); +} + +/* Load RDB file from the `stream`. Dataset will be cleared first and then RDB + * file will be loaded. + * + * `flags` must be zero. This parameter is for future use. + * + * On success REDISMODULE_OK is returned, otherwise REDISMODULE_ERR is returned + * and errno is set accordingly. + * + * Example: + * + * RedisModuleRdbStream *s = RedisModule_RdbStreamCreateFromFile("exp.rdb"); + * RedisModule_RdbLoad(ctx, s, 0); + * RedisModule_RdbStreamFree(s); + */ +int RM_RdbLoad(RedisModuleCtx *ctx, RedisModuleRdbStream *stream, int flags) { + UNUSED(ctx); + + if (!stream || flags != 0) { + errno = EINVAL; + return REDISMODULE_ERR; + } + + /* Not allowed on replicas. */ + if (server.masterhost != NULL) { + errno = ENOTSUP; + return REDISMODULE_ERR; + } + + /* Drop replicas if exist. */ + disconnectSlaves(); + freeReplicationBacklog(); + + if (server.aof_state != AOF_OFF) stopAppendOnly(); + + /* Kill existing RDB fork as it is saving outdated data. Also killing it + * will prevent COW memory issue. */ + if (server.child_type == CHILD_TYPE_RDB) killRDBChild(); + + emptyData(-1,EMPTYDB_NO_FLAGS,NULL); + + /* rdbLoad() can go back to the networking and process network events. If + * RM_RdbLoad() is called inside a command callback, we don't want to + * process the current client. Otherwise, we may free the client or try to + * process next message while we are already in the command callback. */ + if (server.current_client) protectClient(server.current_client); + + serverAssert(stream->type == REDISMODULE_RDB_STREAM_FILE); + int ret = rdbLoad(stream->data.filename,NULL,RDBFLAGS_NONE); + + if (server.current_client) unprotectClient(server.current_client); + if (server.aof_state != AOF_OFF) startAppendOnly(); + + if (ret != RDB_OK) { + errno = (ret == RDB_NOT_EXIST) ? ENOENT : EIO; + return REDISMODULE_ERR; + } + + errno = 0; + return REDISMODULE_OK; +} + +/* Save dataset to the RDB stream. + * + * `flags` must be zero. This parameter is for future use. + * + * On success REDISMODULE_OK is returned, otherwise REDISMODULE_ERR is returned + * and errno is set accordingly. + * + * Example: + * + * RedisModuleRdbStream *s = RedisModule_RdbStreamCreateFromFile("exp.rdb"); + * RedisModule_RdbSave(ctx, s, 0); + * RedisModule_RdbStreamFree(s); + */ +int RM_RdbSave(RedisModuleCtx *ctx, RedisModuleRdbStream *stream, int flags) { + UNUSED(ctx); + + if (!stream || flags != 0) { + errno = EINVAL; + return REDISMODULE_ERR; + } + + serverAssert(stream->type == REDISMODULE_RDB_STREAM_FILE); + + if (rdbSaveToFile(stream->data.filename) != C_OK) { + return REDISMODULE_ERR; + } + + errno = 0; + return REDISMODULE_OK; +} + /* Redis MODULE command. * * MODULE LIST @@ -13627,4 +13758,8 @@ void moduleRegisterCoreAPI(void) { REGISTER_API(RegisterEnumConfig); REGISTER_API(LoadConfigs); REGISTER_API(RegisterAuthCallback); + REGISTER_API(RdbStreamCreateFromFile); + REGISTER_API(RdbStreamFree); + REGISTER_API(RdbLoad); + REGISTER_API(RdbSave); } |