Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
f3d67b38
Unverified
Commit
f3d67b38
authored
Dec 17, 2019
by
Salvatore Sanfilippo
Committed by
GitHub
Dec 17, 2019
Browse files
Merge pull request #6144 from madolson/dev-unstable-cluster-down
Add configuration option for allowing reads on cluster down
parents
e9b99c78
7b3e3d6a
Changes
7
Hide whitespace changes
Inline
Side-by-side
redis.conf
View file @
f3d67b38
...
@@ -1194,6 +1194,22 @@ lua-time-limit 5000
...
@@ -1194,6 +1194,22 @@ lua-time-limit 5000
#
#
# cluster-replica-no-failover no
# cluster-replica-no-failover no
# This option, when set to yes, allows nodes to serve read traffic while the
# the cluster is in a down state, as long as it believes it owns the slots.
#
# This is useful for two cases. The first case is for when an application
# doesn't require consistency of data during node failures or network partitions.
# One example of this is a cache, where as long as the node has the data it
# should be able to serve it.
#
# The second use case is for configurations that don't meet the recommended
# three shards but want to enable cluster mode and scale later. A
# master outage in a 1 or 2 shard configuration causes a read/write outage to the
# entire cluster without this option set, with it set there is only a write outage.
# Without a quorum of masters, slot ownership will not change automatically.
#
# cluster-allow-reads-when-down no
# In order to setup your cluster make sure to read the documentation
# In order to setup your cluster make sure to read the documentation
# available at http://redis.io web site.
# available at http://redis.io web site.
...
...
src/cluster.c
View file @
f3d67b38
...
@@ -5476,8 +5476,8 @@ void readwriteCommand(client *c) {
...
@@ -5476,8 +5476,8 @@ void readwriteCommand(client *c) {
* already "down" but it is fragile to rely on the update of the global state,
* already "down" but it is fragile to rely on the update of the global state,
* so we also handle it here.
* so we also handle it here.
*
*
* CLUSTER_REDIR_DOWN_STATE
if the cluster is down but the user attempts to
* CLUSTER_REDIR_DOWN_STATE
and CLUSTER_REDIR_DOWN_RO_STATE if the cluster is
* execute a command that addresses one or more keys. */
*
down but the user attempts to
execute a command that addresses one or more keys. */
clusterNode
*
getNodeByQuery
(
client
*
c
,
struct
redisCommand
*
cmd
,
robj
**
argv
,
int
argc
,
int
*
hashslot
,
int
*
error_code
)
{
clusterNode
*
getNodeByQuery
(
client
*
c
,
struct
redisCommand
*
cmd
,
robj
**
argv
,
int
argc
,
int
*
hashslot
,
int
*
error_code
)
{
clusterNode
*
n
=
NULL
;
clusterNode
*
n
=
NULL
;
robj
*
firstkey
=
NULL
;
robj
*
firstkey
=
NULL
;
...
@@ -5595,10 +5595,19 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
...
@@ -5595,10 +5595,19 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
* without redirections or errors in all the cases. */
* without redirections or errors in all the cases. */
if
(
n
==
NULL
)
return
myself
;
if
(
n
==
NULL
)
return
myself
;
/* Cluster is globally down but we got keys? We can't serve the request. */
/* Cluster is globally down but we got keys? We only serve the request
* if it is a read command and when allow_reads_when_down is enabled. */
if
(
server
.
cluster
->
state
!=
CLUSTER_OK
)
{
if
(
server
.
cluster
->
state
!=
CLUSTER_OK
)
{
if
(
error_code
)
*
error_code
=
CLUSTER_REDIR_DOWN_STATE
;
if
(
!
server
.
cluster_allow_reads_when_down
)
{
return
NULL
;
if
(
error_code
)
*
error_code
=
CLUSTER_REDIR_DOWN_STATE
;
return
NULL
;
}
if
(
!
(
cmd
->
flags
&
CMD_READONLY
)
&&
!
(
cmd
->
proc
==
evalCommand
)
&&
!
(
cmd
->
proc
==
evalShaCommand
))
{
if
(
error_code
)
*
error_code
=
CLUSTER_REDIR_DOWN_RO_STATE
;
return
NULL
;
}
}
}
/* Return the hashslot by reference. */
/* Return the hashslot by reference. */
...
@@ -5667,6 +5676,8 @@ void clusterRedirectClient(client *c, clusterNode *n, int hashslot, int error_co
...
@@ -5667,6 +5676,8 @@ void clusterRedirectClient(client *c, clusterNode *n, int hashslot, int error_co
addReplySds
(
c
,
sdsnew
(
"-TRYAGAIN Multiple keys request during rehashing of slot
\r\n
"
));
addReplySds
(
c
,
sdsnew
(
"-TRYAGAIN Multiple keys request during rehashing of slot
\r\n
"
));
}
else
if
(
error_code
==
CLUSTER_REDIR_DOWN_STATE
)
{
}
else
if
(
error_code
==
CLUSTER_REDIR_DOWN_STATE
)
{
addReplySds
(
c
,
sdsnew
(
"-CLUSTERDOWN The cluster is down
\r\n
"
));
addReplySds
(
c
,
sdsnew
(
"-CLUSTERDOWN The cluster is down
\r\n
"
));
}
else
if
(
error_code
==
CLUSTER_REDIR_DOWN_RO_STATE
)
{
addReplySds
(
c
,
sdsnew
(
"-CLUSTERDOWN The cluster is down and only accepts read commands
\r\n
"
));
}
else
if
(
error_code
==
CLUSTER_REDIR_DOWN_UNBOUND
)
{
}
else
if
(
error_code
==
CLUSTER_REDIR_DOWN_UNBOUND
)
{
addReplySds
(
c
,
sdsnew
(
"-CLUSTERDOWN Hash slot not served
\r\n
"
));
addReplySds
(
c
,
sdsnew
(
"-CLUSTERDOWN Hash slot not served
\r\n
"
));
}
else
if
(
error_code
==
CLUSTER_REDIR_MOVED
||
}
else
if
(
error_code
==
CLUSTER_REDIR_MOVED
||
...
@@ -5701,7 +5712,10 @@ int clusterRedirectBlockedClientIfNeeded(client *c) {
...
@@ -5701,7 +5712,10 @@ int clusterRedirectBlockedClientIfNeeded(client *c) {
dictEntry
*
de
;
dictEntry
*
de
;
dictIterator
*
di
;
dictIterator
*
di
;
/* If the cluster is down, unblock the client with the right error. */
/* If the cluster is down, unblock the client with the right error.
* If the cluster is configured to allow reads on cluster down, we
* still want to emit this error since a write will be required
* to unblock them which may never come. */
if
(
server
.
cluster
->
state
==
CLUSTER_FAIL
)
{
if
(
server
.
cluster
->
state
==
CLUSTER_FAIL
)
{
clusterRedirectClient
(
c
,
NULL
,
0
,
CLUSTER_REDIR_DOWN_STATE
);
clusterRedirectClient
(
c
,
NULL
,
0
,
CLUSTER_REDIR_DOWN_STATE
);
return
1
;
return
1
;
...
...
src/cluster.h
View file @
f3d67b38
...
@@ -29,6 +29,7 @@
...
@@ -29,6 +29,7 @@
#define CLUSTER_REDIR_MOVED 4
/* -MOVED redirection required. */
#define CLUSTER_REDIR_MOVED 4
/* -MOVED redirection required. */
#define CLUSTER_REDIR_DOWN_STATE 5
/* -CLUSTERDOWN, global state. */
#define CLUSTER_REDIR_DOWN_STATE 5
/* -CLUSTERDOWN, global state. */
#define CLUSTER_REDIR_DOWN_UNBOUND 6
/* -CLUSTERDOWN, unbound slot. */
#define CLUSTER_REDIR_DOWN_UNBOUND 6
/* -CLUSTERDOWN, unbound slot. */
#define CLUSTER_REDIR_DOWN_RO_STATE 7
/* -CLUSTERDOWN, allow reads. */
struct
clusterNode
;
struct
clusterNode
;
...
...
src/config.c
View file @
f3d67b38
...
@@ -2166,6 +2166,8 @@ standardConfig configs[] = {
...
@@ -2166,6 +2166,8 @@ standardConfig configs[] = {
createBoolConfig
(
"syslog-enabled"
,
NULL
,
IMMUTABLE_CONFIG
,
server
.
syslog_enabled
,
0
,
NULL
,
NULL
),
createBoolConfig
(
"syslog-enabled"
,
NULL
,
IMMUTABLE_CONFIG
,
server
.
syslog_enabled
,
0
,
NULL
,
NULL
),
createBoolConfig
(
"cluster-enabled"
,
NULL
,
IMMUTABLE_CONFIG
,
server
.
cluster_enabled
,
0
,
NULL
,
NULL
),
createBoolConfig
(
"cluster-enabled"
,
NULL
,
IMMUTABLE_CONFIG
,
server
.
cluster_enabled
,
0
,
NULL
,
NULL
),
createBoolConfig
(
"appendonly"
,
NULL
,
MODIFIABLE_CONFIG
,
server
.
aof_enabled
,
0
,
NULL
,
updateAppendonly
),
createBoolConfig
(
"appendonly"
,
NULL
,
MODIFIABLE_CONFIG
,
server
.
aof_enabled
,
0
,
NULL
,
updateAppendonly
),
createBoolConfig
(
"cluster-allow-reads-when-down"
,
NULL
,
MODIFIABLE_CONFIG
,
server
.
cluster_allow_reads_when_down
,
0
,
NULL
,
NULL
),
/* String Configs */
/* String Configs */
createStringConfig
(
"aclfile"
,
NULL
,
IMMUTABLE_CONFIG
,
ALLOW_EMPTY_STRING
,
server
.
acl_filename
,
""
,
NULL
,
NULL
),
createStringConfig
(
"aclfile"
,
NULL
,
IMMUTABLE_CONFIG
,
ALLOW_EMPTY_STRING
,
server
.
acl_filename
,
""
,
NULL
,
NULL
),
...
...
src/module.c
View file @
f3d67b38
...
@@ -3174,6 +3174,9 @@ fmterr:
...
@@ -3174,6 +3174,9 @@ fmterr:
* EINVAL: wrong command arity.
* EINVAL: wrong command arity.
* ENOENT: command does not exist.
* ENOENT: command does not exist.
* EPERM: operation in Cluster instance with key in non local slot.
* EPERM: operation in Cluster instance with key in non local slot.
* EROFS: operation in Cluster instance when a write command is sent
* in a readonly state.
* ENETDOWN: operation in Cluster instance when cluster is down.
*
*
* This API is documented here: https://redis.io/topics/modules-intro
* This API is documented here: https://redis.io/topics/modules-intro
*/
*/
...
@@ -3231,13 +3234,20 @@ RedisModuleCallReply *RM_Call(RedisModuleCtx *ctx, const char *cmdname, const ch
...
@@ -3231,13 +3234,20 @@ RedisModuleCallReply *RM_Call(RedisModuleCtx *ctx, const char *cmdname, const ch
* trying to access non-local keys, with the exception of commands
* trying to access non-local keys, with the exception of commands
* received from our master. */
* received from our master. */
if (server.cluster_enabled && !(ctx->client->flags & CLIENT_MASTER)) {
if (server.cluster_enabled && !(ctx->client->flags & CLIENT_MASTER)) {
int error_code;
/* Duplicate relevant flags in the module client. */
/* Duplicate relevant flags in the module client. */
c->flags &= ~(CLIENT_READONLY|CLIENT_ASKING);
c->flags &= ~(CLIENT_READONLY|CLIENT_ASKING);
c->flags |= ctx->client->flags & (CLIENT_READONLY|CLIENT_ASKING);
c->flags |= ctx->client->flags & (CLIENT_READONLY|CLIENT_ASKING);
if
(
getNodeByQuery
(
c
,
c
->
cmd
,
c
->
argv
,
c
->
argc
,
NULL
,
NULL
)
!=
if (getNodeByQuery(c,c->cmd,c->argv,c->argc,NULL,
&error_code
) !=
server.cluster->myself)
server.cluster->myself)
{
{
errno
=
EPERM
;
if (error_code == CLUSTER_REDIR_DOWN_RO_STATE) {
errno = EROFS;
} else if (error_code == CLUSTER_REDIR_DOWN_STATE) {
errno = ENETDOWN;
} else {
errno = EPERM;
}
goto cleanup;
goto cleanup;
}
}
}
}
...
...
src/scripting.c
View file @
f3d67b38
...
@@ -679,15 +679,27 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
...
@@ -679,15 +679,27 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
if
(
server
.
cluster_enabled
&&
!
server
.
loading
&&
if
(
server
.
cluster_enabled
&&
!
server
.
loading
&&
!
(
server
.
lua_caller
->
flags
&
CLIENT_MASTER
))
!
(
server
.
lua_caller
->
flags
&
CLIENT_MASTER
))
{
{
int
error_code
;
/* Duplicate relevant flags in the lua client. */
/* Duplicate relevant flags in the lua client. */
c
->
flags
&=
~
(
CLIENT_READONLY
|
CLIENT_ASKING
);
c
->
flags
&=
~
(
CLIENT_READONLY
|
CLIENT_ASKING
);
c
->
flags
|=
server
.
lua_caller
->
flags
&
(
CLIENT_READONLY
|
CLIENT_ASKING
);
c
->
flags
|=
server
.
lua_caller
->
flags
&
(
CLIENT_READONLY
|
CLIENT_ASKING
);
if
(
getNodeByQuery
(
c
,
c
->
cmd
,
c
->
argv
,
c
->
argc
,
NULL
,
NULL
)
!=
if
(
getNodeByQuery
(
c
,
c
->
cmd
,
c
->
argv
,
c
->
argc
,
NULL
,
&
error_code
)
!=
server
.
cluster
->
myself
)
server
.
cluster
->
myself
)
{
{
luaPushError
(
lua
,
if
(
error_code
==
CLUSTER_REDIR_DOWN_RO_STATE
)
{
"Lua script attempted to access a non local key in a "
luaPushError
(
lua
,
"cluster node"
);
"Lua script attempted to execute a write command while the "
"cluster is down and readonly"
);
}
else
if
(
error_code
==
CLUSTER_REDIR_DOWN_STATE
)
{
luaPushError
(
lua
,
"Lua script attempted to execute a command while the "
"cluster is down"
);
}
else
{
luaPushError
(
lua
,
"Lua script attempted to access a non local key in a "
"cluster node"
);
}
goto
cleanup
;
goto
cleanup
;
}
}
}
}
...
...
src/server.h
View file @
f3d67b38
...
@@ -1334,6 +1334,8 @@ struct redisServer {
...
@@ -1334,6 +1334,8 @@ struct redisServer {
to set in order to suppress certain
to set in order to suppress certain
native Redis Cluster features. Check the
native Redis Cluster features. Check the
REDISMODULE_CLUSTER_FLAG_*. */
REDISMODULE_CLUSTER_FLAG_*. */
int
cluster_allow_reads_when_down
;
/* Are reads allowed when the cluster
is down? */
/* Scripting */
/* Scripting */
lua_State
*
lua
;
/* The Lua interpreter. We use just one for all clients */
lua_State
*
lua
;
/* The Lua interpreter. We use just one for all clients */
client
*
lua_client
;
/* The "fake client" to query Redis from Lua */
client
*
lua_client
;
/* The "fake client" to query Redis from Lua */
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment