summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOran Agra <oran@redislabs.com>2020-10-06 21:43:30 +0300
committerOran Agra <oran@redislabs.com>2020-10-27 08:49:22 +0200
commit89c68ba3f7fba1e1050c0ecfc7c2e57f64ec15f5 (patch)
tree0b4441c8136ba2c9f67b7a40e769df6bce39f2bd
parentfcda82930e78dbfe92a3b0024212eb75fc08a919 (diff)
downloadredis-89c68ba3f7fba1e1050c0ecfc7c2e57f64ec15f5.tar.gz
Allow blocked XREAD on a cluster replica (#7881)
I suppose that it was overlooked, since till recently none of the blocked commands were readonly. other changes: - add test for the above. - add better support for additional (and deferring) clients for cluster tests - improve a test which left the client in MULTI state. (cherry picked from commit 216c1106099f7c66c991a5a811c8fbacbb0ab17c)
-rw-r--r--src/cluster.c9
-rw-r--r--tests/cluster/tests/16-transactions-on-replica.tcl70
-rw-r--r--tests/instances.tcl23
3 files changed, 100 insertions, 2 deletions
diff --git a/src/cluster.c b/src/cluster.c
index 5966d0561..3e19f88eb 100644
--- a/src/cluster.c
+++ b/src/cluster.c
@@ -5741,6 +5741,15 @@ int clusterRedirectBlockedClientIfNeeded(client *c) {
int slot = keyHashSlot((char*)key->ptr, sdslen(key->ptr));
clusterNode *node = server.cluster->slots[slot];
+ /* if the client is read-only and attempting to access key that our
+ * replica can handle, allow it. */
+ if ((c->flags & CLIENT_READONLY) &&
+ (c->lastcmd->flags & CMD_READONLY) &&
+ nodeIsSlave(myself) && myself->slaveof == node)
+ {
+ node = myself;
+ }
+
/* We send an error and unblock the client if:
* 1) The slot is unassigned, emitting a cluster down error.
* 2) The slot is not handled by this node, nor being imported. */
diff --git a/tests/cluster/tests/16-transactions-on-replica.tcl b/tests/cluster/tests/16-transactions-on-replica.tcl
new file mode 100644
index 000000000..da3017bc2
--- /dev/null
+++ b/tests/cluster/tests/16-transactions-on-replica.tcl
@@ -0,0 +1,70 @@
+# Check basic transactions on a replica.
+
+source "../tests/includes/init-tests.tcl"
+
+test "Create a primary with a replica" {
+ create_cluster 1 1
+}
+
+test "Cluster should start ok" {
+ assert_cluster_state ok
+}
+
+set primary [Rn 0]
+set replica [Rn 1]
+
+test "Cant read from replica without READONLY" {
+ $primary SET a 1
+ 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 preform HSET primary and HGET from replica" {
+ $primary HSET h a 1
+ $primary HSET h b 2
+ $primary HSET h c 3
+ assert {[$replica HGET h a] eq {1}}
+ assert {[$replica HGET h b] eq {2}}
+ assert {[$replica HGET h c] eq {3}}
+}
+
+# didn't cherry pick b120366d4 to 5.0 yet
+#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 "read-only blocking operations from replica" {
+ set rd [redis_deferring_client redis 1]
+ $rd readonly
+ $rd read
+ $rd XREAD BLOCK 0 STREAMS k 0
+
+ wait_for_condition 1000 50 {
+ [RI 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
+}
diff --git a/tests/instances.tcl b/tests/instances.tcl
index 357b34818..352b2a2e5 100644
--- a/tests/instances.tcl
+++ b/tests/instances.tcl
@@ -334,10 +334,16 @@ proc S {n args} {
[dict get $s link] {*}$args
}
+# Returns a Redis instance by index.
+# Example:
+# [Rn 0] info
+proc Rn {n} {
+ return [dict get [lindex $::redis_instances $n] link]
+}
+
# Like R but to chat with Redis instances.
proc R {n args} {
- set r [lindex $::redis_instances $n]
- [dict get $r link] {*}$args
+ [Rn $n] {*}$args
}
proc get_info_field {info field} {
@@ -509,3 +515,16 @@ proc restart_instance {type id} {
}
}
+proc redis_deferring_client {type id} {
+ set port [get_instance_attrib $type $id port]
+ set host [get_instance_attrib $type $id host]
+ set client [redis $host $port 1]
+ return $client
+}
+
+proc redis_client {type id} {
+ set port [get_instance_attrib $type $id port]
+ set host [get_instance_attrib $type $id host]
+ set client [redis $host $port 0]
+ return $client
+}