From 28273394cb37a3fabc5869bb5e2725a401e04c6f Mon Sep 17 00:00:00 2001 From: antirez Date: Tue, 14 Jan 2014 16:33:14 +0100 Subject: [PATCH] Cluster: support to read from slave nodes. A client can enter a special cluster read-only mode using the READONLY command: if the client read from a slave instance after this command, for slots that are actually served by the instance's master, the queries will be processed without redirection, allowing clients to read from slaves (but without any kind fo read-after-write guarantee). The READWRITE command can be used in order to exit the readonly state. --- src/cluster.c | 34 +++++++++++++++++++++++++++++++--- src/networking.c | 1 + src/redis.c | 2 ++ src/redis.h | 3 +++ 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/cluster.c b/src/cluster.c index 66cfe4ff..6586a6ce 100644 --- a/src/cluster.c +++ b/src/cluster.c @@ -3274,6 +3274,10 @@ socket_rd_err: return; } +/* ----------------------------------------------------------------------------- + * Cluster functions related to serving / redirecting clients + * -------------------------------------------------------------------------- */ + /* The ASKING command is required after a -ASK redirection. * The client should issue ASKING before to actually send the command to * the target instance. See the Redis Cluster specification for more @@ -3287,9 +3291,23 @@ void askingCommand(redisClient *c) { addReply(c,shared.ok); } -/* ----------------------------------------------------------------------------- - * Cluster functions related to serving / redirecting clients - * -------------------------------------------------------------------------- */ +/* The READONLY command is uesd by clients to enter the read-only mode. + * 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(redisClient *c) { + if (server.cluster_enabled == 0) { + addReplyError(c,"This instance has cluster support disabled"); + return; + } + c->flags |= REDIS_READONLY; + addReply(c,shared.ok); +} + +/* The READWRITE command just clears the READONLY command state. */ +void readwriteCommand(redisClient *c) { + c->flags &= ~REDIS_READONLY; + addReply(c,shared.ok); +} /* Return the pointer to the cluster node that is able to serve the command. * For the function to succeed the command should only target a single @@ -3385,6 +3403,16 @@ clusterNode *getNodeByQuery(redisClient *c, struct redisCommand *cmd, robj **arg (c->flags & REDIS_ASKING || cmd->flags & REDIS_CMD_ASKING)) { return server.cluster->myself; } + /* Handle the read-only client case reading from a slave: if this + * node is a slave and the request is about an hash slot our master + * is serving, we can reply without redirection. */ + if (c->flags & REDIS_READONLY && + cmd->flags & REDIS_CMD_READONLY && + server.cluster->myself->flags & REDIS_NODE_SLAVE && + server.cluster->myself->slaveof == n) + { + return server.cluster->myself; + } /* It's not a -ASK case. Base case: just return the right node. */ return n; } diff --git a/src/networking.c b/src/networking.c index ff3681f5..bf16559a 100644 --- a/src/networking.c +++ b/src/networking.c @@ -1263,6 +1263,7 @@ sds getClientInfoString(redisClient *client) { if (client->flags & REDIS_UNBLOCKED) *p++ = 'u'; if (client->flags & REDIS_CLOSE_ASAP) *p++ = 'A'; if (client->flags & REDIS_UNIX_SOCKET) *p++ = 'U'; + if (client->flags & REDIS_READONLY) *p++ = 'r'; if (p == flags) *p++ = 'N'; *p++ = '\0'; diff --git a/src/redis.c b/src/redis.c index e926e9c8..ef07d9b2 100644 --- a/src/redis.c +++ b/src/redis.c @@ -254,6 +254,8 @@ struct redisCommand redisCommandTable[] = { {"restore-asking",restoreCommand,-4,"awmk",0,NULL,1,1,1,0,0}, {"migrate",migrateCommand,-6,"aw",0,NULL,0,0,0,0,0}, {"asking",askingCommand,1,"r",0,NULL,0,0,0,0,0}, + {"readonly",readonlyCommand,1,"r",0,NULL,0,0,0,0,0}, + {"readwrite",readwriteCommand,1,"r",0,NULL,0,0,0,0,0}, {"dump",dumpCommand,2,"ar",0,NULL,1,1,1,0,0}, {"object",objectCommand,-2,"r",0,NULL,2,2,2,0,0}, {"client",clientCommand,-2,"ar",0,NULL,0,0,0,0,0}, diff --git a/src/redis.h b/src/redis.h index ace974ee..0a7bc096 100644 --- a/src/redis.h +++ b/src/redis.h @@ -232,6 +232,7 @@ #define REDIS_FORCE_AOF (1<<14) /* Force AOF propagation of current cmd. */ #define REDIS_FORCE_REPL (1<<15) /* Force replication of current cmd. */ #define REDIS_PRE_PSYNC (1<<16) /* Instance don't understand PSYNC. */ +#define REDIS_READONLY (1<<17) /* Cluster client is in read-only state. */ /* Client block type (btype field in client structure) * if REDIS_BLOCKED flag is set. */ @@ -1397,6 +1398,8 @@ void clusterCommand(redisClient *c); void restoreCommand(redisClient *c); void migrateCommand(redisClient *c); void askingCommand(redisClient *c); +void readonlyCommand(redisClient *c); +void readwriteCommand(redisClient *c); void dumpCommand(redisClient *c); void objectCommand(redisClient *c); void clientCommand(redisClient *c); -- GitLab