summaryrefslogtreecommitdiff
path: root/src/multi.c
diff options
context:
space:
mode:
authorantirez <antirez@gmail.com>2013-03-26 10:58:10 +0100
committerantirez <antirez@gmail.com>2013-03-26 10:58:10 +0100
commit71f3e7431700f55b88582301d5b36b52b37a0740 (patch)
tree1e2c4ef2f1e6d32ac021ff7fd0d7d5c58e3f5f85 /src/multi.c
parent02c269e27e846b904db80636d321686fa11695d4 (diff)
downloadredis-71f3e7431700f55b88582301d5b36b52b37a0740.tar.gz
Transactions: propagate MULTI/EXEC only when needed.
MULTI/EXEC is now propagated to the AOF / Slaves only once we encounter the first command that is not a read-only one inside the transaction. The old behavior was to always propagate an empty MULTI/EXEC block when the transaction was composed just of read only commands, or even completely empty. This created two problems: 1) It's a bandwidth waste in the replication link and a space waste inside the AOF file. 2) We used to always increment server.dirty to force the propagation of the EXEC command, resulting into triggering RDB saves more often than needed. Note: even read-only commands may also trigger writes that will be propagated, when we access a key that is found expired and Redis will synthesize a DEL operation. However there is no need for this to stay inside the transaction itself, but only to be ordered. So for instance something like: MULTI GET foo SET key zap EXEC May be propagated into: DEL foo MULTI SET key zap EXEC While the DEL is outside the transaction, the commands are delivered in the right order and it is not possible for other commands to be inserted between DEL and MULTI.
Diffstat (limited to 'src/multi.c')
-rw-r--r--src/multi.c24
1 files changed, 14 insertions, 10 deletions
diff --git a/src/multi.c b/src/multi.c
index 191307a67..7000c4841 100644
--- a/src/multi.c
+++ b/src/multi.c
@@ -116,6 +116,7 @@ void execCommand(redisClient *c) {
robj **orig_argv;
int orig_argc;
struct redisCommand *orig_cmd;
+ int must_propagate = 0; /* Need to propagate MULTI/EXEC to AOF / slaves? */
if (!(c->flags & REDIS_MULTI)) {
addReplyError(c,"EXEC without MULTI");
@@ -135,12 +136,6 @@ void execCommand(redisClient *c) {
goto handle_monitor;
}
- /* Propagate a MULTI request now that we are sure the block is executed.
- * This way we'll deliver the MULTI/..../EXEC block as a whole and
- * both the AOF and the replication link will have the same consistency
- * and atomicity guarantees. */
- execCommandPropagateMulti(c);
-
/* Exec all the queued commands */
unwatchAllKeys(c); /* Unwatch ASAP otherwise we'll waste CPU cycles */
orig_argv = c->argv;
@@ -151,6 +146,16 @@ void execCommand(redisClient *c) {
c->argc = c->mstate.commands[j].argc;
c->argv = c->mstate.commands[j].argv;
c->cmd = c->mstate.commands[j].cmd;
+
+ /* Propagate a MULTI request once we encounter the first write op.
+ * This way we'll deliver the MULTI/..../EXEC block as a whole and
+ * both the AOF and the replication link will have the same consistency
+ * and atomicity guarantees. */
+ if (!must_propagate && !(c->cmd->flags & REDIS_CMD_READONLY)) {
+ execCommandPropagateMulti(c);
+ must_propagate = 1;
+ }
+
call(c,REDIS_CALL_FULL);
/* Commands may alter argc/argv, restore mstate. */
@@ -162,10 +167,9 @@ void execCommand(redisClient *c) {
c->argc = orig_argc;
c->cmd = orig_cmd;
discardTransaction(c);
- /* Make sure the EXEC command is always replicated / AOF, since we
- * always send the MULTI command (we can't know beforehand if the
- * next operations will contain at least a modification to the DB). */
- server.dirty++;
+ /* Make sure the EXEC command will be propagated as well if MULTI
+ * was already propagated. */
+ if (must_propagate) server.dirty++;
handle_monitor:
/* Send EXEC to clients waiting data from MONITOR. We do it here