Unverified Commit 53c43fcc authored by Vo Trong Phuc's avatar Vo Trong Phuc Committed by GitHub
Browse files

Add check min-slave-* feature when evaluating Lua scripts and Functions (#10160)



Add check enough good slaves for write command when evaluating scripts.
This check is made before the script is executed, if we have function flags, and per redis command if we don't.
Co-authored-by: default avatarPhuc. Vo Trong <phucvt@vng.com.vn>
Co-authored-by: default avatarOran Agra <oran@redislabs.com>
Co-authored-by: default avatarMeir Shpilraien (Spielrein) <meir@redis.com>
parent f4ecc799
......@@ -146,6 +146,7 @@ int scriptPrepareForRun(scriptRunCtx *run_ctx, client *engine_client, client *ca
return C_ERR;
}
/* Deny writes if we're unale to persist. */
int deny_write_type = writeCommandsDeniedByDiskError();
if (deny_write_type != DISK_ERROR_TYPE_NONE && server.masterhost == NULL) {
if (deny_write_type == DISK_ERROR_TYPE_RDB)
......@@ -164,6 +165,17 @@ int scriptPrepareForRun(scriptRunCtx *run_ctx, client *engine_client, client *ca
addReplyError(caller, "Can not execute a script with write flag using *_ro command.");
return C_ERR;
}
/* Don't accept write commands if there are not enough good slaves and
* user configured the min-slaves-to-write option. */
if (server.masterhost == NULL &&
server.repl_min_slaves_max_lag &&
server.repl_min_slaves_to_write &&
server.repl_good_slaves_count < server.repl_min_slaves_to_write)
{
addReplyErrorObject(caller, shared.noreplicaserr);
return C_ERR;
}
}
} else {
/* Special handling for backwards compatibility (no shebang eval[sha]) mode */
......@@ -359,6 +371,19 @@ static int scriptVerifyWriteCommandAllow(scriptRunCtx *run_ctx, char **err) {
return C_ERR;
}
/* Don't accept write commands if there are not enough good slaves and
* user configured the min-slaves-to-write option. Note this only reachable
* for Eval scripts that didn't declare flags, see the other check in
* scriptPrepareForRun */
if (server.masterhost == NULL &&
server.repl_min_slaves_max_lag &&
server.repl_min_slaves_to_write &&
server.repl_good_slaves_count < server.repl_min_slaves_to_write)
{
*err = sdsdup(shared.noreplicaserr->ptr);
return C_ERR;
}
return C_OK;
}
......
......@@ -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]
}
}
......
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