futriix/tests/integration/replica-redirect.tcl
zhaozhao.zz 743f5ac2ae
standalone -REDIRECT handles special case of MULTI context (#895)
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>
2024-08-30 10:17:53 +08:00

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
}
}
}