mirror of
https://codeberg.org/redict/redict.git
synced 2025-01-22 16:18:28 -05:00
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: Phuc. Vo Trong <phucvt@vng.com.vn> Co-authored-by: Oran Agra <oran@redislabs.com> Co-authored-by: Meir Shpilraien (Spielrein) <meir@redis.com>
This commit is contained in:
parent
f4ecc799c8
commit
53c43fcc84
25
src/script.c
25
src/script.c
@ -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]
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user