From 8fce41e84404167f2cfc1c5ed1ca06c7cb34d385 Mon Sep 17 00:00:00 2001 From: "zhaozhao.zz" Date: Tue, 16 Apr 2024 15:55:28 +0800 Subject: [PATCH] replica redirect read&write to master in standalone mode Signed-off-by: zhaozhao.zz --- src/cluster.c | 8 ------ src/config.c | 1 + src/server.c | 11 ++++++++ src/server.h | 1 + tests/integration/replica-redirect.tcl | 36 ++++++++++++++++++++++++++ valkey.conf | 12 +++++++++ 6 files changed, 61 insertions(+), 8 deletions(-) create mode 100644 tests/integration/replica-redirect.tcl diff --git a/src/cluster.c b/src/cluster.c index 99c02cd86..3bba0233a 100644 --- a/src/cluster.c +++ b/src/cluster.c @@ -1461,20 +1461,12 @@ void askingCommand(client *c) { * In this mode slaves will not redirect clients as long as clients access * with read-only commands to keys that are served by the slave's master. */ void readonlyCommand(client *c) { - if (server.cluster_enabled == 0) { - addReplyError(c,"This instance has cluster support disabled"); - return; - } c->flags |= CLIENT_READONLY; addReply(c,shared.ok); } /* The READWRITE command just clears the READONLY command state. */ void readwriteCommand(client *c) { - if (server.cluster_enabled == 0) { - addReplyError(c,"This instance has cluster support disabled"); - return; - } c->flags &= ~CLIENT_READONLY; addReply(c,shared.ok); } diff --git a/src/config.c b/src/config.c index d3da3f94e..b89d4abb5 100644 --- a/src/config.c +++ b/src/config.c @@ -3094,6 +3094,7 @@ standardConfig static_configs[] = { createBoolConfig("replica-serve-stale-data", "slave-serve-stale-data", MODIFIABLE_CONFIG, server.repl_serve_stale_data, 1, NULL, NULL), createBoolConfig("replica-read-only", "slave-read-only", DEBUG_CONFIG | MODIFIABLE_CONFIG, server.repl_slave_ro, 1, NULL, NULL), createBoolConfig("replica-ignore-maxmemory", "slave-ignore-maxmemory", MODIFIABLE_CONFIG, server.repl_slave_ignore_maxmemory, 1, NULL, NULL), + createBoolConfig("replica-enable-redirect", NULL, MODIFIABLE_CONFIG, server.repl_replica_enable_redirect, 0, NULL, NULL), createBoolConfig("jemalloc-bg-thread", NULL, MODIFIABLE_CONFIG, server.jemalloc_bg_thread, 1, NULL, updateJemallocBgThread), createBoolConfig("activedefrag", NULL, DEBUG_CONFIG | MODIFIABLE_CONFIG, server.active_defrag_enabled, 0, isValidActiveDefrag, NULL), createBoolConfig("syslog-enabled", NULL, IMMUTABLE_CONFIG, server.syslog_enabled, 0, NULL, NULL), diff --git a/src/server.c b/src/server.c index e30338e45..94c295959 100644 --- a/src/server.c +++ b/src/server.c @@ -4009,6 +4009,17 @@ int processCommand(client *c) { } } + if (!server.cluster_enabled && + server.masterhost && + !mustObeyClient(c) && + server.repl_replica_enable_redirect && + (is_write_command || + (is_read_command && !(c->flags & CLIENT_READONLY)))) { + addReplyErrorSds(c,sdscatprintf(sdsempty(), "-MOVED -1 %s:%d", + server.masterhost, server.masterport)); + return C_OK; + } + /* Disconnect some clients if total clients memory is too high. We do this * before key eviction, after the last command was executed and consumed * some client output buffer memory. */ diff --git a/src/server.h b/src/server.h index d891fccfb..343808cdb 100644 --- a/src/server.h +++ b/src/server.h @@ -1911,6 +1911,7 @@ struct valkeyServer { int repl_serve_stale_data; /* Serve stale data when link is down? */ int repl_slave_ro; /* Slave is read only? */ int repl_slave_ignore_maxmemory; /* If true slaves do not evict. */ + int repl_replica_enable_redirect; /* If true replica would redirect read&write commands to master. */ time_t repl_down_since; /* Unix time at which link with master went down */ int repl_disable_tcp_nodelay; /* Disable TCP_NODELAY after SYNC? */ int slave_priority; /* Reported in INFO and used by Sentinel. */ diff --git a/tests/integration/replica-redirect.tcl b/tests/integration/replica-redirect.tcl new file mode 100644 index 000000000..510148ac3 --- /dev/null +++ b/tests/integration/replica-redirect.tcl @@ -0,0 +1,36 @@ +start_server {tags {needs:repl external:skip}} { + start_server {} { + set master_host [srv -1 host] + set master_port [srv -1 port] + + r replicaof $master_host $master_port + wait_for_condition 50 100 { + [s 0 master_link_status] eq {up} + } else { + fail "Replicas not replicating from master" + } + + 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 when enable replica-enable-redirect} { + r config set replica-enable-redirect yes + assert_error "MOVED -1 $master_host:$master_port" {r set foo bar} + assert_error "MOVED -1 $master_host:$master_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 + } {} + } +} diff --git a/valkey.conf b/valkey.conf index b1d180893..39862e6a3 100644 --- a/valkey.conf +++ b/valkey.conf @@ -817,6 +817,18 @@ replica-priority 100 # replica-announce-ip 5.5.5.5 # replica-announce-port 1234 +# You can configure a replica instance to redirect data access commands +# to its master or not, excluding commands such as: +# INFO, AUTH, ROLE, SUBSCRIBE, UNSUBSCRIBE, PUBLISH. +# +# When enabled, a replica instance will reply "-MOVED -1 master-ip:port" +# for data access commands. Normally these are write or read commands, if +# you want to run read commands while replica-enable-redirect is enabled, +# use the READONLY command first. +# +# This config only takes effect in standalone mode. +replica-enable-redirect no + ############################### KEYS TRACKING ################################# # The client side caching of values is assisted via server-side support.