Commit a83e3663 authored by Oran Agra's avatar Oran Agra
Browse files

Merge remote-tracking branch 'origin/unstable' into 7.0

parents d5915a16 5860fa3d
......@@ -46,6 +46,12 @@ start_server {tags {"repl external:skip"}} {
set master_port [srv -1 port]
set slave [srv 0 client]
# Load some functions to be used later
$master FUNCTION load lua test replace {
redis.register_function{function_name='f_default_flags', callback=function(keys, args) return redis.call('get',keys[1]) end, flags={}}
redis.register_function{function_name='f_no_writes', callback=function(keys, args) return redis.call('get',keys[1]) end, flags={'no-writes'}}
}
test {First server should have role slave after SLAVEOF} {
$slave slaveof $master_host $master_port
wait_replica_online $master
......@@ -54,28 +60,46 @@ start_server {tags {"repl external:skip"}} {
test {With min-slaves-to-write (1,3): master should be writable} {
$master config set min-slaves-max-lag 3
$master config set min-slaves-to-write 1
$master set foo bar
} {OK}
assert_equal OK [$master set foo 123]
assert_equal OK [$master eval "return redis.call('set','foo',12345)" 0]
}
test {With min-slaves-to-write (2,3): master should not be writable} {
$master config set min-slaves-max-lag 3
$master config set min-slaves-to-write 2
catch {$master set foo bar} e
set e
} {NOREPLICAS*}
assert_error "*NOREPLICAS*" {$master set foo bar}
assert_error "*NOREPLICAS*" {$master eval "redis.call('set','foo','bar')" 0}
}
test {With min-slaves-to-write function without no-write flag} {
assert_error "*NOREPLICAS*" {$master fcall f_default_flags 1 foo}
assert_equal "12345" [$master fcall f_no_writes 1 foo]
}
test {With not enough good slaves, read in Lua script is still accepted} {
$master config set min-slaves-max-lag 3
$master config set min-slaves-to-write 1
$master eval "redis.call('set','foo','bar')" 0
$master config set min-slaves-to-write 2
$master eval "return redis.call('get','foo')" 0
} {bar}
test {With min-slaves-to-write: master not writable with lagged slave} {
$master config set min-slaves-max-lag 2
$master config set min-slaves-to-write 1
assert {[$master set foo bar] eq {OK}}
assert_equal OK [$master set foo 123]
assert_equal OK [$master eval "return redis.call('set','foo',12345)" 0]
# Killing a slave to make it become a lagged slave.
exec kill -SIGSTOP [srv 0 pid]
# Waiting for slave kill.
wait_for_condition 100 100 {
[catch {$master set foo bar}] != 0
[catch {$master set foo 123}] != 0
} else {
fail "Master didn't become readonly"
}
catch {$master set foo bar} err
assert_match {NOREPLICAS*} $err
assert_error "*NOREPLICAS*" {$master set foo 123}
assert_error "*NOREPLICAS*" {$master eval "return redis.call('set','foo',12345)" 0}
exec kill -SIGCONT [srv 0 pid]
}
}
......
......@@ -318,7 +318,7 @@ foreach mdl {no yes} {
stop_write_load $load_handle4
# Make sure no more commands processed
wait_load_handlers_disconnected
wait_load_handlers_disconnected -3
wait_for_ofs_sync $master [lindex $slaves 0]
wait_for_ofs_sync $master [lindex $slaves 1]
......@@ -1233,3 +1233,89 @@ test {replica can handle EINTR if use diskless load} {
}
}
} {} {external:skip}
start_server {tags {"repl" "external:skip"}} {
test "replica do not write the reply to the replication link - SYNC (_addReplyToBufferOrList)" {
set rd [redis_deferring_client]
set lines [count_log_lines 0]
$rd sync
$rd ping
catch {$rd read} e
if {$::verbose} { puts "SYNC _addReplyToBufferOrList: $e" }
assert_equal "PONG" [r ping]
# Check we got the warning logs about the PING command.
verify_log_message 0 "*Replica generated a reply to command 'ping', disconnecting it: *" $lines
$rd close
catch {exec kill -9 [get_child_pid 0]}
waitForBgsave r
}
test "replica do not write the reply to the replication link - SYNC (addReplyDeferredLen)" {
set rd [redis_deferring_client]
set lines [count_log_lines 0]
$rd sync
$rd xinfo help
catch {$rd read} e
if {$::verbose} { puts "SYNC addReplyDeferredLen: $e" }
assert_equal "PONG" [r ping]
# Check we got the warning logs about the XINFO HELP command.
verify_log_message 0 "*Replica generated a reply to command 'xinfo|help', disconnecting it: *" $lines
$rd close
catch {exec kill -9 [get_child_pid 0]}
waitForBgsave r
}
test "replica do not write the reply to the replication link - PSYNC (_addReplyToBufferOrList)" {
set rd [redis_deferring_client]
set lines [count_log_lines 0]
$rd psync replicationid -1
assert_match {FULLRESYNC * 0} [$rd read]
$rd get foo
catch {$rd read} e
if {$::verbose} { puts "PSYNC _addReplyToBufferOrList: $e" }
assert_equal "PONG" [r ping]
# Check we got the warning logs about the GET command.
verify_log_message 0 "*Replica generated a reply to command 'get', disconnecting it: *" $lines
verify_log_message 0 "*== CRITICAL == This master is sending an error to its replica: *" $lines
verify_log_message 0 "*Replica can't interact with the keyspace*" $lines
$rd close
catch {exec kill -9 [get_child_pid 0]}
waitForBgsave r
}
test "replica do not write the reply to the replication link - PSYNC (addReplyDeferredLen)" {
set rd [redis_deferring_client]
set lines [count_log_lines 0]
$rd psync replicationid -1
assert_match {FULLRESYNC * 0} [$rd read]
$rd slowlog get
catch {$rd read} e
if {$::verbose} { puts "PSYNC addReplyDeferredLen: $e" }
assert_equal "PONG" [r ping]
# Check we got the warning logs about the SLOWLOG GET command.
verify_log_message 0 "*Replica generated a reply to command 'slowlog|get', disconnecting it: *" $lines
$rd close
catch {exec kill -9 [get_child_pid 0]}
waitForBgsave r
}
test "PSYNC with wrong offset should throw error" {
# It used to accept the FULL SYNC, but also replied with an error.
assert_error {ERR value is not an integer or out of range} {r psync replicationid offset_str}
set logs [exec tail -n 100 < [srv 0 stdout]]
assert_match {*Replica * asks for synchronization but with a wrong offset} $logs
assert_equal "PONG" [r ping]
}
}
......@@ -2,11 +2,12 @@
# find the OS
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
warning_cflags = -W -Wall -Wno-missing-field-initializers
ifeq ($(uname_S),Darwin)
SHOBJ_CFLAGS ?= -W -Wall -dynamic -fno-common -g -ggdb -std=c99 -O2
SHOBJ_CFLAGS ?= $(warning_cflags) -dynamic -fno-common -g -ggdb -std=c99 -O2
SHOBJ_LDFLAGS ?= -bundle -undefined dynamic_lookup
else # Linux, others
SHOBJ_CFLAGS ?= -W -Wall -fno-common -g -ggdb -std=c99 -O2
SHOBJ_CFLAGS ?= $(warning_cflags) -fno-common -g -ggdb -std=c99 -O2
SHOBJ_LDFLAGS ?= -shared
endif
......@@ -40,6 +41,7 @@ TEST_MODULES = \
keyspace_events.so \
blockedclient.so \
getkeys.so \
getchannels.so \
test_lazyfree.so \
timer.so \
defragtest.so \
......@@ -51,6 +53,7 @@ TEST_MODULES = \
list.so \
subcommands.so \
reply.so \
cmdintrospection.so \
eventloop.so
.PHONY: all
......
......@@ -15,11 +15,13 @@ int set_aclcheck_key(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
const char *flags = RedisModule_StringPtrLen(argv[1], NULL);
if (!strcasecmp(flags, "W")) {
permissions = REDISMODULE_KEY_PERMISSION_WRITE;
permissions = REDISMODULE_CMD_KEY_UPDATE;
} else if (!strcasecmp(flags, "R")) {
permissions = REDISMODULE_KEY_PERMISSION_READ;
permissions = REDISMODULE_CMD_KEY_ACCESS;
} else if (!strcasecmp(flags, "*")) {
permissions = REDISMODULE_KEY_PERMISSION_ALL;
permissions = REDISMODULE_CMD_KEY_UPDATE | REDISMODULE_CMD_KEY_ACCESS;
} else if (!strcasecmp(flags, "~")) {
permissions = 0; /* Requires either read or write */
} else {
RedisModule_ReplyWithError(ctx, "INVALID FLAGS");
return REDISMODULE_OK;
......@@ -58,7 +60,7 @@ int publish_aclcheck_channel(RedisModuleCtx *ctx, RedisModuleString **argv, int
/* Check that the pubsub channel can be accessed */
RedisModuleString *user_name = RedisModule_GetCurrentUserName(ctx);
RedisModuleUser *user = RedisModule_GetModuleUserFromUserName(user_name);
int ret = RedisModule_ACLCheckChannelPermissions(user, argv[1], 1);
int ret = RedisModule_ACLCheckChannelPermissions(user, argv[1], REDISMODULE_CMD_CHANNEL_SUBSCRIBE);
if (ret != 0) {
RedisModule_ReplyWithError(ctx, "DENIED CHANNEL");
RedisModule_FreeModuleUser(user);
......
#include "redismodule.h"
#define UNUSED(V) ((void) V)
int cmd_xadd(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
UNUSED(argv);
UNUSED(argc);
RedisModule_ReplyWithSimpleString(ctx, "OK");
return REDISMODULE_OK;
}
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
REDISMODULE_NOT_USED(argv);
REDISMODULE_NOT_USED(argc);
if (RedisModule_Init(ctx, "cmdintrospection", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"cmdintrospection.xadd",cmd_xadd,"write deny-oom random fast",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
RedisModuleCommand *xadd = RedisModule_GetCommand(ctx,"cmdintrospection.xadd");
RedisModuleCommandInfo info = {
.version = REDISMODULE_COMMAND_INFO_VERSION,
.arity = -5,
.summary = "Appends a new entry to a stream",
.since = "5.0.0",
.complexity = "O(1) when adding a new entry, O(N) when trimming where N being the number of entries evicted.",
.tips = "nondeterministic_output",
.history = (RedisModuleCommandHistoryEntry[]){
/* NOTE: All versions specified should be the module's versions, not
* Redis'! We use Redis versions in this example for the purpose of
* testing (comparing the output with the output of the vanilla
* XADD). */
{"6.2.0", "Added the `NOMKSTREAM` option, `MINID` trimming strategy and the `LIMIT` option."},
{"7.0.0", "Added support for the `<ms>-*` explicit ID form."},
{0}
},
.key_specs = (RedisModuleCommandKeySpec[]){
{
.notes = "UPDATE instead of INSERT because of the optional trimming feature",
.flags = REDISMODULE_CMD_KEY_RW | REDISMODULE_CMD_KEY_UPDATE,
.begin_search_type = REDISMODULE_KSPEC_BS_INDEX,
.bs.index.pos = 1,
.find_keys_type = REDISMODULE_KSPEC_FK_RANGE,
.fk.range = {0,1,0}
},
{0}
},
.args = (RedisModuleCommandArg[]){
{
.name = "key",
.type = REDISMODULE_ARG_TYPE_KEY,
.key_spec_index = 0
},
{
.name = "nomkstream",
.type = REDISMODULE_ARG_TYPE_PURE_TOKEN,
.token = "NOMKSTREAM",
.since = "6.2.0",
.flags = REDISMODULE_CMD_ARG_OPTIONAL
},
{
.name = "trim",
.type = REDISMODULE_ARG_TYPE_BLOCK,
.flags = REDISMODULE_CMD_ARG_OPTIONAL,
.subargs = (RedisModuleCommandArg[]){
{
.name = "strategy",
.type = REDISMODULE_ARG_TYPE_ONEOF,
.subargs = (RedisModuleCommandArg[]){
{
.name = "maxlen",
.type = REDISMODULE_ARG_TYPE_PURE_TOKEN,
.token = "MAXLEN",
},
{
.name = "minid",
.type = REDISMODULE_ARG_TYPE_PURE_TOKEN,
.token = "MINID",
.since = "6.2.0",
},
{0}
}
},
{
.name = "operator",
.type = REDISMODULE_ARG_TYPE_ONEOF,
.flags = REDISMODULE_CMD_ARG_OPTIONAL,
.subargs = (RedisModuleCommandArg[]){
{
.name = "equal",
.type = REDISMODULE_ARG_TYPE_PURE_TOKEN,
.token = "="
},
{
.name = "approximately",
.type = REDISMODULE_ARG_TYPE_PURE_TOKEN,
.token = "~"
},
{0}
}
},
{
.name = "threshold",
.type = REDISMODULE_ARG_TYPE_STRING,
},
{
.name = "count",
.type = REDISMODULE_ARG_TYPE_INTEGER,
.token = "LIMIT",
.since = "6.2.0",
.flags = REDISMODULE_CMD_ARG_OPTIONAL
},
{0}
}
},
{
.name = "id_or_auto",
.type = REDISMODULE_ARG_TYPE_ONEOF,
.subargs = (RedisModuleCommandArg[]){
{
.name = "auto_id",
.type = REDISMODULE_ARG_TYPE_PURE_TOKEN,
.token = "*"
},
{
.name = "id",
.type = REDISMODULE_ARG_TYPE_STRING,
},
{0}
}
},
{
.name = "field_value",
.type = REDISMODULE_ARG_TYPE_BLOCK,
.flags = REDISMODULE_CMD_ARG_MULTIPLE,
.subargs = (RedisModuleCommandArg[]){
{
.name = "field",
.type = REDISMODULE_ARG_TYPE_STRING,
},
{
.name = "value",
.type = REDISMODULE_ARG_TYPE_STRING,
},
{0}
}
},
{0}
}
};
if (RedisModule_SetCommandInfo(xadd, &info) == REDISMODULE_ERR)
return REDISMODULE_ERR;
return REDISMODULE_OK;
}
#include "redismodule.h"
#include <strings.h>
#include <assert.h>
#include <unistd.h>
#include <errno.h>
/* A sample with declarable channels, that are used to validate against ACLs */
int getChannels_subscribe(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
if ((argc - 1) % 3 != 0) {
RedisModule_WrongArity(ctx);
return REDISMODULE_OK;
}
char *err = NULL;
/* getchannels.command [[subscribe|unsubscribe|publish] [pattern|literal] <channel> ...]
* This command marks the given channel is accessed based on the
* provided modifiers. */
for (int i = 1; i < argc; i += 3) {
const char *operation = RedisModule_StringPtrLen(argv[i], NULL);
const char *type = RedisModule_StringPtrLen(argv[i+1], NULL);
int flags = 0;
if (!strcasecmp(operation, "subscribe")) {
flags |= REDISMODULE_CMD_CHANNEL_SUBSCRIBE;
} else if (!strcasecmp(operation, "unsubscribe")) {
flags |= REDISMODULE_CMD_CHANNEL_UNSUBSCRIBE;
} else if (!strcasecmp(operation, "publish")) {
flags |= REDISMODULE_CMD_CHANNEL_PUBLISH;
} else {
err = "Invalid channel operation";
break;
}
if (!strcasecmp(type, "literal")) {
/* No op */
} else if (!strcasecmp(type, "pattern")) {
flags |= REDISMODULE_CMD_CHANNEL_PATTERN;
} else {
err = "Invalid channel type";
break;
}
if (RedisModule_IsChannelsPositionRequest(ctx)) {
RedisModule_ChannelAtPosWithFlags(ctx, i+2, flags);
}
}
if (!RedisModule_IsChannelsPositionRequest(ctx)) {
if (err) {
RedisModule_ReplyWithError(ctx, err);
} else {
/* Normal implementation would go here, but for tests just return okay */
RedisModule_ReplyWithSimpleString(ctx, "OK");
}
}
return REDISMODULE_OK;
}
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
REDISMODULE_NOT_USED(argv);
REDISMODULE_NOT_USED(argc);
if (RedisModule_Init(ctx, "getchannels", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx, "getchannels.command", getChannels_subscribe, "getchannels-api", 0, 0, 0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
return REDISMODULE_OK;
}
......@@ -44,6 +44,40 @@ int getkeys_command(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
return REDISMODULE_OK;
}
int getkeys_command_with_flags(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{
int i;
int count = 0;
/* Handle getkeys-api introspection */
if (RedisModule_IsKeysPositionRequest(ctx)) {
for (i = 0; i < argc; i++) {
size_t len;
const char *str = RedisModule_StringPtrLen(argv[i], &len);
if (len == 3 && !strncasecmp(str, "key", 3) && i + 1 < argc)
RedisModule_KeyAtPosWithFlags(ctx, i + 1, REDISMODULE_CMD_KEY_RO | REDISMODULE_CMD_KEY_ACCESS);
}
return REDISMODULE_OK;
}
/* Handle real command invocation */
RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_LEN);
for (i = 0; i < argc; i++) {
size_t len;
const char *str = RedisModule_StringPtrLen(argv[i], &len);
if (len == 3 && !strncasecmp(str, "key", 3) && i + 1 < argc) {
RedisModule_ReplyWithString(ctx, argv[i+1]);
count++;
}
}
RedisModule_ReplySetArrayLength(ctx, count);
return REDISMODULE_OK;
}
int getkeys_fixed(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{
int i;
......@@ -57,19 +91,22 @@ int getkeys_fixed(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
/* Introspect a command using RM_GetCommandKeys() and returns the list
* of keys. Essentially this is COMMAND GETKEYS implemented in a module.
* INTROSPECT <with-flags> <cmd> <args>
*/
int getkeys_introspect(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{
UNUSED(argv);
UNUSED(argc);
long long with_flags = 0;
if (argc < 3) {
if (argc < 4) {
RedisModule_WrongArity(ctx);
return REDISMODULE_OK;
}
int num_keys;
int *keyidx = RedisModule_GetCommandKeys(ctx, &argv[1], argc - 1, &num_keys);
if (RedisModule_StringToLongLong(argv[1],&with_flags) != REDISMODULE_OK)
return RedisModule_ReplyWithError(ctx,"ERR invalid integer");
int num_keys, *keyflags = NULL;
int *keyidx = RedisModule_GetCommandKeysWithFlags(ctx, &argv[2], argc - 2, &num_keys, with_flags ? &keyflags : NULL);
if (!keyidx) {
if (!errno)
......@@ -93,10 +130,27 @@ int getkeys_introspect(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
int i;
RedisModule_ReplyWithArray(ctx, num_keys);
for (i = 0; i < num_keys; i++)
RedisModule_ReplyWithString(ctx, argv[1 + keyidx[i]]);
for (i = 0; i < num_keys; i++) {
if (!with_flags) {
RedisModule_ReplyWithString(ctx, argv[2 + keyidx[i]]);
continue;
}
RedisModule_ReplyWithArray(ctx, 2);
RedisModule_ReplyWithString(ctx, argv[2 + keyidx[i]]);
char* sflags = "";
if (keyflags[i] & REDISMODULE_CMD_KEY_RO)
sflags = "RO";
else if (keyflags[i] & REDISMODULE_CMD_KEY_RW)
sflags = "RW";
else if (keyflags[i] & REDISMODULE_CMD_KEY_OW)
sflags = "OW";
else if (keyflags[i] & REDISMODULE_CMD_KEY_RM)
sflags = "RM";
RedisModule_ReplyWithCString(ctx, sflags);
}
RedisModule_Free(keyidx);
RedisModule_Free(keyflags);
}
return REDISMODULE_OK;
......@@ -111,6 +165,9 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
if (RedisModule_CreateCommand(ctx,"getkeys.command", getkeys_command,"getkeys-api",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"getkeys.command_with_flags", getkeys_command_with_flags,"getkeys-api",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"getkeys.fixed", getkeys_fixed,"",2,4,1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
......
#include "redismodule.h"
#ifdef INCLUDE_UNRELEASED_KEYSPEC_API
#define UNUSED(V) ((void) V)
int kspec_legacy(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
/* This function implements all commands in this module. All we care about is
* the COMMAND metadata anyway. */
int kspec_impl(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
UNUSED(argv);
UNUSED(argc);
RedisModule_ReplyWithSimpleString(ctx, "OK");
return REDISMODULE_OK;
}
int kspec_complex1(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
UNUSED(argv);
UNUSED(argc);
RedisModule_ReplyWithSimpleString(ctx, "OK");
return REDISMODULE_OK;
}
int kspec_complex2(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
UNUSED(argv);
UNUSED(argc);
RedisModule_ReplyWithSimpleString(ctx, "OK");
int createKspecNone(RedisModuleCtx *ctx) {
/* A command without keyspecs; only the legacy (first,last,step) triple (MSET like spec). */
if (RedisModule_CreateCommand(ctx,"kspec.none",kspec_impl,"",1,-1,2) == REDISMODULE_ERR)
return REDISMODULE_ERR;
return REDISMODULE_OK;
}
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
REDISMODULE_NOT_USED(argv);
REDISMODULE_NOT_USED(argc);
int spec_id;
if (RedisModule_Init(ctx, "keyspecs", 1, REDISMODULE_APIVER_1)== REDISMODULE_ERR)
int createKspecTwoRanges(RedisModuleCtx *ctx) {
/* Test that two position/range-based key specs are combined to produce the
* legacy (first,last,step) values representing both keys. */
if (RedisModule_CreateCommand(ctx,"kspec.tworanges",kspec_impl,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
RedisModuleCommand *command = RedisModule_GetCommand(ctx,"kspec.tworanges");
RedisModuleCommandInfo info = {
.version = REDISMODULE_COMMAND_INFO_VERSION,
.arity = -2,
.key_specs = (RedisModuleCommandKeySpec[]){
{
.flags = REDISMODULE_CMD_KEY_RO | REDISMODULE_CMD_KEY_ACCESS,
.begin_search_type = REDISMODULE_KSPEC_BS_INDEX,
.bs.index.pos = 1,
.find_keys_type = REDISMODULE_KSPEC_FK_RANGE,
.fk.range = {0,1,0}
},
{
.flags = REDISMODULE_CMD_KEY_RW | REDISMODULE_CMD_KEY_UPDATE,
.begin_search_type = REDISMODULE_KSPEC_BS_INDEX,
.bs.index.pos = 2,
/* Omitted find_keys_type is shorthand for RANGE {0,1,0} */
},
{0}
}
};
if (RedisModule_SetCommandInfo(command, &info) == REDISMODULE_ERR)
return REDISMODULE_ERR;
/* Test legacy range "gluing" */
if (RedisModule_CreateCommand(ctx,"kspec.legacy",kspec_legacy,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
RedisModuleCommand *legacy = RedisModule_GetCommand(ctx,"kspec.legacy");
return REDISMODULE_OK;
}
if (RedisModule_AddCommandKeySpec(legacy,"RO ACCESS",&spec_id) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_SetCommandKeySpecBeginSearchIndex(legacy,spec_id,1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_SetCommandKeySpecFindKeysRange(legacy,spec_id,0,1,0) == REDISMODULE_ERR)
int createKspecKeyword(RedisModuleCtx *ctx) {
/* Only keyword-based specs. The legacy triple is wiped and set to (0,0,0). */
if (RedisModule_CreateCommand(ctx,"kspec.keyword",kspec_impl,"",3,-1,1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
RedisModuleCommand *command = RedisModule_GetCommand(ctx,"kspec.keyword");
RedisModuleCommandInfo info = {
.version = REDISMODULE_COMMAND_INFO_VERSION,
.key_specs = (RedisModuleCommandKeySpec[]){
{
.flags = REDISMODULE_CMD_KEY_RO | REDISMODULE_CMD_KEY_ACCESS,
.begin_search_type = REDISMODULE_KSPEC_BS_KEYWORD,
.bs.keyword.keyword = "KEYS",
.bs.keyword.startfrom = 1,
.find_keys_type = REDISMODULE_KSPEC_FK_RANGE,
.fk.range = {-1,1,0}
},
{0}
}
};
if (RedisModule_SetCommandInfo(command, &info) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_AddCommandKeySpec(legacy,"RW UPDATE",&spec_id) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_SetCommandKeySpecBeginSearchIndex(legacy,spec_id,2) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_SetCommandKeySpecFindKeysRange(legacy,spec_id,0,1,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
return REDISMODULE_OK;
}
/* First is legacy, rest are new specs */
if (RedisModule_CreateCommand(ctx,"kspec.complex1",kspec_complex1,"",1,1,1) == REDISMODULE_ERR)
int createKspecComplex1(RedisModuleCtx *ctx) {
/* First is a range a single key. The rest are keyword-based specs. */
if (RedisModule_CreateCommand(ctx,"kspec.complex1",kspec_impl,"",1,1,1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
RedisModuleCommand *command = RedisModule_GetCommand(ctx,"kspec.complex1");
RedisModuleCommandInfo info = {
.version = REDISMODULE_COMMAND_INFO_VERSION,
.key_specs = (RedisModuleCommandKeySpec[]){
{
.flags = REDISMODULE_CMD_KEY_RO,
.begin_search_type = REDISMODULE_KSPEC_BS_INDEX,
.bs.index.pos = 1,
},
{
.flags = REDISMODULE_CMD_KEY_RW | REDISMODULE_CMD_KEY_UPDATE,
.begin_search_type = REDISMODULE_KSPEC_BS_KEYWORD,
.bs.keyword.keyword = "STORE",
.bs.keyword.startfrom = 2,
},
{
.flags = REDISMODULE_CMD_KEY_RO | REDISMODULE_CMD_KEY_ACCESS,
.begin_search_type = REDISMODULE_KSPEC_BS_KEYWORD,
.bs.keyword.keyword = "KEYS",
.bs.keyword.startfrom = 2,
.find_keys_type = REDISMODULE_KSPEC_FK_KEYNUM,
.fk.keynum = {0,1,1}
},
{0}
}
};
if (RedisModule_SetCommandInfo(command, &info) == REDISMODULE_ERR)
return REDISMODULE_ERR;
RedisModuleCommand *complex1 = RedisModule_GetCommand(ctx,"kspec.complex1");
if (RedisModule_AddCommandKeySpec(complex1,"RW UPDATE",&spec_id) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_SetCommandKeySpecBeginSearchKeyword(complex1,spec_id,"STORE",2) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_SetCommandKeySpecFindKeysRange(complex1,spec_id,0,1,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_AddCommandKeySpec(complex1,"RO ACCESS",&spec_id) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_SetCommandKeySpecBeginSearchKeyword(complex1,spec_id,"KEYS",2) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_SetCommandKeySpecFindKeysKeynum(complex1,spec_id,0,1,1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
return REDISMODULE_OK;
}
int createKspecComplex2(RedisModuleCtx *ctx) {
/* First is not legacy, more than STATIC_KEYS_SPECS_NUM specs */
if (RedisModule_CreateCommand(ctx,"kspec.complex2",kspec_complex2,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
RedisModuleCommand *complex2 = RedisModule_GetCommand(ctx,"kspec.complex2");
if (RedisModule_AddCommandKeySpec(complex2,"RW UPDATE",&spec_id) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_SetCommandKeySpecBeginSearchKeyword(complex2,spec_id,"STORE",5) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_SetCommandKeySpecFindKeysRange(complex2,spec_id,0,1,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_AddCommandKeySpec(complex2,"RO ACCESS",&spec_id) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_SetCommandKeySpecBeginSearchIndex(complex2,spec_id,1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_SetCommandKeySpecFindKeysRange(complex2,spec_id,0,1,0) == REDISMODULE_ERR)
if (RedisModule_CreateCommand(ctx,"kspec.complex2",kspec_impl,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
RedisModuleCommand *command = RedisModule_GetCommand(ctx,"kspec.complex2");
RedisModuleCommandInfo info = {
.version = REDISMODULE_COMMAND_INFO_VERSION,
.key_specs = (RedisModuleCommandKeySpec[]){
{
.flags = REDISMODULE_CMD_KEY_RW | REDISMODULE_CMD_KEY_UPDATE,
.begin_search_type = REDISMODULE_KSPEC_BS_KEYWORD,
.bs.keyword.keyword = "STORE",
.bs.keyword.startfrom = 5,
.find_keys_type = REDISMODULE_KSPEC_FK_RANGE,
.fk.range = {0,1,0}
},
{
.flags = REDISMODULE_CMD_KEY_RO | REDISMODULE_CMD_KEY_ACCESS,
.begin_search_type = REDISMODULE_KSPEC_BS_INDEX,
.bs.index.pos = 1,
.find_keys_type = REDISMODULE_KSPEC_FK_RANGE,
.fk.range = {0,1,0}
},
{
.flags = REDISMODULE_CMD_KEY_RO | REDISMODULE_CMD_KEY_ACCESS,
.begin_search_type = REDISMODULE_KSPEC_BS_INDEX,
.bs.index.pos = 2,
.find_keys_type = REDISMODULE_KSPEC_FK_RANGE,
.fk.range = {0,1,0}
},
{
.flags = REDISMODULE_CMD_KEY_RW | REDISMODULE_CMD_KEY_UPDATE,
.begin_search_type = REDISMODULE_KSPEC_BS_INDEX,
.bs.index.pos = 3,
.find_keys_type = REDISMODULE_KSPEC_FK_KEYNUM,
.fk.keynum = {0,1,1}
},
{
.flags = REDISMODULE_CMD_KEY_RW | REDISMODULE_CMD_KEY_UPDATE,
.begin_search_type = REDISMODULE_KSPEC_BS_KEYWORD,
.bs.keyword.keyword = "MOREKEYS",
.bs.keyword.startfrom = 5,
.find_keys_type = REDISMODULE_KSPEC_FK_RANGE,
.fk.range = {-1,1,0}
},
{0}
}
};
if (RedisModule_SetCommandInfo(command, &info) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_AddCommandKeySpec(complex2,"RO ACCESS",&spec_id) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_SetCommandKeySpecBeginSearchIndex(complex2,spec_id,2) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_SetCommandKeySpecFindKeysRange(complex2,spec_id,0,1,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
return REDISMODULE_OK;
}
if (RedisModule_AddCommandKeySpec(complex2,"RW UPDATE",&spec_id) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_SetCommandKeySpecBeginSearchIndex(complex2,spec_id,3) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_SetCommandKeySpecFindKeysKeynum(complex2,spec_id,0,1,1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
REDISMODULE_NOT_USED(argv);
REDISMODULE_NOT_USED(argc);
if (RedisModule_AddCommandKeySpec(complex2,"RW UPDATE",&spec_id) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_SetCommandKeySpecBeginSearchKeyword(complex2,spec_id,"MOREKEYS",5) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_SetCommandKeySpecFindKeysRange(complex2,spec_id,-1,1,0) == REDISMODULE_ERR)
if (RedisModule_Init(ctx, "keyspecs", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (createKspecNone(ctx) == REDISMODULE_ERR) return REDISMODULE_ERR;
if (createKspecTwoRanges(ctx) == REDISMODULE_ERR) return REDISMODULE_ERR;
if (createKspecKeyword(ctx) == REDISMODULE_ERR) return REDISMODULE_ERR;
if (createKspecComplex1(ctx) == REDISMODULE_ERR) return REDISMODULE_ERR;
if (createKspecComplex2(ctx) == REDISMODULE_ERR) return REDISMODULE_ERR;
return REDISMODULE_OK;
}
#endif
......@@ -29,11 +29,7 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
REDISMODULE_NOT_USED(argv);
REDISMODULE_NOT_USED(argc);
#ifdef INCLUDE_UNRELEASED_KEYSPEC_API
int spec_id;
#endif
if (RedisModule_Init(ctx, "subcommands", 1, REDISMODULE_APIVER_1)== REDISMODULE_ERR)
if (RedisModule_Init(ctx, "subcommands", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"subcommands.bitarray",NULL,"",0,0,0) == REDISMODULE_ERR)
......@@ -43,28 +39,40 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
if (RedisModule_CreateSubcommand(parent,"set",cmd_set,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
RedisModuleCommand *subcmd = RedisModule_GetCommand(ctx,"subcommands.bitarray|set");
#ifdef INCLUDE_UNRELEASED_KEYSPEC_API
if (RedisModule_AddCommandKeySpec(subcmd,"RW UPDATE",&spec_id) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_SetCommandKeySpecBeginSearchIndex(subcmd,spec_id,1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_SetCommandKeySpecFindKeysRange(subcmd,spec_id,0,1,0) == REDISMODULE_ERR)
RedisModuleCommandInfo cmd_set_info = {
.version = REDISMODULE_COMMAND_INFO_VERSION,
.key_specs = (RedisModuleCommandKeySpec[]){
{
.flags = REDISMODULE_CMD_KEY_RW | REDISMODULE_CMD_KEY_UPDATE,
.begin_search_type = REDISMODULE_KSPEC_BS_INDEX,
.bs.index.pos = 1,
.find_keys_type = REDISMODULE_KSPEC_FK_RANGE,
.fk.range = {0,1,0}
},
{0}
}
};
if (RedisModule_SetCommandInfo(subcmd, &cmd_set_info) == REDISMODULE_ERR)
return REDISMODULE_ERR;
#endif
if (RedisModule_CreateSubcommand(parent,"get",cmd_get,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
subcmd = RedisModule_GetCommand(ctx,"subcommands.bitarray|get");
#ifdef INCLUDE_UNRELEASED_KEYSPEC_API
if (RedisModule_AddCommandKeySpec(subcmd,"RO ACCESS",&spec_id) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_SetCommandKeySpecBeginSearchIndex(subcmd,spec_id,1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_SetCommandKeySpecFindKeysRange(subcmd,spec_id,0,1,0) == REDISMODULE_ERR)
RedisModuleCommandInfo cmd_get_info = {
.version = REDISMODULE_COMMAND_INFO_VERSION,
.key_specs = (RedisModuleCommandKeySpec[]){
{
.flags = REDISMODULE_CMD_KEY_RO | REDISMODULE_CMD_KEY_ACCESS,
.begin_search_type = REDISMODULE_KSPEC_BS_INDEX,
.bs.index.pos = 1,
.find_keys_type = REDISMODULE_KSPEC_FK_RANGE,
.fk.range = {0,1,0}
},
{0}
}
};
if (RedisModule_SetCommandInfo(subcmd, &cmd_get_info) == REDISMODULE_ERR)
return REDISMODULE_ERR;
#endif
/* Get the name of the command currently running. */
if (RedisModule_CreateCommand(ctx,"subcommands.parent_get_fullname",cmd_get_fullname,"",0,0,0) == REDISMODULE_ERR)
......
......@@ -44,5 +44,22 @@ test "Sentinel Set with other error situations" {
# unknown parameter option
assert_error "ERR Unknown option or number of arguments for SENTINEL SET 'fakeoption'" {S 0 SENTINEL SET mymaster fakeoption fakevalue}
}
# save new config to disk failed
set info [S 0 SENTINEL master mymaster]
set origin_quorum [dict get $info quorum]
set update_quorum [expr $origin_quorum+1]
set sentinel_id 0
set configfilename [file join "sentinel_$sentinel_id" "sentinel.conf"]
set configfilename_bak [file join "sentinel_$sentinel_id" "sentinel.conf.bak"]
file rename $configfilename $configfilename_bak
file mkdir $configfilename
catch {[S 0 SENTINEL SET mymaster quorum $update_quorum]} err
file delete $configfilename
file rename $configfilename_bak $configfilename
assert_match "ERR Failed to save config file*" $err
}
source "../tests/includes/init-tests.tcl"
test "info command with at most one argument" {
set subCommandList {}
foreach arg {"" "all" "default" "everything"} {
if {$arg == ""} {
set info [S 0 info]
} else {
set info [S 0 info $arg]
}
assert { [string match "*redis_version*" $info] }
assert { [string match "*maxclients*" $info] }
assert { [string match "*used_cpu_user*" $info] }
assert { [string match "*sentinel_tilt*" $info] }
assert { ![string match "*used_memory*" $info] }
assert { ![string match "*rdb_last_bgsave*" $info] }
assert { ![string match "*master_repl_offset*" $info] }
assert { ![string match "*cluster_enabled*" $info] }
}
}
test "info command with one sub-section" {
set info [S 0 info cpu]
assert { [string match "*used_cpu_user*" $info] }
assert { ![string match "*sentinel_tilt*" $info] }
assert { ![string match "*redis_version*" $info] }
set info [S 0 info sentinel]
assert { [string match "*sentinel_tilt*" $info] }
assert { ![string match "*used_cpu_user*" $info] }
assert { ![string match "*redis_version*" $info] }
}
test "info command with multiple sub-sections" {
set info [S 0 info server sentinel replication]
assert { [string match "*redis_version*" $info] }
assert { [string match "*sentinel_tilt*" $info] }
assert { ![string match "*used_memory*" $info] }
assert { ![string match "*used_cpu_user*" $info] }
set info [S 0 info cpu all]
assert { [string match "*used_cpu_user*" $info] }
assert { [string match "*sentinel_tilt*" $info] }
assert { [string match "*redis_version*" $info] }
assert { ![string match "*used_memory*" $info] }
assert { ![string match "*master_repl_offset*" $info] }
}
......@@ -35,6 +35,7 @@ array set ::redis::addr {}
array set ::redis::blocking {}
array set ::redis::deferred {}
array set ::redis::readraw {}
array set ::redis::attributes {} ;# Holds the RESP3 attributes from the last call
array set ::redis::reconnect {}
array set ::redis::tls {}
array set ::redis::callback {}
......@@ -105,6 +106,7 @@ proc ::redis::__dispatch__raw__ {id method argv} {
set argv [lrange $argv 0 end-1]
}
if {[info command ::redis::__method__$method] eq {}} {
catch {unset ::redis::attributes($id)}
set cmd "*[expr {[llength $argv]+1}]\r\n"
append cmd "$[string length $method]\r\n$method\r\n"
foreach a $argv {
......@@ -165,6 +167,7 @@ proc ::redis::__method__close {id fd} {
catch {unset ::redis::blocking($id)}
catch {unset ::redis::deferred($id)}
catch {unset ::redis::readraw($id)}
catch {unset ::redis::attributes($id)}
catch {unset ::redis::reconnect($id)}
catch {unset ::redis::tls($id)}
catch {unset ::redis::state($id)}
......@@ -185,6 +188,10 @@ proc ::redis::__method__readraw {id fd val} {
set ::redis::readraw($id) $val
}
proc ::redis::__method__attributes {id fd} {
set _ $::redis::attributes($id)
}
proc ::redis::redis_write {fd buf} {
puts -nonewline $fd $buf
}
......@@ -286,8 +293,8 @@ proc ::redis::redis_read_reply {id fd} {
* {return [redis_multi_bulk_read $id $fd]}
% {return [redis_read_map $id $fd]}
| {
# ignore attributes for now (nowhere to store them)
redis_read_map $id $fd
set attrib [redis_read_map $id $fd]
set ::redis::attributes($id) $attrib
continue
}
default {
......
......@@ -24,10 +24,10 @@ proc assert_no_match {pattern value} {
}
}
proc assert_match {pattern value} {
proc assert_match {pattern value {detail ""}} {
if {![string match $pattern $value]} {
set context "(context: [info frame -1])"
error "assertion:Expected '$value' to match '$pattern' $context"
error "assertion:Expected '$value' to match '$pattern' $context $detail"
}
}
......@@ -84,9 +84,9 @@ proc assert_range {value min max {detail ""}} {
proc assert_error {pattern code {detail ""}} {
if {[catch {uplevel 1 $code} error]} {
assert_match $pattern $error
assert_match $pattern $error $detail
} else {
assert_failed "assertion:Expected an error but nothing was caught" $detail
assert_failed "Expected an error matching '$pattern' but got '$error'" $detail
}
}
......
......@@ -89,7 +89,7 @@ proc waitForBgsave r {
puts -nonewline "\nWaiting for background save to finish... "
flush stdout
}
after 1000
after 50
} else {
break
}
......@@ -103,7 +103,7 @@ proc waitForBgrewriteaof r {
puts -nonewline "\nWaiting for background AOF rewrite to finish... "
flush stdout
}
after 1000
after 50
} else {
break
}
......@@ -647,7 +647,7 @@ proc latencyrstat_percentiles {cmd r} {
proc generate_fuzzy_traffic_on_key {key duration} {
# Commands per type, blocking commands removed
# TODO: extract these from help.h or elsewhere, and improve to include other types
# TODO: extract these from COMMAND DOCS, and improve to include other types
set string_commands {APPEND BITCOUNT BITFIELD BITOP BITPOS DECR DECRBY GET GETBIT GETRANGE GETSET INCR INCRBY INCRBYFLOAT MGET MSET MSETNX PSETEX SET SETBIT SETEX SETNX SETRANGE LCS STRLEN}
set hash_commands {HDEL HEXISTS HGET HGETALL HINCRBY HINCRBYFLOAT HKEYS HLEN HMGET HMSET HSCAN HSET HSETNX HSTRLEN HVALS HRANDFIELD}
set zset_commands {ZADD ZCARD ZCOUNT ZINCRBY ZINTERSTORE ZLEXCOUNT ZPOPMAX ZPOPMIN ZRANGE ZRANGEBYLEX ZRANGEBYSCORE ZRANK ZREM ZREMRANGEBYLEX ZREMRANGEBYRANK ZREMRANGEBYSCORE ZREVRANGE ZREVRANGEBYLEX ZREVRANGEBYSCORE ZREVRANK ZSCAN ZSCORE ZUNIONSTORE ZRANDMEMBER}
......
......@@ -20,6 +20,7 @@ set ::all_tests {
unit/keyspace
unit/scan
unit/info
unit/info-command
unit/type/string
unit/type/incr
unit/type/list
......@@ -92,6 +93,7 @@ set ::all_tests {
unit/cluster
unit/client-eviction
unit/violations
unit/replybufsize
}
# Index to the next test to run in the ::all_tests list.
set ::next_test 0
......
......@@ -7,6 +7,11 @@ start_server {tags {"acl external:skip"}} {
r ACL setuser newuser
}
test {Usernames can not contain spaces or null characters} {
catch {r ACL setuser "a a"} err
set err
} {*Usernames can't contain spaces or null characters*}
test {New users start disabled} {
r ACL setuser newuser >passwd1
catch {r AUTH newuser passwd1} err
......@@ -699,6 +704,23 @@ start_server {tags {"acl external:skip"}} {
catch {[r ping]} e
assert_match "*I/O error*" $e
}
test {ACL GENPASS command failed test} {
catch {r ACL genpass -236} err1
catch {r ACL genpass 5000} err2
assert_match "*ACL GENPASS argument must be the number*" $err1
assert_match "*ACL GENPASS argument must be the number*" $err2
}
test {Default user can not be removed} {
catch {r ACL deluser default} err
set err
} {ERR The 'default' user cannot be removed}
test {ACL load non-existing configured ACL file} {
catch {r ACL load} err
set err
} {*Redis instance is not configured to use an ACL file*}
}
set server_path [tmpdir "server.acl"]
......
......@@ -3,6 +3,11 @@ start_server {tags {"auth external:skip"}} {
catch {r auth foo} err
set _ $err
} {ERR*any password*}
test {Arity check for auth command} {
catch {r auth a b c} err
set _ $err
} {*syntax error*}
}
start_server {tags {"auth external:skip"} overrides {requirepass foobar}} {
......
......@@ -45,6 +45,10 @@ proc mb {v} {
return [expr $v * 1024 * 1024]
}
proc kb {v} {
return [expr $v * 1024]
}
start_server {} {
set maxmemory_clients 3000000
r config set maxmemory-clients $maxmemory_clients
......@@ -213,7 +217,7 @@ start_server {} {
r debug pause-cron 0
$rr close
$redirected_c close
}
} {0} {needs:debug}
test "client evicted due to client tracking prefixes" {
r flushdb
......@@ -391,6 +395,7 @@ start_server {} {
test "evict clients only until below limit" {
set client_count 10
set client_mem [mb 1]
r debug replybuffer-peak-reset-time never
r config set maxmemory-clients 0
r client setname control
r client no-evict on
......@@ -433,19 +438,23 @@ start_server {} {
set connected_clients [llength [lsearch -all [split [string trim [r client list]] "\r\n"] *name=client*]]
assert {$connected_clients == [expr $client_count / 2]}
# Restore the peak reset time to default
r debug replybuffer-peak-reset-time reset
foreach rr $rrs {$rr close}
}
} {} {needs:debug}
}
start_server {} {
test "evict clients in right order (large to small)" {
# Note that each size step needs to be at least x2 larger than previous step
# because of how the client-eviction size bucktting works
set sizes [list 100000 [mb 1] [mb 3]]
set sizes [list [kb 128] [mb 1] [mb 3]]
set clients_per_size 3
r client setname control
r client no-evict on
r config set maxmemory-clients 0
r debug replybuffer-peak-reset-time never
# Run over all sizes and create some clients using up that size
set total_client_mem 0
......@@ -470,7 +479,6 @@ start_server {} {
# Account total client memory usage
incr total_mem [expr $clients_per_size * $client_mem]
}
incr total_mem [client_field control tot-mem]
# Make sure all clients are connected
set clients [split [string trim [r client list]] "\r\n"]
......@@ -481,8 +489,9 @@ start_server {} {
# For each size reduce maxmemory-clients so relevant clients should be evicted
# do this from largest to smallest
foreach size [lreverse $sizes] {
set control_mem [client_field control tot-mem]
set total_mem [expr $total_mem - $clients_per_size * $size]
r config set maxmemory-clients $total_mem
r config set maxmemory-clients [expr $total_mem + $control_mem]
set clients [split [string trim [r client list]] "\r\n"]
# Verify only relevant clients were evicted
for {set i 0} {$i < [llength $sizes]} {incr i} {
......@@ -495,8 +504,12 @@ start_server {} {
}
}
}
# Restore the peak reset time to default
r debug replybuffer-peak-reset-time reset
foreach rr $rrs {$rr close}
}
} {} {needs:debug}
}
}
......
......@@ -294,6 +294,31 @@ start_server {tags {"geo"}} {
r georadiusbymember nyc "wtc one" 7 km
} {{wtc one} {union square} {central park n/q/r} 4545 {lic market}}
test {GEORADIUSBYMEMBER search areas contain satisfied points in oblique direction} {
r del k1
r geoadd k1 -0.15307903289794921875 85 n1 0.3515625 85.00019260486917005437 n2
set ret1 [r GEORADIUSBYMEMBER k1 n1 4891.94 m]
assert_equal $ret1 {n1 n2}
r zrem k1 n1 n2
r geoadd k1 -4.95211958885192871094 85 n3 11.25 85.0511 n4
set ret2 [r GEORADIUSBYMEMBER k1 n3 156544 m]
assert_equal $ret2 {n3 n4}
r zrem k1 n3 n4
r geoadd k1 -45 65.50900022111811438208 n5 90 85.0511 n6
set ret3 [r GEORADIUSBYMEMBER k1 n5 5009431 m]
assert_equal $ret3 {n5 n6}
}
test {GEORADIUSBYMEMBER crossing pole search} {
r del k1
r geoadd k1 45 65 n1 -135 85.05 n2
set ret [r GEORADIUSBYMEMBER k1 n1 5009431 m]
assert_equal $ret {n1 n2}
}
test {GEOSEARCH FROMMEMBER simple (sorted)} {
r geosearch nyc frommember "wtc one" bybox 14 14 km
} {{wtc one} {union square} {central park n/q/r} 4545 {lic market} q4}
......
start_server {tags {"info and its relative command"}} {
test "info command with at most one sub command" {
foreach arg {"" "all" "default" "everything"} {
if {$arg == ""} {
set info [r 0 info]
} else {
set info [r 0 info $arg]
}
assert { [string match "*redis_version*" $info] }
assert { [string match "*used_cpu_user*" $info] }
assert { ![string match "*sentinel_tilt*" $info] }
assert { [string match "*used_memory*" $info] }
if {$arg == "" || $arg == "default"} {
assert { ![string match "*rejected_calls*" $info] }
} else {
assert { [string match "*rejected_calls*" $info] }
}
}
}
test "info command with one sub-section" {
set info [r info cpu]
assert { [string match "*used_cpu_user*" $info] }
assert { ![string match "*sentinel_tilt*" $info] }
assert { ![string match "*used_memory*" $info] }
set info [r info sentinel]
assert { ![string match "*sentinel_tilt*" $info] }
assert { ![string match "*used_memory*" $info] }
set info [r info commandSTATS] ;# test case insensitive compare
assert { ![string match "*used_memory*" $info] }
assert { [string match "*rejected_calls*" $info] }
}
test "info command with multiple sub-sections" {
set info [r info cpu sentinel]
assert { [string match "*used_cpu_user*" $info] }
assert { ![string match "*sentinel_tilt*" $info] }
assert { ![string match "*master_repl_offset*" $info] }
set info [r info cpu all]
assert { [string match "*used_cpu_user*" $info] }
assert { ![string match "*sentinel_tilt*" $info] }
assert { [string match "*used_memory*" $info] }
assert { [string match "*master_repl_offset*" $info] }
assert { [string match "*rejected_calls*" $info] }
# check that we didn't get the same info twice
assert { ![string match "*used_cpu_user_children*used_cpu_user_children*" $info] }
set info [r info cpu default]
assert { [string match "*used_cpu_user*" $info] }
assert { ![string match "*sentinel_tilt*" $info] }
assert { [string match "*used_memory*" $info] }
assert { [string match "*master_repl_offset*" $info] }
assert { ![string match "*rejected_calls*" $info] }
# check that we didn't get the same info twice
assert { ![string match "*used_cpu_user_children*used_cpu_user_children*" $info] }
}
}
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