summaryrefslogtreecommitdiff
path: root/src/multi.c
diff options
context:
space:
mode:
authorantirez <antirez@gmail.com>2018-12-11 11:39:18 +0100
committerantirez <antirez@gmail.com>2018-12-11 11:39:21 +0100
commit274531396caedd710595f46cc14010ed68a0c931 (patch)
treedb7787ede988e38fd2dccbfc356136ef0ce2eaf0 /src/multi.c
parent086363babf0e47162d975a265bd8d77e8d49dfb1 (diff)
downloadredis-274531396caedd710595f46cc14010ed68a0c931.tar.gz
Reject EXEC containing write commands against RO replica.
Thanks to @soloestoy for discovering this issue in #5667. This is an alternative fix in order to avoid both cycling the clients and also disconnecting clients just having valid read-only transactions pending.
Diffstat (limited to 'src/multi.c')
-rw-r--r--src/multi.c17
1 files changed, 17 insertions, 0 deletions
diff --git a/src/multi.c b/src/multi.c
index 8159adcb3..c2e641879 100644
--- a/src/multi.c
+++ b/src/multi.c
@@ -35,6 +35,7 @@
void initClientMultiState(client *c) {
c->mstate.commands = NULL;
c->mstate.count = 0;
+ c->mstate.cmd_flags = 0;
}
/* Release all the resources associated with MULTI/EXEC state */
@@ -67,6 +68,7 @@ void queueMultiCommand(client *c) {
for (j = 0; j < c->argc; j++)
incrRefCount(mc->argv[j]);
c->mstate.count++;
+ c->mstate.cmd_flags |= c->cmd->flags;
}
void discardTransaction(client *c) {
@@ -137,6 +139,21 @@ void execCommand(client *c) {
goto handle_monitor;
}
+ /* If there are write commands inside the transaction, and this is a read
+ * only slave, we want to send an error. This happens when the transaction
+ * was initiated when the instance was a master or a writable replica and
+ * then the configuration changed (for example instance was turned into
+ * a replica). */
+ if (server.masterhost && server.repl_slave_ro &&
+ !(c->flags & CLIENT_MASTER) && c->mstate.cmd_flags & CMD_WRITE)
+ {
+ addReplyError(c,
+ "Transaction contains write commands but instance "
+ "is now a read-only slave. EXEC aborted.");
+ discardTransaction(c);
+ goto handle_monitor;
+ }
+
/* Exec all the queued commands */
unwatchAllKeys(c); /* Unwatch ASAP otherwise we'll waste CPU cycles */
orig_argv = c->argv;