Allow modules to handle RDB loading errors.
This is especially needed in diskless loading, were a short read could have caused redis to exit. now the module can handle the error and return to the caller gracefully. this fixes #5326
This commit is contained in:
parent
3f1c84751a
commit
d7d028a7a7
41
src/module.c
41
src/module.c
@ -3139,9 +3139,14 @@ void *RM_ModuleTypeGetValue(RedisModuleKey *key) {
|
|||||||
* RDB loading and saving functions
|
* RDB loading and saving functions
|
||||||
* -------------------------------------------------------------------------- */
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
/* Called when there is a load error in the context of a module. This cannot
|
/* Called when there is a load error in the context of a module. On some
|
||||||
* be recovered like for the built-in types. */
|
* modules this cannot be recovered, but if the module declared capability
|
||||||
|
* to handle errors, we'll raise a flag rather than exiting. */
|
||||||
void moduleRDBLoadError(RedisModuleIO *io) {
|
void moduleRDBLoadError(RedisModuleIO *io) {
|
||||||
|
if (io->flags & REDISMODULE_HANDLE_IO_ERRORS) {
|
||||||
|
io->error = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
serverLog(LL_WARNING,
|
serverLog(LL_WARNING,
|
||||||
"Error loading data from RDB (short read or EOF). "
|
"Error loading data from RDB (short read or EOF). "
|
||||||
"Read performed by module '%s' about type '%s' "
|
"Read performed by module '%s' about type '%s' "
|
||||||
@ -3152,6 +3157,23 @@ void moduleRDBLoadError(RedisModuleIO *io) {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Set flags defining capabilities or behavior */
|
||||||
|
void RM_SetIOFlags(RedisModuleIO *io, int flags) {
|
||||||
|
io->flags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get flags which were set by RedisModule_SetIOFlags */
|
||||||
|
int RM_GetIOFlags(RedisModuleIO *io) {
|
||||||
|
return io->flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns true if any previous IO API failed.
|
||||||
|
* for Load* APIs the REDISMODULE_HANDLE_IO_ERRORS flag must be set with
|
||||||
|
* RediModule_SetIOFlags first. */
|
||||||
|
int RM_IsIOError(RedisModuleIO *io) {
|
||||||
|
return io->error;
|
||||||
|
}
|
||||||
|
|
||||||
/* Save an unsigned 64 bit value into the RDB file. This function should only
|
/* 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
|
* be called in the context of the rdb_save method of modules implementing new
|
||||||
* data types. */
|
* data types. */
|
||||||
@ -3175,6 +3197,7 @@ saveerr:
|
|||||||
* be called in the context of the rdb_load method of modules implementing
|
* be called in the context of the rdb_load method of modules implementing
|
||||||
* new data types. */
|
* new data types. */
|
||||||
uint64_t RM_LoadUnsigned(RedisModuleIO *io) {
|
uint64_t RM_LoadUnsigned(RedisModuleIO *io) {
|
||||||
|
if (io->error) return 0;
|
||||||
if (io->ver == 2) {
|
if (io->ver == 2) {
|
||||||
uint64_t opcode = rdbLoadLen(io->rio,NULL);
|
uint64_t opcode = rdbLoadLen(io->rio,NULL);
|
||||||
if (opcode != RDB_MODULE_OPCODE_UINT) goto loaderr;
|
if (opcode != RDB_MODULE_OPCODE_UINT) goto loaderr;
|
||||||
@ -3186,7 +3209,7 @@ uint64_t RM_LoadUnsigned(RedisModuleIO *io) {
|
|||||||
|
|
||||||
loaderr:
|
loaderr:
|
||||||
moduleRDBLoadError(io);
|
moduleRDBLoadError(io);
|
||||||
return 0; /* Never reached. */
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Like RedisModule_SaveUnsigned() but for signed 64 bit values. */
|
/* Like RedisModule_SaveUnsigned() but for signed 64 bit values. */
|
||||||
@ -3245,6 +3268,7 @@ saveerr:
|
|||||||
|
|
||||||
/* Implements RM_LoadString() and RM_LoadStringBuffer() */
|
/* Implements RM_LoadString() and RM_LoadStringBuffer() */
|
||||||
void *moduleLoadString(RedisModuleIO *io, int plain, size_t *lenptr) {
|
void *moduleLoadString(RedisModuleIO *io, int plain, size_t *lenptr) {
|
||||||
|
if (io->error) return NULL;
|
||||||
if (io->ver == 2) {
|
if (io->ver == 2) {
|
||||||
uint64_t opcode = rdbLoadLen(io->rio,NULL);
|
uint64_t opcode = rdbLoadLen(io->rio,NULL);
|
||||||
if (opcode != RDB_MODULE_OPCODE_STRING) goto loaderr;
|
if (opcode != RDB_MODULE_OPCODE_STRING) goto loaderr;
|
||||||
@ -3256,7 +3280,7 @@ void *moduleLoadString(RedisModuleIO *io, int plain, size_t *lenptr) {
|
|||||||
|
|
||||||
loaderr:
|
loaderr:
|
||||||
moduleRDBLoadError(io);
|
moduleRDBLoadError(io);
|
||||||
return NULL; /* Never reached. */
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* In the context of the rdb_load method of a module data type, loads a string
|
/* In the context of the rdb_load method of a module data type, loads a string
|
||||||
@ -3305,6 +3329,7 @@ saveerr:
|
|||||||
/* In the context of the rdb_save method of a module data type, loads back the
|
/* In the context of the rdb_save method of a module data type, loads back the
|
||||||
* double value saved by RedisModule_SaveDouble(). */
|
* double value saved by RedisModule_SaveDouble(). */
|
||||||
double RM_LoadDouble(RedisModuleIO *io) {
|
double RM_LoadDouble(RedisModuleIO *io) {
|
||||||
|
if (io->error) return 0;
|
||||||
if (io->ver == 2) {
|
if (io->ver == 2) {
|
||||||
uint64_t opcode = rdbLoadLen(io->rio,NULL);
|
uint64_t opcode = rdbLoadLen(io->rio,NULL);
|
||||||
if (opcode != RDB_MODULE_OPCODE_DOUBLE) goto loaderr;
|
if (opcode != RDB_MODULE_OPCODE_DOUBLE) goto loaderr;
|
||||||
@ -3316,7 +3341,7 @@ double RM_LoadDouble(RedisModuleIO *io) {
|
|||||||
|
|
||||||
loaderr:
|
loaderr:
|
||||||
moduleRDBLoadError(io);
|
moduleRDBLoadError(io);
|
||||||
return 0; /* Never reached. */
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* In the context of the rdb_save method of a module data type, saves a float
|
/* In the context of the rdb_save method of a module data type, saves a float
|
||||||
@ -3341,6 +3366,7 @@ saveerr:
|
|||||||
/* In the context of the rdb_save method of a module data type, loads back the
|
/* In the context of the rdb_save method of a module data type, loads back the
|
||||||
* float value saved by RedisModule_SaveFloat(). */
|
* float value saved by RedisModule_SaveFloat(). */
|
||||||
float RM_LoadFloat(RedisModuleIO *io) {
|
float RM_LoadFloat(RedisModuleIO *io) {
|
||||||
|
if (io->error) return 0;
|
||||||
if (io->ver == 2) {
|
if (io->ver == 2) {
|
||||||
uint64_t opcode = rdbLoadLen(io->rio,NULL);
|
uint64_t opcode = rdbLoadLen(io->rio,NULL);
|
||||||
if (opcode != RDB_MODULE_OPCODE_FLOAT) goto loaderr;
|
if (opcode != RDB_MODULE_OPCODE_FLOAT) goto loaderr;
|
||||||
@ -3352,7 +3378,7 @@ float RM_LoadFloat(RedisModuleIO *io) {
|
|||||||
|
|
||||||
loaderr:
|
loaderr:
|
||||||
moduleRDBLoadError(io);
|
moduleRDBLoadError(io);
|
||||||
return 0; /* Never reached. */
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------
|
||||||
@ -5408,6 +5434,9 @@ void moduleRegisterCoreAPI(void) {
|
|||||||
REGISTER_API(ModuleTypeSetValue);
|
REGISTER_API(ModuleTypeSetValue);
|
||||||
REGISTER_API(ModuleTypeGetType);
|
REGISTER_API(ModuleTypeGetType);
|
||||||
REGISTER_API(ModuleTypeGetValue);
|
REGISTER_API(ModuleTypeGetValue);
|
||||||
|
REGISTER_API(GetIOFlags);
|
||||||
|
REGISTER_API(SetIOFlags);
|
||||||
|
REGISTER_API(IsIOError);
|
||||||
REGISTER_API(SaveUnsigned);
|
REGISTER_API(SaveUnsigned);
|
||||||
REGISTER_API(LoadUnsigned);
|
REGISTER_API(LoadUnsigned);
|
||||||
REGISTER_API(SaveSigned);
|
REGISTER_API(SaveSigned);
|
||||||
|
@ -140,6 +140,9 @@ typedef uint64_t RedisModuleTimerID;
|
|||||||
/* Do filter RedisModule_Call() commands initiated by module itself. */
|
/* Do filter RedisModule_Call() commands initiated by module itself. */
|
||||||
#define REDISMODULE_CMDFILTER_NOSELF (1<<0)
|
#define REDISMODULE_CMDFILTER_NOSELF (1<<0)
|
||||||
|
|
||||||
|
/* Declare that the module can handle errors with RedisModule_SetIOFlags */
|
||||||
|
#define REDISMODULE_HANDLE_IO_ERRORS (1<<0)
|
||||||
|
|
||||||
/* ------------------------- End of common defines ------------------------ */
|
/* ------------------------- End of common defines ------------------------ */
|
||||||
|
|
||||||
#ifndef REDISMODULE_CORE
|
#ifndef REDISMODULE_CORE
|
||||||
@ -271,6 +274,9 @@ RedisModuleType *REDISMODULE_API_FUNC(RedisModule_CreateDataType)(RedisModuleCtx
|
|||||||
int REDISMODULE_API_FUNC(RedisModule_ModuleTypeSetValue)(RedisModuleKey *key, RedisModuleType *mt, void *value);
|
int REDISMODULE_API_FUNC(RedisModule_ModuleTypeSetValue)(RedisModuleKey *key, RedisModuleType *mt, void *value);
|
||||||
RedisModuleType *REDISMODULE_API_FUNC(RedisModule_ModuleTypeGetType)(RedisModuleKey *key);
|
RedisModuleType *REDISMODULE_API_FUNC(RedisModule_ModuleTypeGetType)(RedisModuleKey *key);
|
||||||
void *REDISMODULE_API_FUNC(RedisModule_ModuleTypeGetValue)(RedisModuleKey *key);
|
void *REDISMODULE_API_FUNC(RedisModule_ModuleTypeGetValue)(RedisModuleKey *key);
|
||||||
|
void REDISMODULE_API_FUNC(RedisModule_SetIOFlags)(RedisModuleIO *io, int flags);
|
||||||
|
int REDISMODULE_API_FUNC(RedisModule_GetIOFlags)(RedisModuleIO *io);
|
||||||
|
int REDISMODULE_API_FUNC(RedisModule_IsIOError)(RedisModuleIO *io);
|
||||||
void REDISMODULE_API_FUNC(RedisModule_SaveUnsigned)(RedisModuleIO *io, uint64_t value);
|
void REDISMODULE_API_FUNC(RedisModule_SaveUnsigned)(RedisModuleIO *io, uint64_t value);
|
||||||
uint64_t REDISMODULE_API_FUNC(RedisModule_LoadUnsigned)(RedisModuleIO *io);
|
uint64_t REDISMODULE_API_FUNC(RedisModule_LoadUnsigned)(RedisModuleIO *io);
|
||||||
void REDISMODULE_API_FUNC(RedisModule_SaveSigned)(RedisModuleIO *io, int64_t value);
|
void REDISMODULE_API_FUNC(RedisModule_SaveSigned)(RedisModuleIO *io, int64_t value);
|
||||||
@ -444,6 +450,9 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
|
|||||||
REDISMODULE_GET_API(ModuleTypeSetValue);
|
REDISMODULE_GET_API(ModuleTypeSetValue);
|
||||||
REDISMODULE_GET_API(ModuleTypeGetType);
|
REDISMODULE_GET_API(ModuleTypeGetType);
|
||||||
REDISMODULE_GET_API(ModuleTypeGetValue);
|
REDISMODULE_GET_API(ModuleTypeGetValue);
|
||||||
|
REDISMODULE_GET_API(SetIOFlags);
|
||||||
|
REDISMODULE_GET_API(GetIOFlags);
|
||||||
|
REDISMODULE_GET_API(IsIOError);
|
||||||
REDISMODULE_GET_API(SaveUnsigned);
|
REDISMODULE_GET_API(SaveUnsigned);
|
||||||
REDISMODULE_GET_API(LoadUnsigned);
|
REDISMODULE_GET_API(LoadUnsigned);
|
||||||
REDISMODULE_GET_API(SaveSigned);
|
REDISMODULE_GET_API(SaveSigned);
|
||||||
|
@ -599,6 +599,7 @@ typedef struct RedisModuleIO {
|
|||||||
* 2 (current version with opcodes annotation). */
|
* 2 (current version with opcodes annotation). */
|
||||||
struct RedisModuleCtx *ctx; /* Optional context, see RM_GetContextFromIO()*/
|
struct RedisModuleCtx *ctx; /* Optional context, see RM_GetContextFromIO()*/
|
||||||
struct redisObject *key; /* Optional name of key processed */
|
struct redisObject *key; /* Optional name of key processed */
|
||||||
|
int flags; /* flags declaring capabilities or behavior */
|
||||||
} RedisModuleIO;
|
} RedisModuleIO;
|
||||||
|
|
||||||
/* Macro to initialize an IO context. Note that the 'ver' field is populated
|
/* Macro to initialize an IO context. Note that the 'ver' field is populated
|
||||||
@ -611,6 +612,7 @@ typedef struct RedisModuleIO {
|
|||||||
iovar.ver = 0; \
|
iovar.ver = 0; \
|
||||||
iovar.key = keyptr; \
|
iovar.key = keyptr; \
|
||||||
iovar.ctx = NULL; \
|
iovar.ctx = NULL; \
|
||||||
|
iovar.flags = 0; \
|
||||||
} while(0);
|
} while(0);
|
||||||
|
|
||||||
/* This is a structure used to export DEBUG DIGEST capabilities to Redis
|
/* This is a structure used to export DEBUG DIGEST capabilities to Redis
|
||||||
|
Loading…
x
Reference in New Issue
Block a user