Unverified Commit 2667c412 authored by Oran Agra's avatar Oran Agra Committed by GitHub
Browse files

Merge pull request #10829 from oranagra/release-7.0.1

Release 7.0.1
parents d375595d 595e725d
...@@ -632,4 +632,50 @@ tags {"aof external:skip"} { ...@@ -632,4 +632,50 @@ tags {"aof external:skip"} {
} result } result
assert_match "*Failed to truncate AOF*to timestamp*because it is not the last file*" $result assert_match "*Failed to truncate AOF*to timestamp*because it is not the last file*" $result
} }
start_server {overrides {appendonly yes appendfsync always}} {
test {FLUSHDB / FLUSHALL should persist in AOF} {
set aof [get_last_incr_aof_path r]
r set key value
r flushdb
r set key value2
r flushdb
# DB is empty
r flushdb
r flushdb
r flushdb
r set key value
r flushall
r set key value2
r flushall
# DBs are empty.
r flushall
r flushall
r flushall
# Assert that each FLUSHDB command is persisted even the DB is empty.
# Assert that each FLUSHALL command is persisted even the DBs are empty.
assert_aof_content $aof {
{select *}
{set key value}
{flushdb}
{set key value2}
{flushdb}
{flushdb}
{flushdb}
{flushdb}
{set key value}
{flushall}
{set key value2}
{flushall}
{flushall}
{flushall}
{flushall}
}
}
}
} }
...@@ -9,7 +9,7 @@ if {$system_name eq {darwin}} { ...@@ -9,7 +9,7 @@ if {$system_name eq {darwin}} {
} elseif {$system_name eq {linux}} { } elseif {$system_name eq {linux}} {
# Avoid the test on libmusl, which does not support backtrace # Avoid the test on libmusl, which does not support backtrace
set ldd [exec ldd src/redis-server] set ldd [exec ldd src/redis-server]
if {![string match {*libc.musl*} $ldd]} { if {![string match {*libc.*musl*} $ldd]} {
set backtrace_supported 1 set backtrace_supported 1
} }
} }
......
...@@ -278,7 +278,7 @@ start_server {overrides {save ""}} { ...@@ -278,7 +278,7 @@ start_server {overrides {save ""}} {
set current_save_keys_total [s current_save_keys_total] set current_save_keys_total [s current_save_keys_total]
if {$::verbose} { if {$::verbose} {
puts "Keys before bgsave start: current_save_keys_total" puts "Keys before bgsave start: $current_save_keys_total"
} }
# on each iteration, we will write some key to the server to trigger copy-on-write, and # on each iteration, we will write some key to the server to trigger copy-on-write, and
...@@ -366,4 +366,49 @@ start_server [list overrides [list "dir" $server_path "dbfilename" "scriptbackup ...@@ -366,4 +366,49 @@ start_server [list overrides [list "dir" $server_path "dbfilename" "scriptbackup
} }
} }
start_server {} {
test "failed bgsave prevents writes" {
r config set rdb-key-save-delay 10000000
populate 1000
r set x x
r bgsave
set pid1 [get_child_pid 0]
catch {exec kill -9 $pid1}
waitForBgsave r
# make sure a read command succeeds
assert_equal [r get x] x
# make sure a write command fails
assert_error {MISCONF *} {r set x y}
# repeate with script
assert_error {MISCONF *} {r eval {
return redis.call('set','x',1)
} 1 x
}
assert_equal {x} [r eval {
return redis.call('get','x')
} 1 x
]
# again with script using shebang
assert_error {MISCONF *} {r eval {#!lua
return redis.call('set','x',1)
} 1 x
}
assert_equal {x} [r eval {#!lua flags=no-writes
return redis.call('get','x')
} 1 x
]
r config set rdb-key-save-delay 0
r bgsave
waitForBgsave r
# server is writable again
r set x y
} {OK}
}
} ;# tags } ;# tags
...@@ -225,11 +225,45 @@ start_server {tags {"repl external:skip"}} { ...@@ -225,11 +225,45 @@ start_server {tags {"repl external:skip"}} {
} }
} }
test {FLUSHALL should replicate} { test {FLUSHDB / FLUSHALL should replicate} {
set repl [attach_to_replication_stream]
r -1 set key value
r -1 flushdb
r -1 set key value2
r -1 flushall
wait_for_ofs_sync [srv 0 client] [srv -1 client]
assert_equal [r -1 dbsize] 0
assert_equal [r 0 dbsize] 0
# DB is empty.
r -1 flushdb
r -1 flushdb
r -1 flushdb
# DBs are empty.
r -1 flushall
r -1 flushall
r -1 flushall r -1 flushall
if {$::valgrind} {after 2000}
list [r -1 dbsize] [r 0 dbsize] # Assert that each FLUSHDB command is replicated even the DB is empty.
} {0 0} # Assert that each FLUSHALL command is replicated even the DBs are empty.
assert_replication_stream $repl {
{set key value}
{flushdb}
{set key value2}
{flushall}
{flushdb}
{flushdb}
{flushdb}
{flushall}
{flushall}
{flushall}
}
close_replication_stream $repl
}
test {ROLE in master reports master with a slave} { test {ROLE in master reports master with a slave} {
set res [r -1 role] set res [r -1 role]
......
...@@ -23,7 +23,8 @@ void done_handler(int exitcode, int bysignal, void *user_data) { ...@@ -23,7 +23,8 @@ void done_handler(int exitcode, int bysignal, void *user_data) {
int fork_create(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) int fork_create(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{ {
long long code_to_exit_with; long long code_to_exit_with;
if (argc != 2) { long long usleep_us;
if (argc != 3) {
RedisModule_WrongArity(ctx); RedisModule_WrongArity(ctx);
return REDISMODULE_OK; return REDISMODULE_OK;
} }
...@@ -34,20 +35,22 @@ int fork_create(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) ...@@ -34,20 +35,22 @@ int fork_create(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
} }
RedisModule_StringToLongLong(argv[1], &code_to_exit_with); RedisModule_StringToLongLong(argv[1], &code_to_exit_with);
RedisModule_StringToLongLong(argv[2], &usleep_us);
exitted_with_code = -1; exitted_with_code = -1;
child_pid = RedisModule_Fork(done_handler, (void*)0xdeadbeef); int fork_child_pid = RedisModule_Fork(done_handler, (void*)0xdeadbeef);
if (child_pid < 0) { if (fork_child_pid < 0) {
RedisModule_ReplyWithError(ctx, "Fork failed"); RedisModule_ReplyWithError(ctx, "Fork failed");
return REDISMODULE_OK; return REDISMODULE_OK;
} else if (child_pid > 0) { } else if (fork_child_pid > 0) {
/* parent */ /* parent */
child_pid = fork_child_pid;
RedisModule_ReplyWithLongLong(ctx, child_pid); RedisModule_ReplyWithLongLong(ctx, child_pid);
return REDISMODULE_OK; return REDISMODULE_OK;
} }
/* child */ /* child */
RedisModule_Log(ctx, "notice", "fork child started"); RedisModule_Log(ctx, "notice", "fork child started");
usleep(500000); usleep(usleep_us);
RedisModule_Log(ctx, "notice", "fork child exiting"); RedisModule_Log(ctx, "notice", "fork child exiting");
RedisModule_ExitFromChild(code_to_exit_with); RedisModule_ExitFromChild(code_to_exit_with);
/* unreachable */ /* unreachable */
......
...@@ -310,6 +310,50 @@ int test_monotonic_time(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) ...@@ -310,6 +310,50 @@ int test_monotonic_time(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
return REDISMODULE_OK; return REDISMODULE_OK;
} }
/* wrapper for RM_Call */
int test_rm_call(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){
if(argc < 2){
return RedisModule_WrongArity(ctx);
}
const char* cmd = RedisModule_StringPtrLen(argv[1], NULL);
RedisModuleCallReply* rep = RedisModule_Call(ctx, cmd, "Ev", argv + 2, argc - 2);
if(!rep){
RedisModule_ReplyWithError(ctx, "NULL reply returned");
}else{
RedisModule_ReplyWithCallReply(ctx, rep);
RedisModule_FreeCallReply(rep);
}
return REDISMODULE_OK;
}
/* wrapper for RM_Call with flags */
int test_rm_call_flags(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){
if(argc < 3){
return RedisModule_WrongArity(ctx);
}
/* Append Ev to the provided flags. */
RedisModuleString *flags = RedisModule_CreateStringFromString(ctx, argv[1]);
RedisModule_StringAppendBuffer(ctx, flags, "Ev", 2);
const char* flg = RedisModule_StringPtrLen(flags, NULL);
const char* cmd = RedisModule_StringPtrLen(argv[2], NULL);
RedisModuleCallReply* rep = RedisModule_Call(ctx, cmd, flg, argv + 3, argc - 3);
if(!rep){
RedisModule_ReplyWithError(ctx, "NULL reply returned");
}else{
RedisModule_ReplyWithCallReply(ctx, rep);
RedisModule_FreeCallReply(rep);
}
RedisModule_FreeString(ctx, flags);
return REDISMODULE_OK;
}
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
REDISMODULE_NOT_USED(argv); REDISMODULE_NOT_USED(argv);
REDISMODULE_NOT_USED(argc); REDISMODULE_NOT_USED(argc);
...@@ -351,6 +395,10 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) ...@@ -351,6 +395,10 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
return REDISMODULE_ERR; return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.monotonic_time", test_monotonic_time,"",0,0,0) == REDISMODULE_ERR) if (RedisModule_CreateCommand(ctx,"test.monotonic_time", test_monotonic_time,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR; return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx, "test.rm_call", test_rm_call,"allow-stale", 0, 0, 0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx, "test.rm_call_flags", test_rm_call_flags,"allow-stale", 0, 0, 0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
return REDISMODULE_OK; return REDISMODULE_OK;
} }
...@@ -121,13 +121,13 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) ...@@ -121,13 +121,13 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
} }
/* On the stack to make sure we're copying them. */ /* On the stack to make sure we're copying them. */
const char *enum_vals[] = {"none", "one", "two", "three"}; const char *enum_vals[] = {"none", "five", "one", "two", "four"};
const int int_vals[] = {0, 1, 2, 4}; const int int_vals[] = {0, 5, 1, 2, 4};
if (RedisModule_RegisterEnumConfig(ctx, "enum", 1, REDISMODULE_CONFIG_DEFAULT, enum_vals, int_vals, 4, getEnumConfigCommand, setEnumConfigCommand, NULL, NULL) == REDISMODULE_ERR) { if (RedisModule_RegisterEnumConfig(ctx, "enum", 1, REDISMODULE_CONFIG_DEFAULT, enum_vals, int_vals, 5, getEnumConfigCommand, setEnumConfigCommand, NULL, NULL) == REDISMODULE_ERR) {
return REDISMODULE_ERR; return REDISMODULE_ERR;
} }
if (RedisModule_RegisterEnumConfig(ctx, "flags", 3, REDISMODULE_CONFIG_DEFAULT | REDISMODULE_CONFIG_BITFLAGS, enum_vals, int_vals, 4, getFlagsConfigCommand, setFlagsConfigCommand, NULL, NULL) == REDISMODULE_ERR) { if (RedisModule_RegisterEnumConfig(ctx, "flags", 3, REDISMODULE_CONFIG_DEFAULT | REDISMODULE_CONFIG_BITFLAGS, enum_vals, int_vals, 5, getFlagsConfigCommand, setFlagsConfigCommand, NULL, NULL) == REDISMODULE_ERR) {
return REDISMODULE_ERR; return REDISMODULE_ERR;
} }
/* Memory config here. */ /* Memory config here. */
......
...@@ -79,7 +79,9 @@ test "Sentinels (re)connection following SENTINEL SET mymaster auth-pass" { ...@@ -79,7 +79,9 @@ test "Sentinels (re)connection following SENTINEL SET mymaster auth-pass" {
restart_instance sentinel $sent2re restart_instance sentinel $sent2re
# Verify sentinel that restarted failed to connect master # Verify sentinel that restarted failed to connect master
if {![string match "*disconnected*" [dict get [S $sent2re SENTINEL MASTER mymaster] flags]]} { wait_for_condition 100 50 {
[string match "*disconnected*" [dict get [S $sent2re SENTINEL MASTER mymaster] flags]] != 0
} else {
fail "Expected to be disconnected from master due to wrong password" fail "Expected to be disconnected from master due to wrong password"
} }
......
...@@ -262,16 +262,22 @@ proc create_server_config_file {filename config} { ...@@ -262,16 +262,22 @@ proc create_server_config_file {filename config} {
close $fp close $fp
} }
proc spawn_server {config_file stdout stderr} { proc spawn_server {config_file stdout stderr args} {
set cmd [list src/redis-server $config_file]
set args {*}$args
if {[llength $args] > 0} {
lappend cmd {*}$args
}
if {$::valgrind} { if {$::valgrind} {
set pid [exec valgrind --track-origins=yes --trace-children=yes --suppressions=[pwd]/src/valgrind.sup --show-reachable=no --show-possibly-lost=no --leak-check=full src/redis-server $config_file >> $stdout 2>> $stderr &] set pid [exec valgrind --track-origins=yes --trace-children=yes --suppressions=[pwd]/src/valgrind.sup --show-reachable=no --show-possibly-lost=no --leak-check=full {*}$cmd >> $stdout 2>> $stderr &]
} elseif ($::stack_logging) { } elseif ($::stack_logging) {
set pid [exec /usr/bin/env MallocStackLogging=1 MallocLogFile=/tmp/malloc_log.txt src/redis-server $config_file >> $stdout 2>> $stderr &] set pid [exec /usr/bin/env MallocStackLogging=1 MallocLogFile=/tmp/malloc_log.txt {*}$cmd >> $stdout 2>> $stderr &]
} else { } else {
# ASAN_OPTIONS environment variable is for address sanitizer. If a test # ASAN_OPTIONS environment variable is for address sanitizer. If a test
# tries to allocate huge memory area and expects allocator to return # tries to allocate huge memory area and expects allocator to return
# NULL, address sanitizer throws an error without this setting. # NULL, address sanitizer throws an error without this setting.
set pid [exec /usr/bin/env ASAN_OPTIONS=allocator_may_return_null=1 src/redis-server $config_file >> $stdout 2>> $stderr &] set pid [exec /usr/bin/env ASAN_OPTIONS=allocator_may_return_null=1 {*}$cmd >> $stdout 2>> $stderr &]
} }
if {$::wait_server} { if {$::wait_server} {
...@@ -398,6 +404,7 @@ proc start_server {options {code undefined}} { ...@@ -398,6 +404,7 @@ proc start_server {options {code undefined}} {
set overrides {} set overrides {}
set omit {} set omit {}
set tags {} set tags {}
set args {}
set keep_persistence false set keep_persistence false
# parse options # parse options
...@@ -409,6 +416,9 @@ proc start_server {options {code undefined}} { ...@@ -409,6 +416,9 @@ proc start_server {options {code undefined}} {
"overrides" { "overrides" {
set overrides $value set overrides $value
} }
"args" {
set args $value
}
"omit" { "omit" {
set omit $value set omit $value
} }
...@@ -518,7 +528,7 @@ proc start_server {options {code undefined}} { ...@@ -518,7 +528,7 @@ proc start_server {options {code undefined}} {
send_data_packet $::test_server_fd "server-spawning" "port $port" send_data_packet $::test_server_fd "server-spawning" "port $port"
set pid [spawn_server $config_file $stdout $stderr] set pid [spawn_server $config_file $stdout $stderr $args]
# check that the server actually started # check that the server actually started
set port_busy [wait_server_started $config_file $stdout $pid] set port_busy [wait_server_started $config_file $stdout $pid]
...@@ -721,7 +731,7 @@ proc restart_server {level wait_ready rotate_logs {reconnect 1} {shutdown sigter ...@@ -721,7 +731,7 @@ proc restart_server {level wait_ready rotate_logs {reconnect 1} {shutdown sigter
set config_file [dict get $srv "config_file"] set config_file [dict get $srv "config_file"]
set pid [spawn_server $config_file $stdout $stderr] set pid [spawn_server $config_file $stdout $stderr {}]
# check that the server actually started # check that the server actually started
wait_server_started $config_file $stdout $pid wait_server_started $config_file $stdout $pid
......
...@@ -126,28 +126,36 @@ proc wait_for_condition {maxtries delay e _else_ elsescript} { ...@@ -126,28 +126,36 @@ proc wait_for_condition {maxtries delay e _else_ elsescript} {
} }
} }
# try to match a value to a list of patterns that is either regex, or plain sub-string # try to match a value to a list of patterns that are either regex (starts with "/") or plain string.
proc search_pattern_list {value pattern_list {substr false}} { # The caller can specify to use only glob-pattern match
set n 0 proc search_pattern_list {value pattern_list {glob_pattern false}} {
foreach el $pattern_list { foreach el $pattern_list {
if {[string length $el] > 0 && ((!$substr && [regexp -- $el $value]) || ($substr && [string match $el $value]))} { if {[string length $el] == 0} { continue }
return $n if { $glob_pattern } {
if {[string match $el $value]} {
return 1
} }
incr n continue
} }
return -1 if {[string equal / [string index $el 0]] && [regexp -- [string range $el 1 end] $value]} {
return 1
} elseif {[string equal $el $value]} {
return 1
}
}
return 0
} }
proc test {name code {okpattern undefined} {tags {}}} { proc test {name code {okpattern undefined} {tags {}}} {
# abort if test name in skiptests # abort if test name in skiptests
if {[search_pattern_list $name $::skiptests] >= 0} { if {[search_pattern_list $name $::skiptests]} {
incr ::num_skipped incr ::num_skipped
send_data_packet $::test_server_fd skip $name send_data_packet $::test_server_fd skip $name
return return
} }
# abort if only_tests was set but test name is not included # abort if only_tests was set but test name is not included
if {[llength $::only_tests] > 0 && [search_pattern_list $name $::only_tests] < 0} { if {[llength $::only_tests] > 0 && ![search_pattern_list $name $::only_tests]} {
incr ::num_skipped incr ::num_skipped
send_data_packet $::test_server_fd skip $name send_data_packet $::test_server_fd skip $name
return return
......
...@@ -588,15 +588,15 @@ proc print_help_screen {} { ...@@ -588,15 +588,15 @@ proc print_help_screen {} {
"--single <unit> Just execute the specified unit (see next option). This option can be repeated." "--single <unit> Just execute the specified unit (see next option). This option can be repeated."
"--verbose Increases verbosity." "--verbose Increases verbosity."
"--list-tests List all the available test units." "--list-tests List all the available test units."
"--only <test> Just execute tests that match <test> regexp. This option can be repeated." "--only <test> Just execute the specified test by test name or tests that match <test> regexp (if <test> starts with '/'). This option can be repeated."
"--skip-till <unit> Skip all units until (and including) the specified one." "--skip-till <unit> Skip all units until (and including) the specified one."
"--skipunit <unit> Skip one unit." "--skipunit <unit> Skip one unit."
"--clients <num> Number of test clients (default 16)." "--clients <num> Number of test clients (default 16)."
"--timeout <sec> Test timeout in seconds (default 20 min)." "--timeout <sec> Test timeout in seconds (default 20 min)."
"--force-failure Force the execution of a test that always fails." "--force-failure Force the execution of a test that always fails."
"--config <k> <v> Extra config file argument." "--config <k> <v> Extra config file argument."
"--skipfile <file> Name of a file containing test names or regexp patterns that should be skipped (one per line)." "--skipfile <file> Name of a file containing test names or regexp patterns (if <test> starts with '/') that should be skipped (one per line)."
"--skiptest <test> Test name or regexp pattern to skip. This option can be repeated." "--skiptest <test> Test name or regexp pattern (if <test> starts with '/') to skip. This option can be repeated."
"--tags <tags> Run only tests having specified tags or not having '-' prefixed tags." "--tags <tags> Run only tests having specified tags or not having '-' prefixed tags."
"--dont-clean Don't delete redis log files after the run." "--dont-clean Don't delete redis log files after the run."
"--no-latency Skip latency measurements and validation by some tests." "--no-latency Skip latency measurements and validation by some tests."
......
...@@ -529,6 +529,13 @@ start_server {tags {"acl external:skip"}} { ...@@ -529,6 +529,13 @@ start_server {tags {"acl external:skip"}} {
assert_equal [lsearch [r acl cat stream] "get"] -1 assert_equal [lsearch [r acl cat stream] "get"] -1
} }
test "ACL requires explicit permission for scripting for EVAL_RO, EVALSHA_RO and FCALL_RO" {
r ACL SETUSER scripter on nopass +readonly
assert_equal "This user has no permissions to run the 'eval_ro' command" [r ACL DRYRUN scripter EVAL_RO "" 0]
assert_equal "This user has no permissions to run the 'evalsha_ro' command" [r ACL DRYRUN scripter EVALSHA_RO "" 0]
assert_equal "This user has no permissions to run the 'fcall_ro' command" [r ACL DRYRUN scripter FCALL_RO "" 0]
}
test {ACL #5998 regression: memory leaks adding / removing subcommands} { test {ACL #5998 regression: memory leaks adding / removing subcommands} {
r AUTH default "" r AUTH default ""
r ACL setuser newuser reset -debug +debug|a +debug|b +debug|c r ACL setuser newuser reset -debug +debug|a +debug|b +debug|c
...@@ -926,3 +933,13 @@ start_server [list overrides [list "dir" $server_path "aclfile" "user.acl"] tags ...@@ -926,3 +933,13 @@ start_server [list overrides [list "dir" $server_path "aclfile" "user.acl"] tags
assert_match {*Duplicate user*} $err assert_match {*Duplicate user*} $err
} {} {external:skip} } {} {external:skip}
} }
start_server {overrides {user "default on nopass ~* +@all -flushdb"} tags {acl external:skip}} {
test {ACL from config file and config rewrite} {
assert_error {NOPERM *} {r flushdb}
r config rewrite
restart_server 0 true false
assert_error {NOPERM *} {r flushdb}
}
}
...@@ -424,7 +424,7 @@ start_server {tags {"scripting repl external:skip"}} { ...@@ -424,7 +424,7 @@ start_server {tags {"scripting repl external:skip"}} {
r -1 fcall test 0 r -1 fcall test 0
} e } e
set _ $e set _ $e
} {*Can not run script with write flag on readonly replica*} } {READONLY You can't write against a read only replica.}
} }
} }
...@@ -1052,7 +1052,7 @@ start_server {tags {"scripting"}} { ...@@ -1052,7 +1052,7 @@ start_server {tags {"scripting"}} {
r config set maxmemory 1 r config set maxmemory 1
catch {[r fcall f1 1 k]} e catch {[r fcall f1 1 k]} e
assert_match {*can not run it when used memory > 'maxmemory'*} $e assert_match {OOM *when used memory > 'maxmemory'*} $e
r config set maxmemory 0 r config set maxmemory 0
} {OK} {needs:config-maxmemory} } {OK} {needs:config-maxmemory}
...@@ -1064,11 +1064,8 @@ start_server {tags {"scripting"}} { ...@@ -1064,11 +1064,8 @@ start_server {tags {"scripting"}} {
r config set maxmemory 1 r config set maxmemory 1
catch {r fcall f1 1 k} e assert_equal [r fcall f1 1 k] hello
assert_match {*can not run it when used memory > 'maxmemory'*} $e assert_equal [r fcall_ro f1 1 k] hello
catch {r fcall_ro f1 1 k} e
assert_match {*can not run it when used memory > 'maxmemory'*} $e
r config set maxmemory 0 r config set maxmemory 0
} {OK} {needs:config-maxmemory} } {OK} {needs:config-maxmemory}
...@@ -1085,12 +1082,12 @@ start_server {tags {"scripting"}} { ...@@ -1085,12 +1082,12 @@ start_server {tags {"scripting"}} {
r replicaof 127.0.0.1 1 r replicaof 127.0.0.1 1
catch {[r fcall f1 0]} e catch {[r fcall f1 0]} e
assert_match {*'allow-stale' flag is not set on the script*} $e assert_match {MASTERDOWN *} $e
assert_equal {hello} [r fcall f2 0] assert_equal {hello} [r fcall f2 0]
catch {[r fcall f3 0]} e catch {[r fcall f3 0]} e
assert_match {*Can not execute the command on a stale replica*} $e assert_match {ERR *Can not execute the command on a stale replica*} $e
assert_match {*redis_version*} [r fcall f4 0] assert_match {*redis_version*} [r fcall f4 0]
...@@ -1141,6 +1138,23 @@ start_server {tags {"scripting"}} { ...@@ -1141,6 +1138,23 @@ start_server {tags {"scripting"}} {
r function stats r function stats
} {running_script {} engines {LUA {libraries_count 1 functions_count 1}}} } {running_script {} engines {LUA {libraries_count 1 functions_count 1}}}
test {FUNCTION - test function stats on loading failure} {
r FUNCTION FLUSH
r FUNCTION load {#!lua name=test1
redis.register_function('f1', function() return 1 end)
redis.register_function('f2', function() return 1 end)
}
catch {r FUNCTION load {#!lua name=test1
redis.register_function('f3', function() return 1 end)
}} e
assert_match "*Library 'test1' already exists*" $e
r function stats
} {running_script {} engines {LUA {libraries_count 1 functions_count 2}}}
test {FUNCTION - function stats cleaned after flush} { test {FUNCTION - function stats cleaned after flush} {
r function flush r function flush
r function stats r function stats
......
...@@ -166,11 +166,26 @@ start_server {tags {"introspection"}} { ...@@ -166,11 +166,26 @@ start_server {tags {"introspection"}} {
assert_match [r config get save] {save {3600 1 300 100 60 10000}} assert_match [r config get save] {save {3600 1 300 100 60 10000}}
} }
# First "save" keyword overrides defaults # First "save" keyword overrides hard coded defaults
start_server {config "minimal.conf" overrides {save {100 100}}} { start_server {config "minimal.conf" overrides {save {100 100}}} {
# Defaults # Defaults
assert_match [r config get save] {save {100 100}} assert_match [r config get save] {save {100 100}}
} }
# First "save" keyword in default config file
start_server {config "default.conf"} {
assert_match [r config get save] {save {900 1}}
}
# First "save" keyword appends default from config file
start_server {config "default.conf" args {--save 100 100}} {
assert_match [r config get save] {save {900 1 100 100}}
}
# Empty "save" keyword resets all
start_server {config "default.conf" args {--save {}}} {
assert_match [r config get save] {save {}}
}
} {} {external:skip} } {} {external:skip}
test {CONFIG sanity} { test {CONFIG sanity} {
...@@ -449,6 +464,70 @@ start_server {tags {"introspection"}} { ...@@ -449,6 +464,70 @@ start_server {tags {"introspection"}} {
assert {[dict exists $res bind]} assert {[dict exists $res bind]}
} }
test {redis-server command line arguments - error cases} {
catch {exec src/redis-server --port} err
assert_match {*'port'*wrong number of arguments*} $err
catch {exec src/redis-server --port 6380 --loglevel} err
assert_match {*'loglevel'*wrong number of arguments*} $err
# Take `6379` and `6380` as the port option value.
catch {exec src/redis-server --port 6379 6380} err
assert_match {*'port "6379" "6380"'*wrong number of arguments*} $err
# Take `--loglevel` and `verbose` as the port option value.
catch {exec src/redis-server --port --loglevel verbose} err
assert_match {*'port "--loglevel" "verbose"'*wrong number of arguments*} $err
# Take `--bla` as the port option value.
catch {exec src/redis-server --port --bla --loglevel verbose} err
assert_match {*'port "--bla"'*argument couldn't be parsed into an integer*} $err
# Take `--bla` as the loglevel option value.
catch {exec src/redis-server --logfile --my--log--file --loglevel --bla} err
assert_match {*'loglevel "--bla"'*argument(s) must be one of the following*} $err
# Using MULTI_ARG's own check, empty option value
catch {exec src/redis-server --shutdown-on-sigint} err
assert_match {*'shutdown-on-sigint'*argument(s) must be one of the following*} $err
catch {exec src/redis-server --shutdown-on-sigint "now force" --shutdown-on-sigterm} err
assert_match {*'shutdown-on-sigterm'*argument(s) must be one of the following*} $err
# Something like `redis-server --some-config --config-value1 --config-value2 --loglevel debug` would break,
# because if you want to pass a value to a config starting with `--`, it can only be a single value.
catch {exec src/redis-server --replicaof 127.0.0.1 abc} err
assert_match {*'replicaof "127.0.0.1" "abc"'*Invalid master port*} $err
catch {exec src/redis-server --replicaof --127.0.0.1 abc} err
assert_match {*'replicaof "--127.0.0.1" "abc"'*Invalid master port*} $err
catch {exec src/redis-server --replicaof --127.0.0.1 --abc} err
assert_match {*'replicaof "--127.0.0.1"'*wrong number of arguments*} $err
} {} {external:skip}
test {redis-server command line arguments - allow option value to use the `--` prefix} {
start_server {config "default.conf" args {--proc-title-template --my--title--template --loglevel verbose}} {
assert_match [r config get proc-title-template] {proc-title-template --my--title--template}
assert_match [r config get loglevel] {loglevel verbose}
}
} {} {external:skip}
test {redis-server command line arguments - save with empty input} {
# Take `--loglevel` as the save option value.
catch {exec src/redis-server --save --loglevel verbose} err
assert_match {*'save "--loglevel" "verbose"'*Invalid save parameters*} $err
start_server {config "default.conf" args {--save {} --loglevel verbose}} {
assert_match [r config get save] {save {}}
assert_match [r config get loglevel] {loglevel verbose}
}
} {} {external:skip}
test {redis-server command line arguments - take one bulk string with spaces for MULTI_ARG configs parsing} {
start_server {config "default.conf" args {--shutdown-on-sigint nosave force now --shutdown-on-sigterm "nosave force"}} {
assert_match [r config get shutdown-on-sigint] {shutdown-on-sigint {nosave now force}}
assert_match [r config get shutdown-on-sigterm] {shutdown-on-sigterm {nosave force}}
}
} {} {external:skip}
# Config file at this point is at a weird state, and includes all # Config file at this point is at a weird state, and includes all
# known keywords. Might be a good idea to avoid adding tests here. # known keywords. Might be a good idea to avoid adding tests here.
} }
...@@ -503,3 +582,38 @@ test {config during loading} { ...@@ -503,3 +582,38 @@ test {config during loading} {
exec kill [srv 0 pid] exec kill [srv 0 pid]
} }
} {} {external:skip} } {} {external:skip}
test {CONFIG REWRITE handles rename-command properly} {
start_server {tags {"introspection"} overrides {rename-command {flushdb badger}}} {
assert_error {ERR unknown command*} {r flushdb}
r config rewrite
restart_server 0 true false
assert_error {ERR unknown command*} {r flushdb}
}
} {} {external:skip}
test {CONFIG REWRITE handles alias config properly} {
start_server {tags {"introspection"} overrides {hash-max-listpack-entries 20 hash-max-ziplist-entries 21}} {
assert_equal [r config get hash-max-listpack-entries] {hash-max-listpack-entries 21}
assert_equal [r config get hash-max-ziplist-entries] {hash-max-ziplist-entries 21}
r config set hash-max-listpack-entries 100
r config rewrite
restart_server 0 true false
assert_equal [r config get hash-max-listpack-entries] {hash-max-listpack-entries 100}
}
# test the order doesn't matter
start_server {tags {"introspection"} overrides {hash-max-ziplist-entries 20 hash-max-listpack-entries 21}} {
assert_equal [r config get hash-max-listpack-entries] {hash-max-listpack-entries 21}
assert_equal [r config get hash-max-ziplist-entries] {hash-max-ziplist-entries 21}
r config set hash-max-listpack-entries 100
r config rewrite
restart_server 0 true false
assert_equal [r config get hash-max-listpack-entries] {hash-max-listpack-entries 100}
}
} {} {external:skip}
...@@ -13,7 +13,8 @@ start_server {tags {"modules"}} { ...@@ -13,7 +13,8 @@ start_server {tags {"modules"}} {
test {Module fork} { test {Module fork} {
# the argument to fork.create is the exitcode on termination # the argument to fork.create is the exitcode on termination
r fork.create 3 # the second argument to fork.create is passed to usleep
r fork.create 3 100000 ;# 100ms
wait_for_condition 20 100 { wait_for_condition 20 100 {
[r fork.exitcode] != -1 [r fork.exitcode] != -1
} else { } else {
...@@ -23,22 +24,25 @@ start_server {tags {"modules"}} { ...@@ -23,22 +24,25 @@ start_server {tags {"modules"}} {
} {3} } {3}
test {Module fork kill} { test {Module fork kill} {
r fork.create 3 # use a longer time to avoid the child exiting before being killed
after 250 r fork.create 3 100000000 ;# 100s
wait_for_condition 20 100 {
[count_log_message "fork child started"] == 2
} else {
fail "fork didn't start"
}
# module fork twice
assert_error {Fork failed} {r fork.create 0 1}
assert {[count_log_message "Can't fork for module: File exists"] eq "1"}
r fork.kill r fork.kill
assert {[count_log_message "fork child started"] eq "2"}
assert {[count_log_message "Received SIGUSR1 in child"] eq "1"} assert {[count_log_message "Received SIGUSR1 in child"] eq "1"}
# check that it wasn't printed again (the print belong to the previous test)
assert {[count_log_message "fork child exiting"] eq "1"} assert {[count_log_message "fork child exiting"] eq "1"}
} }
test {Module fork twice} {
r fork.create 0
after 250
catch {r fork.create 0}
assert {[count_log_message "Can't fork for module: File exists"] eq "1"}
}
test "Unload the module - fork" { test "Unload the module - fork" {
assert_equal {OK} [r module unload fork] assert_equal {OK} [r module unload fork]
} }
......
...@@ -133,6 +133,223 @@ start_server {tags {"modules"}} { ...@@ -133,6 +133,223 @@ start_server {tags {"modules"}} {
assert { [r test.monotonic_time] >= $x } assert { [r test.monotonic_time] >= $x }
} }
test {rm_call OOM} {
r config set maxmemory 1
r config set maxmemory-policy volatile-lru
# sanity test plain call
assert_equal {OK} [
r test.rm_call set x 1
]
# add the M flag
assert_error {OOM *} {
r test.rm_call_flags M set x 1
}
# test a non deny-oom command
assert_equal {1} [
r test.rm_call_flags M get x
]
r config set maxmemory 0
} {OK} {needs:config-maxmemory}
test {rm_call write flag} {
# add the W flag
assert_error {ERR Write command 'set' was called while write is not allowed.} {
r test.rm_call_flags W set x 1
}
# test a non deny-oom command
r test.rm_call_flags W get x
} {1}
test {rm_call EVAL} {
r test.rm_call eval {
redis.call('set','x',1)
return 1
} 1 x
assert_error {ERR Write commands are not allowed from read-only scripts.*} {
r test.rm_call eval {#!lua flags=no-writes
redis.call('set','x',1)
return 1
} 1 x
}
}
test {rm_call EVAL - OOM} {
r config set maxmemory 1
assert_error {OOM command not allowed when used memory > 'maxmemory'. script*} {
r test.rm_call eval {
redis.call('set','x',1)
return 1
} 1 x
}
r test.rm_call eval {#!lua flags=no-writes
redis.call('get','x')
return 2
} 1 x
assert_error {OOM allow-oom flag is not set on the script,*} {
r test.rm_call eval {#!lua
redis.call('get','x')
return 3
} 1 x
}
r test.rm_call eval {
redis.call('get','x')
return 4
} 1 x
r config set maxmemory 0
} {OK} {needs:config-maxmemory}
test "not enough good replicas" {
r set x "some value"
r config set min-replicas-to-write 1
# rm_call in script mode
assert_error {NOREPLICAS *} {r test.rm_call_flags S set x s}
assert_equal [
r test.rm_call eval {#!lua flags=no-writes
return redis.call('get','x')
} 1 x
] "some value"
assert_equal [
r test.rm_call eval {
return redis.call('get','x')
} 1 x
] "some value"
assert_error {NOREPLICAS *} {
r test.rm_call eval {#!lua
return redis.call('get','x')
} 1 x
}
assert_error {NOREPLICAS *} {
r test.rm_call eval {
return redis.call('set','x', 1)
} 1 x
}
r config set min-replicas-to-write 0
}
test {rm_call EVAL - read-only replica} {
r replicaof 127.0.0.1 1
# rm_call in script mode
assert_error {READONLY *} {r test.rm_call_flags S set x 1}
assert_error {READONLY You can't write against a read only replica. script*} {
r test.rm_call eval {
redis.call('set','x',1)
return 1
} 1 x
}
r test.rm_call eval {#!lua flags=no-writes
redis.call('get','x')
return 2
} 1 x
assert_error {READONLY Can not run script with write flag on readonly replica*} {
r test.rm_call eval {#!lua
redis.call('get','x')
return 3
} 1 x
}
r test.rm_call eval {
redis.call('get','x')
return 4
} 1 x
r replicaof no one
} {OK} {needs:config-maxmemory}
test {rm_call EVAL - stale replica} {
r replicaof 127.0.0.1 1
r config set replica-serve-stale-data no
# rm_call in script mode
assert_error {MASTERDOWN *} {
r test.rm_call_flags S get x
}
assert_error {MASTERDOWN *} {
r test.rm_call eval {#!lua flags=no-writes
redis.call('get','x')
return 2
} 1 x
}
assert_error {MASTERDOWN *} {
r test.rm_call eval {
redis.call('get','x')
return 4
} 1 x
}
r replicaof no one
r config set replica-serve-stale-data yes
} {OK} {needs:config-maxmemory}
test "rm_call EVAL - failed bgsave prevents writes" {
r config set rdb-key-save-delay 10000000
populate 1000
r set x x
r bgsave
set pid1 [get_child_pid 0]
catch {exec kill -9 $pid1}
waitForBgsave r
# make sure a read command succeeds
assert_equal [r get x] x
# make sure a write command fails
assert_error {MISCONF *} {r set x y}
# rm_call in script mode
assert_error {MISCONF *} {r test.rm_call_flags S set x 1}
# repeate with script
assert_error {MISCONF *} {r test.rm_call eval {
return redis.call('set','x',1)
} 1 x
}
assert_equal {x} [r test.rm_call eval {
return redis.call('get','x')
} 1 x
]
# again with script using shebang
assert_error {MISCONF *} {r test.rm_call eval {#!lua
return redis.call('set','x',1)
} 1 x
}
assert_equal {x} [r test.rm_call eval {#!lua flags=no-writes
return redis.call('get','x')
} 1 x
]
r config set rdb-key-save-delay 0
r bgsave
waitForBgsave r
# server is writable again
r set x y
} {OK}
test "Unload the module - misc" { test "Unload the module - misc" {
assert_equal {OK} [r module unload misc] assert_equal {OK} [r module unload misc]
} }
......
...@@ -32,12 +32,27 @@ start_server {tags {"modules"}} { ...@@ -32,12 +32,27 @@ start_server {tags {"modules"}} {
assert_equal [r config get moduleconfigs.enum] "moduleconfigs.enum two" assert_equal [r config get moduleconfigs.enum] "moduleconfigs.enum two"
r config set moduleconfigs.flags two r config set moduleconfigs.flags two
assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags two" assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags two"
r config set moduleconfigs.flags "two three"
assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags {two three}"
r config set moduleconfigs.numeric -2 r config set moduleconfigs.numeric -2
assert_equal [r config get moduleconfigs.numeric] "moduleconfigs.numeric -2" assert_equal [r config get moduleconfigs.numeric] "moduleconfigs.numeric -2"
} }
test {Config set commands enum flags} {
r config set moduleconfigs.flags "none"
assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags none"
r config set moduleconfigs.flags "two four"
assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags {two four}"
r config set moduleconfigs.flags "five"
assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags five"
r config set moduleconfigs.flags "one four"
assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags five"
r config set moduleconfigs.flags "one two four"
assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags {five two}"
}
test {Immutable flag works properly and rejected strings dont leak} { test {Immutable flag works properly and rejected strings dont leak} {
# Configs flagged immutable should not allow sets # Configs flagged immutable should not allow sets
catch {[r config set moduleconfigs.immutable_bool yes]} e catch {[r config set moduleconfigs.immutable_bool yes]} e
...@@ -58,7 +73,7 @@ start_server {tags {"modules"}} { ...@@ -58,7 +73,7 @@ start_server {tags {"modules"}} {
test {Enums only able to be set to passed in values} { test {Enums only able to be set to passed in values} {
# Module authors specify what values are valid for enums, check that only those values are ok on a set # Module authors specify what values are valid for enums, check that only those values are ok on a set
catch {[r config set moduleconfigs.enum four]} e catch {[r config set moduleconfigs.enum asdf]} e
assert_match {*must be one of the following*} $e assert_match {*must be one of the following*} $e
} }
...@@ -147,7 +162,7 @@ start_server {tags {"modules"}} { ...@@ -147,7 +162,7 @@ start_server {tags {"modules"}} {
r config set moduleconfigs.mutable_bool yes r config set moduleconfigs.mutable_bool yes
r config set moduleconfigs.memory_numeric 750 r config set moduleconfigs.memory_numeric 750
r config set moduleconfigs.enum two r config set moduleconfigs.enum two
r config set moduleconfigs.flags "two three" r config set moduleconfigs.flags "four two"
r config rewrite r config rewrite
restart_server 0 true false restart_server 0 true false
# Ensure configs we rewrote are present and that the conf file is readable # Ensure configs we rewrote are present and that the conf file is readable
...@@ -155,7 +170,7 @@ start_server {tags {"modules"}} { ...@@ -155,7 +170,7 @@ start_server {tags {"modules"}} {
assert_equal [r config get moduleconfigs.memory_numeric] "moduleconfigs.memory_numeric 750" assert_equal [r config get moduleconfigs.memory_numeric] "moduleconfigs.memory_numeric 750"
assert_equal [r config get moduleconfigs.string] "moduleconfigs.string {super \0secret password}" assert_equal [r config get moduleconfigs.string] "moduleconfigs.string {super \0secret password}"
assert_equal [r config get moduleconfigs.enum] "moduleconfigs.enum two" assert_equal [r config get moduleconfigs.enum] "moduleconfigs.enum two"
assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags {two three}" assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags {two four}"
assert_equal [r config get moduleconfigs.numeric] "moduleconfigs.numeric -1" assert_equal [r config get moduleconfigs.numeric] "moduleconfigs.numeric -1"
r module unload moduleconfigs r module unload moduleconfigs
} }
...@@ -230,12 +245,12 @@ start_server {tags {"modules"}} { ...@@ -230,12 +245,12 @@ start_server {tags {"modules"}} {
set stdout [dict get $noload stdout] set stdout [dict get $noload stdout]
assert_equal [count_message_lines $stdout "Module Configurations were not set, likely a missing LoadConfigs call. Unloading the module."] 1 assert_equal [count_message_lines $stdout "Module Configurations were not set, likely a missing LoadConfigs call. Unloading the module."] 1
start_server [list overrides [list loadmodule "$testmodule" moduleconfigs.string "bootedup" moduleconfigs.enum two moduleconfigs.flags "two three"]] { start_server [list overrides [list loadmodule "$testmodule" moduleconfigs.string "bootedup" moduleconfigs.enum two moduleconfigs.flags "two four"]] {
assert_equal [r config get moduleconfigs.string] "moduleconfigs.string bootedup" assert_equal [r config get moduleconfigs.string] "moduleconfigs.string bootedup"
assert_equal [r config get moduleconfigs.mutable_bool] "moduleconfigs.mutable_bool yes" assert_equal [r config get moduleconfigs.mutable_bool] "moduleconfigs.mutable_bool yes"
assert_equal [r config get moduleconfigs.immutable_bool] "moduleconfigs.immutable_bool no" assert_equal [r config get moduleconfigs.immutable_bool] "moduleconfigs.immutable_bool no"
assert_equal [r config get moduleconfigs.enum] "moduleconfigs.enum two" assert_equal [r config get moduleconfigs.enum] "moduleconfigs.enum two"
assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags {two three}" assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags {two four}"
assert_equal [r config get moduleconfigs.numeric] "moduleconfigs.numeric -1" assert_equal [r config get moduleconfigs.numeric] "moduleconfigs.numeric -1"
assert_equal [r config get moduleconfigs.memory_numeric] "moduleconfigs.memory_numeric 1024" assert_equal [r config get moduleconfigs.memory_numeric] "moduleconfigs.memory_numeric 1024"
} }
......
...@@ -11,7 +11,7 @@ start_server {tags {"modules"}} { ...@@ -11,7 +11,7 @@ start_server {tags {"modules"}} {
assert_equal {1} [subscribe $rd2 {chan1}] assert_equal {1} [subscribe $rd2 {chan1}]
assert_equal 1 [r publish.shard chan1 hello] assert_equal 1 [r publish.shard chan1 hello]
assert_equal 1 [r publish.classic chan1 world] assert_equal 1 [r publish.classic chan1 world]
assert_equal {message chan1 hello} [$rd1 read] assert_equal {smessage chan1 hello} [$rd1 read]
assert_equal {message chan1 world} [$rd2 read] assert_equal {message chan1 world} [$rd2 read]
} }
} }
...@@ -38,9 +38,25 @@ start_server {tags {"other"}} { ...@@ -38,9 +38,25 @@ start_server {tags {"other"}} {
} }
} }
start_server {overrides {save ""} tags {external:skip}} {
test {FLUSHALL should not reset the dirty counter if we disable save} {
r set key value
r flushall
assert_morethan [s rdb_changes_since_last_save] 0
}
test {FLUSHALL should reset the dirty counter to 0 if we enable save} {
r config set save "3600 1 300 100 60 10000"
r set key value
r flushall
assert_equal [s rdb_changes_since_last_save] 0
}
}
test {BGSAVE} { test {BGSAVE} {
r flushdb # Use FLUSHALL instead of FLUSHDB, FLUSHALL do a foreground save
waitForBgsave r # and reset the dirty counter to 0, so we won't trigger an unexpected bgsave.
r flushall
r save r save
r set x 10 r set x 10
r bgsave r bgsave
......
...@@ -47,18 +47,14 @@ start_server {tags {"pause network"}} { ...@@ -47,18 +47,14 @@ start_server {tags {"pause network"}} {
test "Test read/admin mutli-execs are not blocked by pause RO" { test "Test read/admin mutli-execs are not blocked by pause RO" {
r SET FOO BAR r SET FOO BAR
r client PAUSE 100000 WRITE r client PAUSE 100000 WRITE
set rd [redis_deferring_client] set rr [redis_client]
$rd MULTI assert_equal [$rr MULTI] "OK"
assert_equal [$rd read] "OK" assert_equal [$rr PING] "QUEUED"
$rd PING assert_equal [$rr GET FOO] "QUEUED"
assert_equal [$rd read] "QUEUED" assert_match "PONG BAR" [$rr EXEC]
$rd GET FOO
assert_equal [$rd read] "QUEUED"
$rd EXEC
assert_equal [s 0 blocked_clients] 0 assert_equal [s 0 blocked_clients] 0
r client unpause r client unpause
assert_match "PONG BAR" [$rd read] $rr close
$rd close
} }
test "Test write mutli-execs are blocked by pause RO" { test "Test write mutli-execs are blocked by pause RO" {
...@@ -78,20 +74,129 @@ start_server {tags {"pause network"}} { ...@@ -78,20 +74,129 @@ start_server {tags {"pause network"}} {
test "Test scripts are blocked by pause RO" { test "Test scripts are blocked by pause RO" {
r client PAUSE 60000 WRITE r client PAUSE 60000 WRITE
set rd [redis_deferring_client] set rd [redis_deferring_client]
set rd2 [redis_deferring_client]
$rd EVAL "return 1" 0 $rd EVAL "return 1" 0
wait_for_blocked_clients_count 1 50 100 # test a script with a shebang and no flags for coverage
$rd2 EVAL {#!lua
return 1
} 0
wait_for_blocked_clients_count 2 50 100
r client unpause r client unpause
assert_match "1" [$rd read] assert_match "1" [$rd read]
assert_match "1" [$rd2 read]
$rd close $rd close
$rd2 close
}
test "Test RO scripts are not blocked by pause RO" {
r set x y
# create a function for later
r FUNCTION load replace {#!lua name=f1
redis.register_function{
function_name='f1',
callback=function() return "hello" end,
flags={'no-writes'}
} }
}
r client PAUSE 6000000 WRITE
set rr [redis_client]
# test an eval that's for sure not in the script cache
assert_equal [$rr EVAL {#!lua flags=no-writes
return 'unique script'
} 0
] "unique script"
# for sanity, repeat that EVAL on a script that's already cached
assert_equal [$rr EVAL {#!lua flags=no-writes
return 'unique script'
} 0
] "unique script"
# test EVAL_RO on a unique script that's for sure not in the cache
assert_equal [$rr EVAL_RO {
return redis.call('GeT', 'x')..' unique script'
} 1 x
] "y unique script"
# test with evalsha
set sha [$rr script load {#!lua flags=no-writes
return 2
}]
assert_equal [$rr EVALSHA $sha 0] 2
# test with function
assert_equal [$rr fcall f1 0] hello
r client unpause
$rr close
}
test "Test read-only scripts in mutli-exec are not blocked by pause RO" {
r SET FOO BAR
r client PAUSE 100000 WRITE
set rr [redis_client]
assert_equal [$rr MULTI] "OK"
assert_equal [$rr EVAL {#!lua flags=no-writes
return 12
} 0
] QUEUED
assert_equal [$rr EVAL {#!lua flags=no-writes
return 13
} 0
] QUEUED
assert_match "12 13" [$rr EXEC]
assert_equal [s 0 blocked_clients] 0
r client unpause
$rr close
}
test "Test write scripts in mutli-exec are blocked by pause RO" {
set rd [redis_deferring_client]
set rd2 [redis_deferring_client]
# one with a shebang
$rd MULTI
assert_equal [$rd read] "OK"
$rd EVAL {#!lua
return 12
} 0
assert_equal [$rd read] "QUEUED"
# one without a shebang
$rd2 MULTI
assert_equal [$rd2 read] "OK"
$rd2 EVAL {#!lua
return 13
} 0
assert_equal [$rd2 read] "QUEUED"
test "Test may-replicate commands are rejected in ro script by pause RO" {
r client PAUSE 60000 WRITE r client PAUSE 60000 WRITE
assert_error {ERR May-replicate commands are not allowed when client pause write*} { $rd EXEC
$rd2 EXEC
wait_for_blocked_clients_count 2 50 100
r client unpause
assert_match "12" [$rd read]
assert_match "13" [$rd2 read]
$rd close
$rd2 close
}
test "Test may-replicate commands are rejected in RO scripts" {
# that's specifically important for CLIENT PAUSE WRITE
assert_error {ERR Write commands are not allowed from read-only scripts. script:*} {
r EVAL_RO "return redis.call('publish','ch','msg')" 0 r EVAL_RO "return redis.call('publish','ch','msg')" 0
} }
r client unpause assert_error {ERR Write commands are not allowed from read-only scripts. script:*} {
r EVAL {#!lua flags=no-writes
return redis.call('publish','ch','msg')
} 0
}
# make sure that publish isn't blocked from a non-RO script
assert_equal [r EVAL "return redis.call('publish','ch','msg')" 0] 0
} }
test "Test multiple clients can be queued up and unblocked" { test "Test multiple clients can be queued up and unblocked" {
......
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