# Data flow — redis/redis

> Canonical page: https://codevetter.com/unpack/redis/data-flow

# Data flow — redis/redis

Scanned commit c3215f019a4c11c35d438f241114e94730fe39e5 (2026-09-22). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

Input arrives on a connection abstraction (`connection.h` over socket/unix/TLS), is read into a per-client or thread-reusable query buffer by `readQueryFromClient()`, parsed into `pendingCommand` structs by `processInlineBuffer()`/`processMultibulkBuffer()`, dispatched by `processCommand()` and executed by `call()`. Command handlers mutate the authoritative state owner — `server.db[j]` kvstores — and any side effects are queued through `alsoPropagate()`. At the end of the execution unit `propagateNow()` fans out to exactly three sinks: the AOF buffer, the replica stream, and the atomic-slot-migration client. Replies accumulate in per-client output buffers and are flushed in `beforeSleep()` by `handleClientsWithPendingWrites()`, strictly after `flushAppendOnlyFile()` so `appendfsync=always` is honoured before acknowledgement.
- The input boundary is `readQueryFromClient(connection *conn)`, which recovers the client via `connGetPrivateData(conn)` and reads `PROTO_IOBUF_LEN` bytes, except when a multibulk argument exceeds `PROTO_MBULK_BIG_ARG`, in which case it sizes the read to exactly the remaining bulk length to enable zero-copy object creation. — [src/networking.c#L3981-4032](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/networking.c#L3981-L4032)
- Query buffers are pooled per IO thread: a client with no private `querybuf` borrows `thread_reusable_qb` and sets `CLIENT_IO_REUSABLE_QUERYBUFFER`; if `thread_reusable_qb_used` is already set (nested execution via `processEventsWhileBlocked()`) the client allocates a private buffer instead. — [src/networking.c#L4032-4052](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/networking.c#L4032-L4052)
- `processInputBuffer()` parses ahead up to `server.lookahead` commands into `c->pending_cmds`, but clamps the lookahead to 1 for unauthenticated clients because AUTH changes the handling of succeeding commands. — [src/networking.c#L3778-3800](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/networking.c#L3778-L3800) [src/networking.c#L3820-3860](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/networking.c#L3820-L3860)
- Request type is inferred from the first byte: `'*'` selects `PROTO_REQ_MULTIBULK` → `processMultibulkBuffer()`, anything else selects `PROTO_REQ_INLINE` → `processInlineBuffer()`; an unknown `reqtype` triggers `serverPanic("Unknown request type")`. — [src/networking.c#L3828-3862](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/networking.c#L3828-L3862)
- `processCommand()` resolves argv to a `struct redisCommand` through a three-tier lookup: an already-set `c->lookedcmd`, then `isCommandReusable(c->lastcmd, ...)` reusing the previous command, then a full `lookupCommand(c->argv, c->argc)`. — [src/server.c#L4694-4712](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L4694-L4712) [src/server.c#L3675-3745](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L3675-L3745)
- The command table itself is populated once at startup by `populateCommandTable()` walking the generated `extern struct redisCommand redisCommandTable[]` from commands.def, so adding a command is a data change in `src/commands/*.json` followed by codegen. — [src/server.c#L3556-3570](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L3556-L3570) [src/commands.def](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/commands.def) [src/Makefile#L503-504](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/Makefile#L503-L504)
- Module command filters get first look at every non-reprocessed command: `processCommand()` calls `moduleCallCommandFilters(c)` and `reqresAppendRequest(c)` before command lookup, meaning a module can rewrite argv before the core ever resolves the command name. — [src/server.c#L4671-4678](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L4671-L4678) [src/module.c#L11960-11970](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/module.c#L11960-L11970)
- `call()` records `dirty = server.dirty` and `old_master_repl_offset = server.master_repl_offset` before invoking the proc, and clears `CLIENT_FORCE_AOF|CLIENT_FORCE_REPL|CLIENT_PREVENT_PROP` so the handler must opt back into propagation. — [src/server.c#L4151-4185](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L4151-L4185)
- `server.allowed_propagate_targets` is a global mask narrowed for the duration of each `call()` so that nested `RM_Call`/Lua `redis.set_repl()` restrictions from an outer call cannot be escaped by an inner one — this is what makes effect-command rewriting (SPOP→SREM) honour selective replication. — [src/server.c#L4218-4230](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L4218-L4230) [src/server.c#L3070](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L3070)
- `propagateNow()` is the single fan-out point to the three output sinks: `feedAppendOnlyFile()` when `server.aof_state != AOF_OFF && target & PROPAGATE_AOF`, then `replicationFeedSlaves(server.slaves, ...)` and `asmFeedMigrationClient(argv, argc)` when `target & PROPAGATE_REPL`. — [src/server.c#L3796-3824](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L3796-L3824) [src/aof.c#L1666](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/aof.c#L1666) [src/replication.c#L651](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/replication.c#L651)
- `propagateNow()` asserts that propagation never happens while replica actions are paused — `serverAssert(!(isPausedActions(PAUSE_ACTION_REPLICA) && !server.client_pause_in_transaction))` — because a dataset change during a replica pause could be lost on failover. — [src/server.c#L3813-3817](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L3813-L3817)
- Output ordering in `beforeSleep()` is load-bearing and commented as such: `connTypeProcessPendingData()` and `clientCompressionProcessPendingData()` must run before `flushAppendOnlyFile(0)`, which must run before `handleClientsWithPendingWrites()` and `sendPendingClientsToIOThreads()` so `appendfsync=always` is satisfied before the reply is sent. — [src/server.c#L1988-2000](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L1988-L2000) [src/server.c#L2060-2072](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L2060-L2072) [src/networking.c#L3046](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/networking.c#L3046)
- Blocked-client resumption is sequenced before AOF flushing: `blockedBeforeSleep()` is called with the comment 'must be done before flushAppendOnlyFile, in case of appendfsync=always, since the unblocked clients may write data'. — [src/server.c#L2008-2013](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L2008-L2013) [src/blocked.c](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/blocked.c)
- Client-side caching invalidation is a distinct output boundary flushed per-iteration: `beforeSleep()` asserts `server.tracking_pending_keys` and `server.pending_push_messages` are empty, then calls `trackingBroadcastInvalidationMessages(NULL)` for BCAST-mode clients. — [src/server.c#L2048-2058](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L2048-L2058) [src/tracking.c](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/tracking.c)
- On startup the persistence-to-memory direction is owned by `loadDataFromDisk()`, which honours a `preload-file` config prefixed `aof:/` or `rdb:/` (dispatching to `loadPreLoadAOFFile`/`loadPreLoadManifestFile`/`rdbLoad`), else falls back to `loadAppendOnlyFiles(server.aof_manifest)` when AOF is on, else `rdbLoad(server.rdb_filename, ...)`. — [src/server.c#L7903-7975](https://github.com/redis/redis/blob/c3215f019a4c11c35d438f241114e94730fe39e5/src/server.c#L7903-L7975)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
