
In standalone mode, when a `-REDIRECT` error occurs, special handling is required if the client is in the `MULTI` context. We have adopted the same handling method as the cluster mode: 1. If a command in the transaction encounters a `REDIRECT` at the time of queuing, the execution of `EXEC` will return an `EXECABORT` error (we expect the client to redirect and discard the transaction upon receiving a `REDIRECT`). That is: ``` MULTI ==> +OK SET x y ==> -REDIRECT EXEC ==> -EXECABORT ``` 2. If all commands are successfully queued (i.e., `QUEUED` results are received) but a redirect is detected during `EXEC` execution (such as a primary-replica switch), a `REDIRECT` is returned to instruct the client to perform a redirect. That is: ``` MULTI ==> +OK SET x y ==> +QUEUED failover EXEC ==> -REDIRECT ``` --------- Signed-off-by: zhaozhao.zz <zhaozhao.zz@alibaba-inc.com>
96 lines
3.1 KiB
Tcl
96 lines
3.1 KiB
Tcl
start_server {tags {needs:repl external:skip}} {
|
|
start_server {} {
|
|
set primary [srv -1 client]
|
|
set primary_host [srv -1 host]
|
|
set primary_port [srv -1 port]
|
|
set primary_pid [srv -1 pid]
|
|
|
|
set replica_host [srv 0 host]
|
|
set replica_port [srv 0 port]
|
|
set replica_pid [srv 0 pid]
|
|
|
|
test {write command inside MULTI is QUEUED, EXEC should be REDIRECT} {
|
|
set rr [valkey_client]
|
|
$rr client capa redirect
|
|
$rr multi
|
|
assert_equal "QUEUED" [$rr set foo bar]
|
|
|
|
# Change the current instance to be a replica
|
|
r replicaof $primary_host $primary_port
|
|
wait_replica_online $primary
|
|
|
|
assert_error "REDIRECT*" {$rr exec}
|
|
$rr close
|
|
}
|
|
|
|
test {write command inside MULTI is REDIRECT, EXEC should be EXECABORT} {
|
|
set rr [valkey_client]
|
|
$rr client capa redirect
|
|
$rr multi
|
|
assert_error "REDIRECT*" {$rr set foo bar}
|
|
assert_error "EXECABORT*" {$rr exec}
|
|
$rr close
|
|
}
|
|
|
|
test {replica allow read command by default} {
|
|
r get foo
|
|
} {}
|
|
|
|
test {replica reply READONLY error for write command by default} {
|
|
assert_error {READONLY*} {r set foo bar}
|
|
}
|
|
|
|
test {replica redirect read and write command after CLIENT CAPA REDIRECT} {
|
|
r client capa redirect
|
|
assert_error "REDIRECT $primary_host:$primary_port" {r set foo bar}
|
|
assert_error "REDIRECT $primary_host:$primary_port" {r get foo}
|
|
}
|
|
|
|
test {non-data access commands are not redirected} {
|
|
r ping
|
|
} {PONG}
|
|
|
|
test {replica allow read command in READONLY mode} {
|
|
r readonly
|
|
r get foo
|
|
} {}
|
|
|
|
test {client paused during failover-in-progress} {
|
|
pause_process $replica_pid
|
|
# replica will never acknowledge this write
|
|
r -1 set foo bar
|
|
r -1 failover to $replica_host $replica_port TIMEOUT 100 FORCE
|
|
|
|
# Wait for primary to give up on sync attempt and start failover
|
|
wait_for_condition 50 100 {
|
|
[s -1 master_failover_state] == "failover-in-progress"
|
|
} else {
|
|
fail "Failover from primary to replica did not timeout"
|
|
}
|
|
|
|
set rd [valkey_deferring_client -1]
|
|
$rd client capa redirect
|
|
assert_match "OK" [$rd read]
|
|
$rd set foo bar
|
|
|
|
# Client paused during failover-in-progress, see more details in PR #871
|
|
wait_for_blocked_clients_count 1 100 10 -1
|
|
|
|
resume_process $replica_pid
|
|
|
|
# Wait for failover to end
|
|
wait_for_condition 50 100 {
|
|
[s -1 master_failover_state] == "no-failover"
|
|
} else {
|
|
fail "Failover from primary to replica did not finish"
|
|
}
|
|
|
|
assert_match *master* [r role]
|
|
assert_match *slave* [r -1 role]
|
|
|
|
assert_error "REDIRECT $replica_host:$replica_port" {$rd read}
|
|
$rd close
|
|
}
|
|
}
|
|
}
|