futriix/tests/modules/datatype.c
Dmitry Polyakovsky f0e1edc273
Updated modules examples and tests to use valkey vs redis (#349)
Scope of the changes:
- updated example modules to reference Valkey vs Redis in variable names
- updated tests to use valkeymodule.h
- updated vars in tests/modules to use valkey vs redis in variable names

Summary of the testing:
- ran make for all modules, loaded them into valkey-server and tested
commands
- ran make for test/modules
- ran make test for the entire codebase

---------

Signed-off-by: Dmitry Polyakovsky <dmitry.polyakovky@oracle.com>
Co-authored-by: Dmitry Polyakovsky <dmitry.polyakovky@oracle.com>
2024-04-23 17:55:44 +02:00

315 lines
10 KiB
C

/* This module current tests a small subset but should be extended in the future
* for general ModuleDataType coverage.
*/
/* define macros for having usleep */
#define _BSD_SOURCE
#define _DEFAULT_SOURCE
#include <unistd.h>
#include "valkeymodule.h"
static ValkeyModuleType *datatype = NULL;
static int load_encver = 0;
/* used to test processing events during slow loading */
static volatile int slow_loading = 0;
static volatile int is_in_slow_loading = 0;
#define DATATYPE_ENC_VER 1
typedef struct {
long long intval;
ValkeyModuleString *strval;
} DataType;
static void *datatype_load(ValkeyModuleIO *io, int encver) {
load_encver = encver;
int intval = ValkeyModule_LoadSigned(io);
if (ValkeyModule_IsIOError(io)) return NULL;
ValkeyModuleString *strval = ValkeyModule_LoadString(io);
if (ValkeyModule_IsIOError(io)) return NULL;
DataType *dt = (DataType *) ValkeyModule_Alloc(sizeof(DataType));
dt->intval = intval;
dt->strval = strval;
if (slow_loading) {
ValkeyModuleCtx *ctx = ValkeyModule_GetContextFromIO(io);
is_in_slow_loading = 1;
while (slow_loading) {
ValkeyModule_Yield(ctx, VALKEYMODULE_YIELD_FLAG_CLIENTS, "Slow module operation");
usleep(1000);
}
is_in_slow_loading = 0;
}
return dt;
}
static void datatype_save(ValkeyModuleIO *io, void *value) {
DataType *dt = (DataType *) value;
ValkeyModule_SaveSigned(io, dt->intval);
ValkeyModule_SaveString(io, dt->strval);
}
static void datatype_free(void *value) {
if (value) {
DataType *dt = (DataType *) value;
if (dt->strval) ValkeyModule_FreeString(NULL, dt->strval);
ValkeyModule_Free(dt);
}
}
static void *datatype_copy(ValkeyModuleString *fromkey, ValkeyModuleString *tokey, const void *value) {
const DataType *old = value;
/* Answers to ultimate questions cannot be copied! */
if (old->intval == 42)
return NULL;
DataType *new = (DataType *) ValkeyModule_Alloc(sizeof(DataType));
new->intval = old->intval;
new->strval = ValkeyModule_CreateStringFromString(NULL, old->strval);
/* Breaking the rules here! We return a copy that also includes traces
* of fromkey/tokey to confirm we get what we expect.
*/
size_t len;
const char *str = ValkeyModule_StringPtrLen(fromkey, &len);
ValkeyModule_StringAppendBuffer(NULL, new->strval, "/", 1);
ValkeyModule_StringAppendBuffer(NULL, new->strval, str, len);
ValkeyModule_StringAppendBuffer(NULL, new->strval, "/", 1);
str = ValkeyModule_StringPtrLen(tokey, &len);
ValkeyModule_StringAppendBuffer(NULL, new->strval, str, len);
return new;
}
static int datatype_set(ValkeyModuleCtx *ctx, ValkeyModuleString **argv, int argc) {
if (argc != 4) {
ValkeyModule_WrongArity(ctx);
return VALKEYMODULE_OK;
}
long long intval;
if (ValkeyModule_StringToLongLong(argv[2], &intval) != VALKEYMODULE_OK) {
ValkeyModule_ReplyWithError(ctx, "Invalid integer value");
return VALKEYMODULE_OK;
}
ValkeyModuleKey *key = ValkeyModule_OpenKey(ctx, argv[1], VALKEYMODULE_WRITE);
DataType *dt = ValkeyModule_Calloc(sizeof(DataType), 1);
dt->intval = intval;
dt->strval = argv[3];
ValkeyModule_RetainString(ctx, dt->strval);
ValkeyModule_ModuleTypeSetValue(key, datatype, dt);
ValkeyModule_CloseKey(key);
ValkeyModule_ReplyWithSimpleString(ctx, "OK");
return VALKEYMODULE_OK;
}
static int datatype_restore(ValkeyModuleCtx *ctx, ValkeyModuleString **argv, int argc) {
if (argc != 4) {
ValkeyModule_WrongArity(ctx);
return VALKEYMODULE_OK;
}
long long encver;
if (ValkeyModule_StringToLongLong(argv[3], &encver) != VALKEYMODULE_OK) {
ValkeyModule_ReplyWithError(ctx, "Invalid integer value");
return VALKEYMODULE_OK;
}
DataType *dt = ValkeyModule_LoadDataTypeFromStringEncver(argv[2], datatype, encver);
if (!dt) {
ValkeyModule_ReplyWithError(ctx, "Invalid data");
return VALKEYMODULE_OK;
}
ValkeyModuleKey *key = ValkeyModule_OpenKey(ctx, argv[1], VALKEYMODULE_WRITE);
ValkeyModule_ModuleTypeSetValue(key, datatype, dt);
ValkeyModule_CloseKey(key);
ValkeyModule_ReplyWithLongLong(ctx, load_encver);
return VALKEYMODULE_OK;
}
static int datatype_get(ValkeyModuleCtx *ctx, ValkeyModuleString **argv, int argc) {
if (argc != 2) {
ValkeyModule_WrongArity(ctx);
return VALKEYMODULE_OK;
}
ValkeyModuleKey *key = ValkeyModule_OpenKey(ctx, argv[1], VALKEYMODULE_READ);
DataType *dt = ValkeyModule_ModuleTypeGetValue(key);
ValkeyModule_CloseKey(key);
if (!dt) {
ValkeyModule_ReplyWithNullArray(ctx);
} else {
ValkeyModule_ReplyWithArray(ctx, 2);
ValkeyModule_ReplyWithLongLong(ctx, dt->intval);
ValkeyModule_ReplyWithString(ctx, dt->strval);
}
return VALKEYMODULE_OK;
}
static int datatype_dump(ValkeyModuleCtx *ctx, ValkeyModuleString **argv, int argc) {
if (argc != 2) {
ValkeyModule_WrongArity(ctx);
return VALKEYMODULE_OK;
}
ValkeyModuleKey *key = ValkeyModule_OpenKey(ctx, argv[1], VALKEYMODULE_READ);
DataType *dt = ValkeyModule_ModuleTypeGetValue(key);
ValkeyModule_CloseKey(key);
ValkeyModuleString *reply = ValkeyModule_SaveDataTypeToString(ctx, dt, datatype);
if (!reply) {
ValkeyModule_ReplyWithError(ctx, "Failed to save");
return VALKEYMODULE_OK;
}
ValkeyModule_ReplyWithString(ctx, reply);
ValkeyModule_FreeString(ctx, reply);
return VALKEYMODULE_OK;
}
static int datatype_swap(ValkeyModuleCtx *ctx, ValkeyModuleString **argv, int argc) {
if (argc != 3) {
ValkeyModule_WrongArity(ctx);
return VALKEYMODULE_OK;
}
ValkeyModuleKey *a = ValkeyModule_OpenKey(ctx, argv[1], VALKEYMODULE_WRITE);
ValkeyModuleKey *b = ValkeyModule_OpenKey(ctx, argv[2], VALKEYMODULE_WRITE);
void *val = ValkeyModule_ModuleTypeGetValue(a);
int error = (ValkeyModule_ModuleTypeReplaceValue(b, datatype, val, &val) == VALKEYMODULE_ERR ||
ValkeyModule_ModuleTypeReplaceValue(a, datatype, val, NULL) == VALKEYMODULE_ERR);
if (!error)
ValkeyModule_ReplyWithSimpleString(ctx, "OK");
else
ValkeyModule_ReplyWithError(ctx, "ERR failed");
ValkeyModule_CloseKey(a);
ValkeyModule_CloseKey(b);
return VALKEYMODULE_OK;
}
/* used to enable or disable slow loading */
static int datatype_slow_loading(ValkeyModuleCtx *ctx, ValkeyModuleString **argv, int argc) {
if (argc != 2) {
ValkeyModule_WrongArity(ctx);
return VALKEYMODULE_OK;
}
long long ll;
if (ValkeyModule_StringToLongLong(argv[1], &ll) != VALKEYMODULE_OK) {
ValkeyModule_ReplyWithError(ctx, "Invalid integer value");
return VALKEYMODULE_OK;
}
slow_loading = ll;
ValkeyModule_ReplyWithSimpleString(ctx, "OK");
return VALKEYMODULE_OK;
}
/* used to test if we reached the slow loading code */
static int datatype_is_in_slow_loading(ValkeyModuleCtx *ctx, ValkeyModuleString **argv, int argc) {
VALKEYMODULE_NOT_USED(argv);
if (argc != 1) {
ValkeyModule_WrongArity(ctx);
return VALKEYMODULE_OK;
}
ValkeyModule_ReplyWithLongLong(ctx, is_in_slow_loading);
return VALKEYMODULE_OK;
}
int createDataTypeBlockCheck(ValkeyModuleCtx *ctx, ValkeyModuleString **argv, int argc) {
VALKEYMODULE_NOT_USED(argv);
VALKEYMODULE_NOT_USED(argc);
static ValkeyModuleType *datatype_outside_onload = NULL;
ValkeyModuleTypeMethods datatype_methods = {
.version = VALKEYMODULE_TYPE_METHOD_VERSION,
.rdb_load = datatype_load,
.rdb_save = datatype_save,
.free = datatype_free,
.copy = datatype_copy
};
datatype_outside_onload = ValkeyModule_CreateDataType(ctx, "test_dt_outside_onload", 1, &datatype_methods);
/* This validates that it's not possible to create datatype outside OnLoad,
* thus returns an error if it succeeds. */
if (datatype_outside_onload == NULL) {
ValkeyModule_ReplyWithSimpleString(ctx, "OK");
} else {
ValkeyModule_ReplyWithError(ctx, "UNEXPECTEDOK");
}
return VALKEYMODULE_OK;
}
int ValkeyModule_OnLoad(ValkeyModuleCtx *ctx, ValkeyModuleString **argv, int argc) {
VALKEYMODULE_NOT_USED(argv);
VALKEYMODULE_NOT_USED(argc);
if (ValkeyModule_Init(ctx,"datatype",DATATYPE_ENC_VER,VALKEYMODULE_APIVER_1) == VALKEYMODULE_ERR)
return VALKEYMODULE_ERR;
/* Creates a command which creates a datatype outside OnLoad() function. */
if (ValkeyModule_CreateCommand(ctx,"block.create.datatype.outside.onload", createDataTypeBlockCheck, "write", 0, 0, 0) == VALKEYMODULE_ERR)
return VALKEYMODULE_ERR;
ValkeyModule_SetModuleOptions(ctx, VALKEYMODULE_OPTIONS_HANDLE_IO_ERRORS);
ValkeyModuleTypeMethods datatype_methods = {
.version = VALKEYMODULE_TYPE_METHOD_VERSION,
.rdb_load = datatype_load,
.rdb_save = datatype_save,
.free = datatype_free,
.copy = datatype_copy
};
datatype = ValkeyModule_CreateDataType(ctx, "test___dt", 1, &datatype_methods);
if (datatype == NULL)
return VALKEYMODULE_ERR;
if (ValkeyModule_CreateCommand(ctx,"datatype.set", datatype_set,
"write deny-oom", 1, 1, 1) == VALKEYMODULE_ERR)
return VALKEYMODULE_ERR;
if (ValkeyModule_CreateCommand(ctx,"datatype.get", datatype_get,"",1,1,1) == VALKEYMODULE_ERR)
return VALKEYMODULE_ERR;
if (ValkeyModule_CreateCommand(ctx,"datatype.restore", datatype_restore,
"write deny-oom", 1, 1, 1) == VALKEYMODULE_ERR)
return VALKEYMODULE_ERR;
if (ValkeyModule_CreateCommand(ctx,"datatype.dump", datatype_dump,"",1,1,1) == VALKEYMODULE_ERR)
return VALKEYMODULE_ERR;
if (ValkeyModule_CreateCommand(ctx, "datatype.swap", datatype_swap,
"write", 1, 1, 1) == VALKEYMODULE_ERR)
return VALKEYMODULE_ERR;
if (ValkeyModule_CreateCommand(ctx, "datatype.slow_loading", datatype_slow_loading,
"allow-loading", 0, 0, 0) == VALKEYMODULE_ERR)
return VALKEYMODULE_ERR;
if (ValkeyModule_CreateCommand(ctx, "datatype.is_in_slow_loading", datatype_is_in_slow_loading,
"allow-loading", 0, 0, 0) == VALKEYMODULE_ERR)
return VALKEYMODULE_ERR;
return VALKEYMODULE_OK;
}