From c80f6f438e5b99908ffa44a46edae798c3d9d171 Mon Sep 17 00:00:00 2001 From: charsyam Date: Mon, 13 Apr 2026 21:26:52 +0900 Subject: [PATCH] aof: write directly to server.aof_buf in feedAppendOnlyFile Eliminate the per-call temporary sds buffer in feedAppendOnlyFile by appending the timestamp annotation, SELECT command, and command body directly into server.aof_buf. The previous code allocated an empty sds, built the payload, then memcpy'd it into aof_buf and freed the temp, costing one alloc + one copy + one free per propagated command. Also move the AOF-active gate to the top of the function as an early return. As a side effect, server.aof_selected_db is no longer updated when the function would have discarded its output anyway, which is actually more correct: stopAppendOnly() and the post-rewrite path both reset aof_selected_db to -1, so the next live write naturally re-emits SELECT when needed. Microbenchmark (10M iterations of a 3-arg SET, median of 5 runs, measured via a local DEBUG subcommand looping feedAppendOnlyFile): before: 163.51 ns/op (~6.12M ops/sec) after : 73.03 ns/op (~13.69M ops/sec) About 2.2x faster on this hot path. Larger commands amplify the win since the eliminated copy scaled with payload size. Signed-off-by: charsyam --- src/aof.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/src/aof.c b/src/aof.c index a094d11ca..74c7be6b9 100644 --- a/src/aof.c +++ b/src/aof.c @@ -1407,15 +1407,19 @@ sds genAofTimestampAnnotationIfNeeded(int force) { * argc - Number of values in argv */ void feedAppendOnlyFile(int dictid, robj **argv, int argc) { - sds buf = sdsempty(); - serverAssert(dictid == -1 || (dictid >= 0 && dictid < server.dbnum)); + if (!(server.aof_state == AOF_ON || + (server.aof_state == AOF_WAIT_REWRITE && server.child_type == CHILD_TYPE_AOF))) + { + return; + } + /* Feed timestamp if needed */ if (server.aof_timestamp_enabled) { sds ts = genAofTimestampAnnotationIfNeeded(0); if (ts != NULL) { - buf = sdscatsds(buf, ts); + server.aof_buf = sdscatsds(server.aof_buf, ts); sdsfree(ts); } } @@ -1426,25 +1430,15 @@ void feedAppendOnlyFile(int dictid, robj **argv, int argc) { char seldb[64]; snprintf(seldb,sizeof(seldb),"%d",dictid); - buf = sdscatprintf(buf,"*2\r\n$6\r\nSELECT\r\n$%lu\r\n%s\r\n", + server.aof_buf = sdscatprintf(server.aof_buf, + "*2\r\n$6\r\nSELECT\r\n$%lu\r\n%s\r\n", (unsigned long)strlen(seldb),seldb); server.aof_selected_db = dictid; } /* All commands should be propagated the same way in AOF as in replication. * No need for AOF-specific translation. */ - buf = catAppendOnlyGenericCommand(buf,argc,argv); - - /* Append to the AOF buffer. This will be flushed on disk just before - * of re-entering the event loop, so before the client will get a - * positive reply about the operation performed. */ - if (server.aof_state == AOF_ON || - (server.aof_state == AOF_WAIT_REWRITE && server.child_type == CHILD_TYPE_AOF)) - { - server.aof_buf = sdscatlen(server.aof_buf, buf, sdslen(buf)); - } - - sdsfree(buf); + server.aof_buf = catAppendOnlyGenericCommand(server.aof_buf,argc,argv); } /* ----------------------------------------------------------------------------