summaryrefslogtreecommitdiff
path: root/src/multi.c
diff options
context:
space:
mode:
authorOran Agra <oran@redislabs.com>2020-06-11 21:09:35 +0300
committerOran Agra <oran@redislabs.com>2020-06-23 12:01:33 +0300
commit65a3307bc95aadbc91d85cdf9dfbe1b3493222ca (patch)
tree3c9702403e1cc82f943a6c3d9ef84558e08e3b87 /src/multi.c
parent2ebcd63d6a011dc46df26d27b860209a28efe727 (diff)
downloadredis-65a3307bc95aadbc91d85cdf9dfbe1b3493222ca.tar.gz
EXEC always fails with EXECABORT and multi-state is cleared
In order to support the use of multi-exec in pipeline, it is important that MULTI and EXEC are never rejected and it is easy for the client to know if the connection is still in multi state. It was easy to make sure MULTI and DISCARD never fail (done by previous commits) since these only change the client state and don't do any actual change in the server, but EXEC is a different story. Since in the past, it was possible for clients to handle some EXEC errors and retry the EXEC, we now can't affort to return any error on EXEC other than EXECABORT, which now carries with it the real reason for the abort too. Other fixes in this commit: - Some checks that where performed at the time of queuing need to be re- validated when EXEC runs, for instance if the transaction contains writes commands, it needs to be aborted. there was one check that was already done in execCommand (-READONLY), but other checks where missing: -OOM, -MISCONF, -NOREPLICAS, -MASTERDOWN - When a command is rejected by processCommand it was rejected with addReply, which was not recognized as an error in case the bad command came from the master. this will enable to count or MONITOR these errors in the future. - make it easier for tests to create additional (non deferred) clients. - add tests for the fixes of this commit.
Diffstat (limited to 'src/multi.c')
-rw-r--r--src/multi.c43
1 files changed, 19 insertions, 24 deletions
diff --git a/src/multi.c b/src/multi.c
index 60a07dfc7..35ddf92af 100644
--- a/src/multi.c
+++ b/src/multi.c
@@ -36,6 +36,7 @@ void initClientMultiState(client *c) {
c->mstate.commands = NULL;
c->mstate.count = 0;
c->mstate.cmd_flags = 0;
+ c->mstate.cmd_inv_flags = 0;
}
/* Release all the resources associated with MULTI/EXEC state */
@@ -76,6 +77,7 @@ void queueMultiCommand(client *c) {
incrRefCount(mc->argv[j]);
c->mstate.count++;
c->mstate.cmd_flags |= c->cmd->flags;
+ c->mstate.cmd_inv_flags |= ~c->cmd->flags;
}
void discardTransaction(client *c) {
@@ -122,6 +124,23 @@ void execCommandPropagateExec(client *c) {
PROPAGATE_AOF|PROPAGATE_REPL);
}
+/* Aborts a transaction, with a specific error message.
+ * The transaction is always aboarted with -EXECABORT so that the client knows
+ * the server exited the multi state, but the actual reason for the abort is
+ * included too. */
+void execCommandAbort(client *c, sds error) {
+ discardTransaction(c);
+
+ if (error[0] == '-') error++;
+ addReplyErrorFormat(c, "-EXECABORT Transaction discarded because of: %s", error);
+
+ /* Send EXEC to clients waiting data from MONITOR. We did send a MULTI
+ * already, and didn't send any of the queued commands, now we'll just send
+ * EXEC so it is clear that the transaction is over. */
+ if (listLength(server.monitors) && !server.loading)
+ replicationFeedMonitors(c,server.monitors,c->db->id,c->argv,c->argc);
+}
+
void execCommand(client *c) {
int j;
robj **orig_argv;
@@ -135,15 +154,6 @@ void execCommand(client *c) {
return;
}
- /* If we are in -BUSY state, flag the transaction and return the
- * -BUSY error, like Redis <= 5. This is a temporary fix, may be changed
- * ASAP, see issue #7353 on Github. */
- if (server.lua_timedout) {
- flagTransaction(c);
- addReply(c, shared.slowscripterr);
- return;
- }
-
/* Check if we need to abort the EXEC because:
* 1) Some WATCHed key was touched.
* 2) There was a previous error while queueing commands.
@@ -157,21 +167,6 @@ 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.loading && 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 replica. 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;