Commit 0db3b0a0 authored by Yossi Gottlieb's avatar Yossi Gottlieb
Browse files

Merge remote-tracking branch 'upstream/unstable' into tls

parents c469f6ad b8e02f2b
...@@ -242,17 +242,17 @@ int streamAppendItem(stream *s, robj **argv, int64_t numfields, streamID *added_ ...@@ -242,17 +242,17 @@ int streamAppendItem(stream *s, robj **argv, int64_t numfields, streamID *added_
* the current node is full. */ * the current node is full. */
if (lp != NULL) { if (lp != NULL) {
if (server.stream_node_max_bytes && if (server.stream_node_max_bytes &&
lp_bytes > server.stream_node_max_bytes) lp_bytes >= server.stream_node_max_bytes)
{ {
lp = NULL; lp = NULL;
} else if (server.stream_node_max_entries) { } else if (server.stream_node_max_entries) {
int64_t count = lpGetInteger(lpFirst(lp)); int64_t count = lpGetInteger(lpFirst(lp));
if (count > server.stream_node_max_entries) lp = NULL; if (count >= server.stream_node_max_entries) lp = NULL;
} }
} }
int flags = STREAM_ITEM_FLAG_NONE; int flags = STREAM_ITEM_FLAG_NONE;
if (lp == NULL || lp_bytes > server.stream_node_max_bytes) { if (lp == NULL || lp_bytes >= server.stream_node_max_bytes) {
master_id = id; master_id = id;
streamEncodeID(rax_key,&id); streamEncodeID(rax_key,&id);
/* Create the listpack having the master entry ID and fields. */ /* Create the listpack having the master entry ID and fields. */
......
...@@ -326,6 +326,7 @@ size_t zmalloc_get_rss(void) { ...@@ -326,6 +326,7 @@ size_t zmalloc_get_rss(void) {
#endif #endif
#if defined(USE_JEMALLOC) #if defined(USE_JEMALLOC)
int zmalloc_get_allocator_info(size_t *allocated, int zmalloc_get_allocator_info(size_t *allocated,
size_t *active, size_t *active,
size_t *resident) { size_t *resident) {
...@@ -347,13 +348,44 @@ int zmalloc_get_allocator_info(size_t *allocated, ...@@ -347,13 +348,44 @@ int zmalloc_get_allocator_info(size_t *allocated,
je_mallctl("stats.allocated", allocated, &sz, NULL, 0); je_mallctl("stats.allocated", allocated, &sz, NULL, 0);
return 1; return 1;
} }
void set_jemalloc_bg_thread(int enable) {
/* let jemalloc do purging asynchronously, required when there's no traffic
* after flushdb */
char val = !!enable;
je_mallctl("background_thread", NULL, 0, &val, 1);
}
int jemalloc_purge() {
/* return all unused (reserved) pages to the OS */
char tmp[32];
unsigned narenas = 0;
size_t sz = sizeof(unsigned);
if (!je_mallctl("arenas.narenas", &narenas, &sz, NULL, 0)) {
sprintf(tmp, "arena.%d.purge", narenas);
if (!je_mallctl(tmp, NULL, 0, NULL, 0))
return 0;
}
return -1;
}
#else #else
int zmalloc_get_allocator_info(size_t *allocated, int zmalloc_get_allocator_info(size_t *allocated,
size_t *active, size_t *active,
size_t *resident) { size_t *resident) {
*allocated = *resident = *active = 0; *allocated = *resident = *active = 0;
return 1; return 1;
} }
void set_jemalloc_bg_thread(int enable) {
((void)(enable));
}
int jemalloc_purge() {
return 0;
}
#endif #endif
/* Get the sum of the specified field (converted form kb to bytes) in /* Get the sum of the specified field (converted form kb to bytes) in
......
...@@ -86,6 +86,8 @@ size_t zmalloc_used_memory(void); ...@@ -86,6 +86,8 @@ size_t zmalloc_used_memory(void);
void zmalloc_set_oom_handler(void (*oom_handler)(size_t)); void zmalloc_set_oom_handler(void (*oom_handler)(size_t));
size_t zmalloc_get_rss(void); size_t zmalloc_get_rss(void);
int zmalloc_get_allocator_info(size_t *allocated, size_t *active, size_t *resident); int zmalloc_get_allocator_info(size_t *allocated, size_t *active, size_t *resident);
void set_jemalloc_bg_thread(int enable);
int jemalloc_purge();
size_t zmalloc_get_private_dirty(long pid); size_t zmalloc_get_private_dirty(long pid);
size_t zmalloc_get_smap_bytes_by_field(char *field, long pid); size_t zmalloc_get_smap_bytes_by_field(char *field, long pid);
size_t zmalloc_get_memory_size(void); size_t zmalloc_get_memory_size(void);
......
...@@ -115,3 +115,17 @@ start_server_and_kill_it [list "dir" $server_path] { ...@@ -115,3 +115,17 @@ start_server_and_kill_it [list "dir" $server_path] {
} }
} }
} }
start_server {} {
test {Test FLUSHALL aborts bgsave} {
r config set rdb-key-save-delay 1000
r debug populate 1000
r bgsave
assert_equal [s rdb_bgsave_in_progress] 1
r flushall
after 200
assert_equal [s rdb_bgsave_in_progress] 0
# make sure the server is still writable
r set x xx
}
}
\ No newline at end of file
...@@ -319,7 +319,7 @@ start_server {tags {"repl"}} { ...@@ -319,7 +319,7 @@ start_server {tags {"repl"}} {
} }
} }
test {slave fails full sync and diskless load swapdb recoveres it} { test {slave fails full sync and diskless load swapdb recovers it} {
start_server {tags {"repl"}} { start_server {tags {"repl"}} {
set slave [srv 0 client] set slave [srv 0 client]
set slave_host [srv 0 host] set slave_host [srv 0 host]
......
...@@ -13,16 +13,28 @@ endif ...@@ -13,16 +13,28 @@ endif
.SUFFIXES: .c .so .xo .o .SUFFIXES: .c .so .xo .o
all: commandfilter.so testrdb.so all: commandfilter.so testrdb.so fork.so infotest.so propagate.so
.c.xo: .c.xo:
$(CC) -I../../src $(CFLAGS) $(SHOBJ_CFLAGS) -fPIC -c $< -o $@ $(CC) -I../../src $(CFLAGS) $(SHOBJ_CFLAGS) -fPIC -c $< -o $@
commandfilter.xo: ../../src/redismodule.h commandfilter.xo: ../../src/redismodule.h
fork.xo: ../../src/redismodule.h
testrdb.xo: ../../src/redismodule.h testrdb.xo: ../../src/redismodule.h
infotest.xo: ../../src/redismodule.h
propagate.xo: ../../src/redismodule.h
commandfilter.so: commandfilter.xo commandfilter.so: commandfilter.xo
$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc $(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc
fork.so: fork.xo
$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc
testrdb.so: testrdb.xo testrdb.so: testrdb.xo
$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc $(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc
infotest.so: infotest.xo
$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc
propagate.so: propagate.xo
$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc
#define REDISMODULE_EXPERIMENTAL_API
#include "redismodule.h"
#include <string.h>
#include <assert.h>
#include <unistd.h>
#define UNUSED(V) ((void) V)
int child_pid = -1;
int exitted_with_code = -1;
void done_handler(int exitcode, int bysignal, void *user_data) {
child_pid = -1;
exitted_with_code = exitcode;
assert(user_data==(void*)0xdeadbeef);
UNUSED(bysignal);
}
int fork_create(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{
long long code_to_exit_with;
if (argc != 2) {
RedisModule_WrongArity(ctx);
return REDISMODULE_OK;
}
RedisModule_StringToLongLong(argv[1], &code_to_exit_with);
exitted_with_code = -1;
child_pid = RedisModule_Fork(done_handler, (void*)0xdeadbeef);
if (child_pid < 0) {
RedisModule_ReplyWithError(ctx, "Fork failed");
return REDISMODULE_OK;
} else if (child_pid > 0) {
/* parent */
RedisModule_ReplyWithLongLong(ctx, child_pid);
return REDISMODULE_OK;
}
/* child */
RedisModule_Log(ctx, "notice", "fork child started");
usleep(200000);
RedisModule_Log(ctx, "notice", "fork child exiting");
RedisModule_ExitFromChild(code_to_exit_with);
/* unreachable */
return 0;
}
int fork_exitcode(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{
UNUSED(argv);
UNUSED(argc);
RedisModule_ReplyWithLongLong(ctx, exitted_with_code);
return REDISMODULE_OK;
}
int fork_kill(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{
UNUSED(argv);
UNUSED(argc);
if (RedisModule_KillForkChild(child_pid) != REDISMODULE_OK)
RedisModule_ReplyWithError(ctx, "KillForkChild failed");
else
RedisModule_ReplyWithLongLong(ctx, 1);
child_pid = -1;
return REDISMODULE_OK;
}
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
UNUSED(argv);
UNUSED(argc);
if (RedisModule_Init(ctx,"fork",1,REDISMODULE_APIVER_1)== REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"fork.create", fork_create,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"fork.exitcode", fork_exitcode,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"fork.kill", fork_kill,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
return REDISMODULE_OK;
}
#include "redismodule.h"
#include <string.h>
void InfoFunc(RedisModuleInfoCtx *ctx, int for_crash_report) {
RedisModule_InfoAddSection(ctx, "");
RedisModule_InfoAddFieldLongLong(ctx, "global", -2);
RedisModule_InfoAddSection(ctx, "Spanish");
RedisModule_InfoAddFieldCString(ctx, "uno", "one");
RedisModule_InfoAddFieldLongLong(ctx, "dos", 2);
RedisModule_InfoAddSection(ctx, "Italian");
RedisModule_InfoAddFieldLongLong(ctx, "due", 2);
RedisModule_InfoAddFieldDouble(ctx, "tre", 3.3);
RedisModule_InfoAddSection(ctx, "keyspace");
RedisModule_InfoBeginDictField(ctx, "db0");
RedisModule_InfoAddFieldLongLong(ctx, "keys", 3);
RedisModule_InfoAddFieldLongLong(ctx, "expires", 1);
RedisModule_InfoEndDictField(ctx);
if (for_crash_report) {
RedisModule_InfoAddSection(ctx, "Klingon");
RedisModule_InfoAddFieldCString(ctx, "one", "wa’");
RedisModule_InfoAddFieldCString(ctx, "two", "cha’");
RedisModule_InfoAddFieldCString(ctx, "three", "wej");
}
}
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
REDISMODULE_NOT_USED(argv);
REDISMODULE_NOT_USED(argc);
if (RedisModule_Init(ctx,"infotest",1,REDISMODULE_APIVER_1)
== REDISMODULE_ERR) return REDISMODULE_ERR;
if (RedisModule_RegisterInfoFunc(ctx, InfoFunc) == REDISMODULE_ERR) return REDISMODULE_ERR;
return REDISMODULE_OK;
}
/* This module is used to test the propagation (replication + AOF) of
* commands, via the RedisModule_Replicate() interface, in asynchronous
* contexts, such as callbacks not implementing commands, and thread safe
* contexts.
*
* We create a timer callback and a threads using a thread safe context.
* Using both we try to propagate counters increments, and later we check
* if the replica contains the changes as expected.
*
* -----------------------------------------------------------------------------
*
* Copyright (c) 2019, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#define REDISMODULE_EXPERIMENTAL_API
#include "redismodule.h"
#include <pthread.h>
/* Timer callback. */
void timerHandler(RedisModuleCtx *ctx, void *data) {
REDISMODULE_NOT_USED(ctx);
REDISMODULE_NOT_USED(data);
static int times = 0;
RedisModule_Replicate(ctx,"INCR","c","timer");
times++;
if (times < 10)
RedisModule_CreateTimer(ctx,100,timerHandler,NULL);
else
times = 0;
}
/* The thread entry point. */
void *threadMain(void *arg) {
REDISMODULE_NOT_USED(arg);
RedisModuleCtx *ctx = RedisModule_GetThreadSafeContext(NULL);
RedisModule_SelectDb(ctx,9); /* Tests ran in database number 9. */
for (int i = 0; i < 10; i++) {
RedisModule_ThreadSafeContextLock(ctx);
RedisModule_Replicate(ctx,"INCR","c","thread");
RedisModule_ThreadSafeContextUnlock(ctx);
}
RedisModule_FreeThreadSafeContext(ctx);
return NULL;
}
int propagateTestCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{
REDISMODULE_NOT_USED(argv);
REDISMODULE_NOT_USED(argc);
RedisModuleTimerID timer_id =
RedisModule_CreateTimer(ctx,100,timerHandler,NULL);
REDISMODULE_NOT_USED(timer_id);
pthread_t tid;
if (pthread_create(&tid,NULL,threadMain,NULL) != 0)
return RedisModule_ReplyWithError(ctx,"-ERR Can't start thread");
REDISMODULE_NOT_USED(tid);
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,"propagate-test",1,REDISMODULE_APIVER_1)
== REDISMODULE_ERR) return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"propagate-test",
propagateTestCommand,
"",1,1,1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
return REDISMODULE_OK;
}
...@@ -15,6 +15,8 @@ RedisModuleString *after_str = NULL; ...@@ -15,6 +15,8 @@ RedisModuleString *after_str = NULL;
void *testrdb_type_load(RedisModuleIO *rdb, int encver) { void *testrdb_type_load(RedisModuleIO *rdb, int encver) {
int count = RedisModule_LoadSigned(rdb); int count = RedisModule_LoadSigned(rdb);
if (RedisModule_IsIOError(rdb))
return NULL;
assert(count==1); assert(count==1);
assert(encver==1); assert(encver==1);
RedisModuleString *str = RedisModule_LoadString(rdb); RedisModuleString *str = RedisModule_LoadString(rdb);
...@@ -57,6 +59,8 @@ int testrdb_aux_load(RedisModuleIO *rdb, int encver, int when) { ...@@ -57,6 +59,8 @@ int testrdb_aux_load(RedisModuleIO *rdb, int encver, int when) {
RedisModule_FreeString(ctx, before_str); RedisModule_FreeString(ctx, before_str);
before_str = NULL; before_str = NULL;
int count = RedisModule_LoadSigned(rdb); int count = RedisModule_LoadSigned(rdb);
if (RedisModule_IsIOError(rdb))
return REDISMODULE_ERR;
if (count) if (count)
before_str = RedisModule_LoadString(rdb); before_str = RedisModule_LoadString(rdb);
} else { } else {
...@@ -64,14 +68,19 @@ int testrdb_aux_load(RedisModuleIO *rdb, int encver, int when) { ...@@ -64,14 +68,19 @@ int testrdb_aux_load(RedisModuleIO *rdb, int encver, int when) {
RedisModule_FreeString(ctx, after_str); RedisModule_FreeString(ctx, after_str);
after_str = NULL; after_str = NULL;
int count = RedisModule_LoadSigned(rdb); int count = RedisModule_LoadSigned(rdb);
if (RedisModule_IsIOError(rdb))
return REDISMODULE_ERR;
if (count) if (count)
after_str = RedisModule_LoadString(rdb); after_str = RedisModule_LoadString(rdb);
} }
if (RedisModule_IsIOError(rdb))
return REDISMODULE_ERR;
return REDISMODULE_OK; return REDISMODULE_OK;
} }
void testrdb_type_free(void *value) { void testrdb_type_free(void *value) {
RedisModule_FreeString(NULL, (RedisModuleString*)value); if (value)
RedisModule_FreeString(NULL, (RedisModuleString*)value);
} }
int testrdb_set_before(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) int testrdb_set_before(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
...@@ -171,6 +180,8 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) ...@@ -171,6 +180,8 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
if (RedisModule_Init(ctx,"testrdb",1,REDISMODULE_APIVER_1) == REDISMODULE_ERR) if (RedisModule_Init(ctx,"testrdb",1,REDISMODULE_APIVER_1) == REDISMODULE_ERR)
return REDISMODULE_ERR; return REDISMODULE_ERR;
RedisModule_SetModuleOptions(ctx, REDISMODULE_OPTIONS_HANDLE_IO_ERRORS);
if (argc > 0) if (argc > 0)
RedisModule_StringToLongLong(argv[0], &conf_aux_count); RedisModule_StringToLongLong(argv[0], &conf_aux_count);
......
...@@ -15,6 +15,12 @@ proc assert {condition} { ...@@ -15,6 +15,12 @@ proc assert {condition} {
} }
} }
proc assert_no_match {pattern value} {
if {[string match $pattern $value]} {
error "assertion:Expected '$value' to not match '$pattern'"
}
}
proc assert_match {pattern value} { proc assert_match {pattern value} {
if {![string match $pattern $value]} { if {![string match $pattern $value]} {
error "assertion:Expected '$value' to match '$pattern'" error "assertion:Expected '$value' to match '$pattern'"
......
...@@ -35,6 +35,32 @@ start_server {tags {"acl"}} { ...@@ -35,6 +35,32 @@ start_server {tags {"acl"}} {
set e set e
} {*WRONGPASS*} } {*WRONGPASS*}
test {Test password hashes can be added} {
r ACL setuser newuser #34344e4d60c2b6d639b7bd22e18f2b0b91bc34bf0ac5f9952744435093cfb4e6
catch {r AUTH newuser passwd4} e
assert {$e eq "OK"}
}
test {Test password hashes validate input} {
# Validate Length
catch {r ACL setuser newuser #34344e4d60c2b6d639b7bd22e18f2b0b91bc34bf0ac5f9952744435093cfb4e} e
# Validate character outside set
catch {r ACL setuser newuser #34344e4d60c2b6d639b7bd22e18f2b0b91bc34bf0ac5f9952744435093cfb4eq} e
set e
} {*Error in ACL SETUSER modifier*}
test {ACL GETUSER returns the password hash instead of the actual password} {
set passstr [dict get [r ACL getuser newuser] passwords]
assert_match {*34344e4d60c2b6d639b7bd22e18f2b0b91bc34bf0ac5f9952744435093cfb4e6*} $passstr
assert_no_match {*passwd4*} $passstr
}
test {Test hashed passwords removal} {
r ACL setuser newuser !34344e4d60c2b6d639b7bd22e18f2b0b91bc34bf0ac5f9952744435093cfb4e6
set passstr [dict get [r ACL getuser newuser] passwords]
assert_no_match {*34344e4d60c2b6d639b7bd22e18f2b0b91bc34bf0ac5f9952744435093cfb4e6*} $passstr
}
test {By default users are not able to access any command} { test {By default users are not able to access any command} {
catch {r SET foo bar} e catch {r SET foo bar} e
set e set e
...@@ -67,7 +93,7 @@ start_server {tags {"acl"}} { ...@@ -67,7 +93,7 @@ start_server {tags {"acl"}} {
set e set e
} {*NOPERM*} } {*NOPERM*}
test {ACLs can include or excluse whole classes of commands} { test {ACLs can include or exclude whole classes of commands} {
r ACL setuser newuser -@all +@set +acl r ACL setuser newuser -@all +@set +acl
r SADD myset a b c; # Should not raise an error r SADD myset a b c; # Should not raise an error
r ACL setuser newuser +@all -@string r ACL setuser newuser +@all -@string
......
...@@ -129,7 +129,7 @@ start_server {tags {"geo"}} { ...@@ -129,7 +129,7 @@ start_server {tags {"geo"}} {
r del points r del points
r geoadd points -5.6 42.6 test r geoadd points -5.6 42.6 test
lindex [r geohash points test] 0 lindex [r geohash points test] 0
} {ezs42e44yx0} } {ezs42e44yx}
test {GEOPOS simple} { test {GEOPOS simple} {
r del points r del points
......
set testmodule [file normalize tests/modules/fork.so]
proc count_log_message {pattern} {
set result [exec grep -c $pattern < [srv 0 stdout]]
}
start_server {tags {"modules"}} {
r module load $testmodule
test {Module fork} {
# the argument to fork.create is the exitcode on termination
r fork.create 3
wait_for_condition 20 100 {
[r fork.exitcode] != -1
} else {
fail "fork didn't terminate"
}
r fork.exitcode
} {3}
test {Module fork kill} {
r fork.create 3
after 20
r fork.kill
after 100
assert {[count_log_message "fork child started"] eq "2"}
assert {[count_log_message "Received SIGUSR1 in child"] eq "1"}
assert {[count_log_message "fork child exiting"] eq "1"}
}
}
set testmodule [file normalize tests/modules/infotest.so]
# Return value for INFO property
proc field {info property} {
if {[regexp "\r\n$property:(.*?)\r\n" $info _ value]} {
set _ $value
}
}
start_server {tags {"modules"}} {
r module load $testmodule log-key 0
test {module info all} {
set info [r info all]
# info all does not contain modules
assert { ![string match "*Spanish*" $info] }
assert { ![string match "*infotest_*" $info] }
assert { [string match "*used_memory*" $info] }
}
test {module info everything} {
set info [r info everything]
# info everything contains all default sections, but not ones for crash report
assert { [string match "*infotest_global*" $info] }
assert { [string match "*Spanish*" $info] }
assert { [string match "*Italian*" $info] }
assert { [string match "*used_memory*" $info] }
assert { ![string match "*Klingon*" $info] }
field $info infotest_dos
} {2}
test {module info modules} {
set info [r info modules]
# info all does not contain modules
assert { [string match "*Spanish*" $info] }
assert { [string match "*infotest_global*" $info] }
assert { ![string match "*used_memory*" $info] }
}
test {module info one module} {
set info [r info INFOTEST]
# info all does not contain modules
assert { [string match "*Spanish*" $info] }
assert { ![string match "*used_memory*" $info] }
field $info infotest_global
} {-2}
test {module info one section} {
set info [r info INFOTEST_SPANISH]
assert { ![string match "*used_memory*" $info] }
assert { ![string match "*Italian*" $info] }
assert { ![string match "*infotest_global*" $info] }
field $info infotest_uno
} {one}
test {module info dict} {
set info [r info infotest_keyspace]
set keyspace [field $info infotest_db0]
set keys [scan [regexp -inline {keys\=([\d]*)} $keyspace] keys=%d]
} {3}
# TODO: test crash report.
}
set testmodule [file normalize tests/modules/propagate.so]
tags "modules" {
test {Modules can propagate in async and threaded contexts} {
start_server {} {
set replica [srv 0 client]
set replica_host [srv 0 host]
set replica_port [srv 0 port]
start_server [list overrides [list loadmodule "$testmodule"]] {
set master [srv 0 client]
set master_host [srv 0 host]
set master_port [srv 0 port]
# Start the replication process...
$replica replicaof $master_host $master_port
wait_for_sync $replica
after 1000
$master propagate-test
wait_for_condition 5000 10 {
([$replica get timer] eq "10") && \
([$replica get thread] eq "10")
} else {
fail "The two counters don't match the expected value."
}
}
}
}
}
...@@ -56,7 +56,67 @@ tags "modules" { ...@@ -56,7 +56,67 @@ tags "modules" {
} }
} }
tags {repl} {
test {diskless loading short read with module} {
start_server [list overrides [list loadmodule "$testmodule"]] {
set replica [srv 0 client]
set replica_host [srv 0 host]
set replica_port [srv 0 port]
start_server [list overrides [list loadmodule "$testmodule"]] {
set master [srv 0 client]
set master_host [srv 0 host]
set master_port [srv 0 port]
# TODO: test short read handling # Set master and replica to use diskless replication
$master config set repl-diskless-sync yes
$master config set rdbcompression no
$replica config set repl-diskless-load swapdb
for {set k 0} {$k < 30} {incr k} {
r testrdb.set.key key$k [string repeat A [expr {int(rand()*1000000)}]]
}
# Start the replication process...
$master config set repl-diskless-sync-delay 0
$replica replicaof $master_host $master_port
# kill the replication at various points
set attempts 3
if {$::accurate} { set attempts 10 }
for {set i 0} {$i < $attempts} {incr i} {
# wait for the replica to start reading the rdb
# using the log file since the replica only responds to INFO once in 2mb
wait_for_log_message -1 "*Loading DB in memory*" 5 2000 1
# add some additional random sleep so that we kill the master on a different place each time
after [expr {int(rand()*100)}]
# kill the replica connection on the master
set killed [$master client kill type replica]
if {[catch {
set res [wait_for_log_message -1 "*Internal error in RDB*" 5 100 10]
if {$::verbose} {
puts $res
}
}]} {
puts "failed triggering short read"
# force the replica to try another full sync
$master client kill type replica
$master set asdf asdf
# the side effect of resizing the backlog is that it is flushed (16k is the min size)
$master config set repl-backlog-size [expr {16384 + $i}]
}
# wait for loading to stop (fail)
wait_for_condition 100 10 {
[s -1 loading] eq 0
} else {
fail "Replica didn't disconnect"
}
}
# enable fast shutdown
$master config set rdb-key-save-delay 0
}
}
}
}
} }
...@@ -306,4 +306,18 @@ start_server {tags {"multi"}} { ...@@ -306,4 +306,18 @@ start_server {tags {"multi"}} {
} }
close_replication_stream $repl close_replication_stream $repl
} }
test {DISCARD should not fail during OOM} {
set rd [redis_deferring_client]
$rd config set maxmemory 1
assert {[$rd read] eq {OK}}
r multi
catch {r set x 1} e
assert_match {OOM*} $e
r discard
$rd config set maxmemory 0
assert {[$rd read] eq {OK}}
$rd close
r ping
} {PONG}
} }
...@@ -355,12 +355,12 @@ start_server {tags {"stream"} overrides {appendonly yes stream-node-max-entries ...@@ -355,12 +355,12 @@ start_server {tags {"stream"} overrides {appendonly yes stream-node-max-entries
r XADD mystream * xitem v r XADD mystream * xitem v
} }
r XTRIM mystream MAXLEN ~ 85 r XTRIM mystream MAXLEN ~ 85
assert {[r xlen mystream] == 89} assert {[r xlen mystream] == 90}
r config set stream-node-max-entries 1 r config set stream-node-max-entries 1
r debug loadaof r debug loadaof
r XADD mystream * xitem v r XADD mystream * xitem v
incr j incr j
assert {[r xlen mystream] == 90} assert {[r xlen mystream] == 91}
} }
} }
......
...@@ -16,7 +16,7 @@ To create a cluster, follow these steps: ...@@ -16,7 +16,7 @@ To create a cluster, follow these steps:
number of instances you want to create. number of instances you want to create.
2. Use "./create-cluster start" in order to run the instances. 2. Use "./create-cluster start" in order to run the instances.
3. Use "./create-cluster create" in order to execute redis-cli --cluster create, so that 3. Use "./create-cluster create" in order to execute redis-cli --cluster create, so that
an actual Redis cluster will be created. an actual Redis cluster will be created. (If you're accessing your setup via a local container, ensure that the CLUSTER_HOST value is changed to your local IP)
4. Now you are ready to play with the cluster. AOF files and logs for each instances are created in the current directory. 4. Now you are ready to play with the cluster. AOF files and logs for each instances are created in the current directory.
In order to stop a cluster: In order to stop a cluster:
......
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