futriix/tests/unit/cluster/transactions-on-replica.tcl
zhaozhao.zz 1864c2f4b4 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-09-03 09:00:45 -07:00

108 lines
2.7 KiB
Tcl

# Check basic transactions on a replica.
start_cluster 1 1 {tags {external:skip cluster}} {
test "Cluster should start ok" {
wait_for_cluster_state ok
}
set primary [srv 0 "client"]
set replica [srv -1 "client"]
test "Can't read from replica without READONLY" {
$primary SET a 1
wait_for_ofs_sync $primary $replica
catch {$replica GET a} err
assert {[string range $err 0 4] eq {MOVED}}
}
test "Can't read from replica after READWRITE" {
$replica READWRITE
catch {$replica GET a} err
assert {[string range $err 0 4] eq {MOVED}}
}
test "Can read from replica after READONLY" {
$replica READONLY
assert {[$replica GET a] eq {1}}
}
test "Can perform HSET primary and HGET from replica" {
$primary HSET h a 1
$primary HSET h b 2
$primary HSET h c 3
wait_for_ofs_sync $primary $replica
assert {[$replica HGET h a] eq {1}}
assert {[$replica HGET h b] eq {2}}
assert {[$replica HGET h c] eq {3}}
}
test "Can MULTI-EXEC transaction of HGET operations from replica" {
$replica MULTI
assert {[$replica HGET h a] eq {QUEUED}}
assert {[$replica HGET h b] eq {QUEUED}}
assert {[$replica HGET h c] eq {QUEUED}}
assert {[$replica EXEC] eq {1 2 3}}
}
test "MULTI-EXEC with write operations is MOVED" {
$replica MULTI
catch {$replica HSET h b 4} err
assert {[string range $err 0 4] eq {MOVED}}
catch {$replica exec} err
assert {[string range $err 0 8] eq {EXECABORT}}
}
test "write command is QUEUED, then EXEC should be MOVED after failover" {
set rr [valkey_client]
$rr MULTI
assert {[$rr SET foo bar] eq {QUEUED}}
$replica CLUSTER FAILOVER FORCE
wait_for_condition 50 1000 {
[status $primary master_link_status] == "up"
} else {
fail "FAILOVER failed."
}
catch {$rr EXEC} err
assert {[string range $err 0 4] eq {MOVED}}
$rr close
$primary CLUSTER FAILOVER FORCE
wait_for_condition 50 1000 {
[status $replica master_link_status] == "up"
} else {
fail "FAILOVER failed."
}
}
test "read-only blocking operations from replica" {
set rd [valkey_deferring_client -1]
$rd readonly
$rd read
$rd XREAD BLOCK 0 STREAMS k 0
wait_for_condition 1000 50 {
[s -1 blocked_clients] eq {1}
} else {
fail "client wasn't blocked"
}
$primary XADD k * foo bar
set res [$rd read]
set res [lindex [lindex [lindex [lindex $res 0] 1] 0] 1]
assert {$res eq {foo bar}}
$rd close
}
test "reply MOVED when eval from replica for update" {
catch {[$replica eval {#!lua
return server.call('del','a')
} 1 a
]} err
assert {[string range $err 0 4] eq {MOVED}}
}
} ;# start_cluster