From 274531396caedd710595f46cc14010ed68a0c931 Mon Sep 17 00:00:00 2001 From: antirez Date: Tue, 11 Dec 2018 11:39:18 +0100 Subject: 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. --- src/multi.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'src/multi.c') 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; -- cgit v1.2.1