Unverified Commit 70469b76 authored by Salvatore Sanfilippo's avatar Salvatore Sanfilippo Committed by GitHub
Browse files

Merge branch 'unstable' into expose_zmalloc_capabilities

parents e45239e3 c697edf4
......@@ -19,7 +19,9 @@ TEST_MODULES = \
propagate.so \
misc.so \
hooks.so \
blockonkeys.so
blockonkeys.so \
scan.so \
datatype.so
.PHONY: all
......
/* This module current tests a small subset but should be extended in the future
* for general ModuleDataType coverage.
*/
#include "redismodule.h"
static RedisModuleType *datatype = NULL;
typedef struct {
long long intval;
RedisModuleString *strval;
} DataType;
static void *datatype_load(RedisModuleIO *io, int encver) {
(void) encver;
int intval = RedisModule_LoadSigned(io);
if (RedisModule_IsIOError(io)) return NULL;
RedisModuleString *strval = RedisModule_LoadString(io);
if (RedisModule_IsIOError(io)) return NULL;
DataType *dt = (DataType *) RedisModule_Alloc(sizeof(DataType));
dt->intval = intval;
dt->strval = strval;
return dt;
}
static void datatype_save(RedisModuleIO *io, void *value) {
DataType *dt = (DataType *) value;
RedisModule_SaveSigned(io, dt->intval);
RedisModule_SaveString(io, dt->strval);
}
static void datatype_free(void *value) {
if (value) {
DataType *dt = (DataType *) value;
if (dt->strval) RedisModule_FreeString(NULL, dt->strval);
RedisModule_Free(dt);
}
}
static int datatype_set(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
if (argc != 4) {
RedisModule_WrongArity(ctx);
return REDISMODULE_OK;
}
long long intval;
if (RedisModule_StringToLongLong(argv[2], &intval) != REDISMODULE_OK) {
RedisModule_ReplyWithError(ctx, "Invalid integr value");
return REDISMODULE_OK;
}
RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE);
DataType *dt = RedisModule_Calloc(sizeof(DataType), 1);
dt->intval = intval;
dt->strval = argv[3];
RedisModule_RetainString(ctx, dt->strval);
RedisModule_ModuleTypeSetValue(key, datatype, dt);
RedisModule_CloseKey(key);
RedisModule_ReplyWithSimpleString(ctx, "OK");
return REDISMODULE_OK;
}
static int datatype_restore(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
if (argc != 3) {
RedisModule_WrongArity(ctx);
return REDISMODULE_OK;
}
DataType *dt = RedisModule_LoadDataTypeFromString(argv[2], datatype);
if (!dt) {
RedisModule_ReplyWithError(ctx, "Invalid data");
return REDISMODULE_OK;
}
RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE);
RedisModule_ModuleTypeSetValue(key, datatype, dt);
RedisModule_CloseKey(key);
RedisModule_ReplyWithSimpleString(ctx, "OK");
return REDISMODULE_OK;
}
static int datatype_get(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
if (argc != 2) {
RedisModule_WrongArity(ctx);
return REDISMODULE_OK;
}
RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ);
DataType *dt = RedisModule_ModuleTypeGetValue(key);
RedisModule_CloseKey(key);
RedisModule_ReplyWithArray(ctx, 2);
RedisModule_ReplyWithLongLong(ctx, dt->intval);
RedisModule_ReplyWithString(ctx, dt->strval);
return REDISMODULE_OK;
}
static int datatype_dump(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
if (argc != 2) {
RedisModule_WrongArity(ctx);
return REDISMODULE_OK;
}
RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ);
DataType *dt = RedisModule_ModuleTypeGetValue(key);
RedisModule_CloseKey(key);
RedisModuleString *reply = RedisModule_SaveDataTypeToString(ctx, dt, datatype);
if (!reply) {
RedisModule_ReplyWithError(ctx, "Failed to save");
return REDISMODULE_OK;
}
RedisModule_ReplyWithString(ctx, reply);
RedisModule_FreeString(ctx, reply);
return REDISMODULE_OK;
}
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
REDISMODULE_NOT_USED(argv);
REDISMODULE_NOT_USED(argc);
if (RedisModule_Init(ctx,"datatype",1,REDISMODULE_APIVER_1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
RedisModule_SetModuleOptions(ctx, REDISMODULE_OPTIONS_HANDLE_IO_ERRORS);
RedisModuleTypeMethods datatype_methods = {
.version = REDISMODULE_TYPE_METHOD_VERSION,
.rdb_load = datatype_load,
.rdb_save = datatype_save,
.free = datatype_free,
};
datatype = RedisModule_CreateDataType(ctx, "test___dt", 1, &datatype_methods);
if (datatype == NULL)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"datatype.set", datatype_set,"deny-oom",1,1,1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"datatype.get", datatype_get,"",1,1,1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"datatype.restore", datatype_restore,"deny-oom",1,1,1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"datatype.dump", datatype_dump,"",1,1,1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
return REDISMODULE_OK;
}
......@@ -6,6 +6,8 @@
#include <unistd.h>
#include <errno.h>
#define UNUSED(x) (void)(x)
int test_call_generic(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{
if (argc<2) {
......@@ -40,6 +42,45 @@ int test_call_info(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
return REDISMODULE_OK;
}
int test_ld_conv(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
UNUSED(argv);
UNUSED(argc);
long double ld = 0.00000000000000001L;
const char *ldstr = "0.00000000000000001";
RedisModuleString *s1 = RedisModule_CreateStringFromLongDouble(ctx, ld, 1);
RedisModuleString *s2 =
RedisModule_CreateString(ctx, ldstr, strlen(ldstr));
if (RedisModule_StringCompare(s1, s2) != 0) {
char err[4096];
snprintf(err, 4096,
"Failed to convert long double to string ('%s' != '%s')",
RedisModule_StringPtrLen(s1, NULL),
RedisModule_StringPtrLen(s2, NULL));
RedisModule_ReplyWithError(ctx, err);
goto final;
}
long double ld2 = 0;
if (RedisModule_StringToLongDouble(s2, &ld2) == REDISMODULE_ERR) {
RedisModule_ReplyWithError(ctx,
"Failed to convert string to long double");
goto final;
}
if (ld2 != ld) {
char err[4096];
snprintf(err, 4096,
"Failed to convert string to long double (%.40Lf != %.40Lf)",
ld2,
ld);
RedisModule_ReplyWithError(ctx, err);
goto final;
}
RedisModule_ReplyWithLongDouble(ctx, ld2);
final:
RedisModule_FreeString(ctx, s1);
RedisModule_FreeString(ctx, s2);
return REDISMODULE_OK;
}
int test_flushall(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{
REDISMODULE_NOT_USED(argv);
......@@ -68,16 +109,24 @@ int test_randomkey(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
return REDISMODULE_OK;
}
RedisModuleKey *open_key_or_reply(RedisModuleCtx *ctx, RedisModuleString *keyname, int mode) {
RedisModuleKey *key = RedisModule_OpenKey(ctx, keyname, mode);
if (!key) {
RedisModule_ReplyWithError(ctx, "key not found");
return NULL;
}
return key;
}
int test_getlru(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{
if (argc<2) {
RedisModule_WrongArity(ctx);
return REDISMODULE_OK;
}
RedisModuleString *keyname = argv[1];
RedisModuleKey *key = RedisModule_OpenKey(ctx, keyname, REDISMODULE_READ|REDISMODULE_OPEN_KEY_NOTOUCH);
long long lru, lfu;
RedisModule_GetLRUOrLFU(key, &lfu, &lru);
RedisModuleKey *key = open_key_or_reply(ctx, argv[1], REDISMODULE_READ|REDISMODULE_OPEN_KEY_NOTOUCH);
mstime_t lru;
RedisModule_GetLRU(key, &lru);
RedisModule_ReplyWithLongLong(ctx, lru);
RedisModule_CloseKey(key);
return REDISMODULE_OK;
......@@ -89,12 +138,46 @@ int test_setlru(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
RedisModule_WrongArity(ctx);
return REDISMODULE_OK;
}
RedisModuleString *keyname = argv[1];
RedisModuleKey *key = RedisModule_OpenKey(ctx, keyname, REDISMODULE_WRITE|REDISMODULE_OPEN_KEY_NOTOUCH);
long long lru;
RedisModule_StringToLongLong(argv[2], &lru);
RedisModule_SetLRUOrLFU(key, -1, lru);
RedisModule_ReplyWithCString(ctx, "Ok");
RedisModuleKey *key = open_key_or_reply(ctx, argv[1], REDISMODULE_READ|REDISMODULE_OPEN_KEY_NOTOUCH);
mstime_t lru;
if (RedisModule_StringToLongLong(argv[2], &lru) != REDISMODULE_OK) {
RedisModule_ReplyWithError(ctx, "invalid idle time");
return REDISMODULE_OK;
}
int was_set = RedisModule_SetLRU(key, lru)==REDISMODULE_OK;
RedisModule_ReplyWithLongLong(ctx, was_set);
RedisModule_CloseKey(key);
return REDISMODULE_OK;
}
int test_getlfu(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{
if (argc<2) {
RedisModule_WrongArity(ctx);
return REDISMODULE_OK;
}
RedisModuleKey *key = open_key_or_reply(ctx, argv[1], REDISMODULE_READ|REDISMODULE_OPEN_KEY_NOTOUCH);
mstime_t lfu;
RedisModule_GetLFU(key, &lfu);
RedisModule_ReplyWithLongLong(ctx, lfu);
RedisModule_CloseKey(key);
return REDISMODULE_OK;
}
int test_setlfu(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{
if (argc<3) {
RedisModule_WrongArity(ctx);
return REDISMODULE_OK;
}
RedisModuleKey *key = open_key_or_reply(ctx, argv[1], REDISMODULE_READ|REDISMODULE_OPEN_KEY_NOTOUCH);
mstime_t lfu;
if (RedisModule_StringToLongLong(argv[2], &lfu) != REDISMODULE_OK) {
RedisModule_ReplyWithError(ctx, "invalid freq");
return REDISMODULE_OK;
}
int was_set = RedisModule_SetLFU(key, lfu)==REDISMODULE_OK;
RedisModule_ReplyWithLongLong(ctx, was_set);
RedisModule_CloseKey(key);
return REDISMODULE_OK;
}
......@@ -109,6 +192,8 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.call_info", test_call_info,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.ld_conversion", test_ld_conv, "",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.flushall", test_flushall,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.dbsize", test_dbsize,"",0,0,0) == REDISMODULE_ERR)
......@@ -119,6 +204,10 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.getlru", test_getlru,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.setlfu", test_setlfu,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.getlfu", test_getlfu,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
return REDISMODULE_OK;
}
#include "redismodule.h"
#include <string.h>
#include <assert.h>
#include <unistd.h>
typedef struct {
size_t nkeys;
} scan_strings_pd;
void scan_strings_callback(RedisModuleCtx *ctx, RedisModuleString* keyname, RedisModuleKey* key, void *privdata) {
scan_strings_pd* pd = privdata;
int was_opened = 0;
if (!key) {
key = RedisModule_OpenKey(ctx, keyname, REDISMODULE_READ);
was_opened = 1;
}
if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_STRING) {
size_t len;
char * data = RedisModule_StringDMA(key, &len, REDISMODULE_READ);
RedisModule_ReplyWithArray(ctx, 2);
RedisModule_ReplyWithString(ctx, keyname);
RedisModule_ReplyWithStringBuffer(ctx, data, len);
pd->nkeys++;
}
if (was_opened)
RedisModule_CloseKey(key);
}
int scan_strings(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{
REDISMODULE_NOT_USED(argv);
REDISMODULE_NOT_USED(argc);
scan_strings_pd pd = {
.nkeys = 0,
};
RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN);
RedisModuleScanCursor* cursor = RedisModule_ScanCursorCreate();
while(RedisModule_Scan(ctx, cursor, scan_strings_callback, &pd));
RedisModule_ScanCursorDestroy(cursor);
RedisModule_ReplySetArrayLength(ctx, pd.nkeys);
return REDISMODULE_OK;
}
typedef struct {
RedisModuleCtx *ctx;
size_t nreplies;
} scan_key_pd;
void scan_key_callback(RedisModuleKey *key, RedisModuleString* field, RedisModuleString* value, void *privdata) {
REDISMODULE_NOT_USED(key);
scan_key_pd* pd = privdata;
RedisModule_ReplyWithArray(pd->ctx, 2);
RedisModule_ReplyWithString(pd->ctx, field);
if (value)
RedisModule_ReplyWithString(pd->ctx, value);
else
RedisModule_ReplyWithNull(pd->ctx);
pd->nreplies++;
}
int scan_key(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{
if (argc != 2) {
RedisModule_WrongArity(ctx);
return REDISMODULE_OK;
}
scan_key_pd pd = {
.ctx = ctx,
.nreplies = 0,
};
RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ);
if (!key) {
RedisModule_ReplyWithError(ctx, "not found");
return REDISMODULE_OK;
}
RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN);
RedisModuleScanCursor* cursor = RedisModule_ScanCursorCreate();
while(RedisModule_ScanKey(key, cursor, scan_key_callback, &pd));
RedisModule_ScanCursorDestroy(cursor);
RedisModule_ReplySetArrayLength(ctx, pd.nreplies);
RedisModule_CloseKey(key);
return REDISMODULE_OK;
}
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
REDISMODULE_NOT_USED(argv);
REDISMODULE_NOT_USED(argc);
if (RedisModule_Init(ctx, "scan", 1, REDISMODULE_APIVER_1)== REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx, "scan.scan_strings", scan_strings, "", 0, 0, 0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx, "scan.scan_key", scan_key, "", 0, 0, 0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
return REDISMODULE_OK;
}
......@@ -18,8 +18,11 @@ void *testrdb_type_load(RedisModuleIO *rdb, int encver) {
RedisModuleString *str = RedisModule_LoadString(rdb);
float f = RedisModule_LoadFloat(rdb);
long double ld = RedisModule_LoadLongDouble(rdb);
if (RedisModule_IsIOError(rdb))
if (RedisModule_IsIOError(rdb)) {
RedisModuleCtx *ctx = RedisModule_GetContextFromIO(rdb);
RedisModule_FreeString(ctx, str);
return NULL;
}
/* Using the values only after checking for io errors. */
assert(count==1);
assert(encver==1);
......
......@@ -11,28 +11,55 @@ proc fail {msg} {
proc assert {condition} {
if {![uplevel 1 [list expr $condition]]} {
error "assertion:Expected condition '$condition' to be true ([uplevel 1 [list subst -nocommands $condition]])"
set context "(context: [info frame -1])"
error "assertion:Expected [uplevel 1 [list subst -nocommands $condition]] $context"
}
}
proc assert_no_match {pattern value} {
if {[string match $pattern $value]} {
error "assertion:Expected '$value' to not match '$pattern'"
set context "(context: [info frame -1])"
error "assertion:Expected '$value' to not match '$pattern' $context"
}
}
proc assert_match {pattern value} {
if {![string match $pattern $value]} {
error "assertion:Expected '$value' to match '$pattern'"
set context "(context: [info frame -1])"
error "assertion:Expected '$value' to match '$pattern' $context"
}
}
proc assert_equal {expected value {detail ""}} {
proc assert_equal {value expected {detail ""}} {
if {$expected ne $value} {
if {$detail ne ""} {
set detail " (detail: $detail)"
set detail "(detail: $detail)"
} else {
set detail "(context: [info frame -1])"
}
error "assertion:Expected '$value' to be equal to '$expected' $detail"
}
}
proc assert_lessthan {value expected {detail ""}} {
if {!($value < $expected)} {
if {$detail ne ""} {
set detail "(detail: $detail)"
} else {
set detail "(context: [info frame -1])"
}
error "assertion:Expected '$value' to be lessthan to '$expected' $detail"
}
}
proc assert_range {value min max {detail ""}} {
if {!($value <= $max && $value >= $min)} {
if {$detail ne ""} {
set detail "(detail: $detail)"
} else {
set detail "(context: [info frame -1])"
}
error "assertion:Expected '$value' to be equal to '$expected'$detail"
error "assertion:Expected '$value' to be between to '$min' and '$max' $detail"
}
}
......
set testmodule [file normalize tests/modules/datatype.so]
start_server {tags {"modules"}} {
r module load $testmodule
test {DataType: Test module is sane, GET/SET work.} {
r datatype.set dtkey 100 stringval
assert {[r datatype.get dtkey] eq {100 stringval}}
}
test {DataType: RM_SaveDataTypeToString(), RM_LoadDataTypeFromString() work} {
r datatype.set dtkey -1111 MyString
set encoded [r datatype.dump dtkey]
r datatype.restore dtkeycopy $encoded
assert {[r datatype.get dtkeycopy] eq {-1111 MyString}}
}
test {DataType: Handle truncated RM_LoadDataTypeFromString()} {
r datatype.set dtkey -1111 MyString
set encoded [r datatype.dump dtkey]
set truncated [string range $encoded 0 end-1]
catch {r datatype.restore dtkeycopy $truncated} e
set e
} {*Invalid*}
}
......@@ -16,6 +16,11 @@ start_server {tags {"modules"}} {
assert { [string match "*cmdstat_module*" $info] }
}
test {test long double conversions} {
set ld [r test.ld_conversion]
assert {[string match $ld "0.00000000000000001"]}
}
test {test module db commands} {
r set x foo
set key [r test.randomkey]
......@@ -26,13 +31,40 @@ start_server {tags {"modules"}} {
}
test {test modle lru api} {
r config set maxmemory-policy allkeys-lru
r set x foo
set lru [r test.getlru x]
assert { $lru <= 1 }
r test.setlru x 100
assert { $lru <= 1000 }
set was_set [r test.setlru x 100000]
assert { $was_set == 1 }
set idle [r object idletime x]
assert { $idle >= 100 }
set lru [r test.getlru x]
assert { $lru >= 100 }
assert { $lru >= 100000 }
r config set maxmemory-policy allkeys-lfu
set lru [r test.getlru x]
assert { $lru == -1 }
set was_set [r test.setlru x 100000]
assert { $was_set == 0 }
}
r config set maxmemory-policy allkeys-lru
test {test modle lfu api} {
r config set maxmemory-policy allkeys-lfu
r set x foo
set lfu [r test.getlfu x]
assert { $lfu >= 1 }
set was_set [r test.setlfu x 100]
assert { $was_set == 1 }
set freq [r object freq x]
assert { $freq <= 100 }
set lfu [r test.getlfu x]
assert { $lfu <= 100 }
r config set maxmemory-policy allkeys-lru
set lfu [r test.getlfu x]
assert { $lfu == -1 }
set was_set [r test.setlfu x 100]
assert { $was_set == 0 }
}
}
set testmodule [file normalize tests/modules/scan.so]
start_server {tags {"modules"}} {
r module load $testmodule
test {Module scan keyspace} {
# the module create a scan command with filtering which also return values
r set x 1
r set y 2
r set z 3
r hset h f v
lsort [r scan.scan_strings]
} {{x 1} {y 2} {z 3}}
test {Module scan hash ziplist} {
r hmset hh f1 v1 f2 v2
lsort [r scan.scan_key hh]
} {{f1 v1} {f2 v2}}
test {Module scan hash dict} {
r config set hash-max-ziplist-entries 2
r hmset hh f3 v3
lsort [r scan.scan_key hh]
} {{f1 v1} {f2 v2} {f3 v3}}
test {Module scan zset ziplist} {
r zadd zz 1 f1 2 f2
lsort [r scan.scan_key zz]
} {{f1 1} {f2 2}}
test {Module scan zset dict} {
r config set zset-max-ziplist-entries 2
r zadd zz 3 f3
lsort [r scan.scan_key zz]
} {{f1 1} {f2 2} {f3 3}}
test {Module scan set intset} {
r sadd ss 1 2
lsort [r scan.scan_key ss]
} {{1 {}} {2 {}}}
test {Module scan set dict} {
r config set set-max-intset-entries 2
r sadd ss 3
lsort [r scan.scan_key ss]
} {{1 {}} {2 {}} {3 {}}}
}
......@@ -536,7 +536,7 @@ foreach cmdrepl {0 1} {
start_server {tags {"scripting repl"}} {
start_server {} {
if {$cmdrepl == 1} {
set rt "(commmands replication)"
set rt "(commands replication)"
} else {
set rt "(scripts replication)"
r debug lua-always-replicate-commands 1
......
......@@ -123,6 +123,12 @@ start_server {
assert {[r xlen mystream] == $j}
}
test {XADD with ID 0-0} {
r DEL otherstream
catch {r XADD otherstream 0-0 k v} err
assert {[r EXISTS otherstream] == 0}
}
test {XRANGE COUNT works as expected} {
assert {[llength [r xrange mystream - + COUNT 10]] == 10}
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment