Video summary
Implementing Command Pipelining | Redis Internals
Main summary
Key takeaways
Redis command pipelining (concept)
- Problem with normal request/response: When a client sends commands one at a time over TCP, each command requires a network round trip (client → server request, server → client response). This becomes a major cost over the internet.
- Pipelining idea: Instead of sending one command per request, the client sends multiple commands back-to-back in a single request.
- The server executes them sequentially (command1, then command2, then command3).
- The server returns multiple responses in the same overall reply.
- Important note: Pipelining is not a transaction. It’s just multiplexing many commands into one request/response exchange.
Tradeoffs and performance impact
- Throughput improves: Redis operations are mostly in-memory, so computing each command is very fast (microseconds/nanoseconds). The dominant overhead is network I/O and blocking system calls.
- Fewer context switches / less waiting on network I/O: With pipelining, the server spends less time alternating between processing and network reads/writes.
- Memory cost increases: Because responses for all pipelined commands must be sent together, Redis (or the custom implementation) needs to buffer outputs in memory before writing them back.
- More pipelining → potentially higher memory consumption.
How the video demonstrates pipelining
- Uses Redis CLI for baseline testing.
- Then uses netcat with manually crafted RESP payloads (showing
$,*,\r\nformatting) to send:pingset k vget k
- Shows that a single request yields three responses:
pong+OK$1 ... v
Implementation guide: adding pipelining to a Redis-like server
Main changes described in the codebase:
-
Input parsing changes (read side)
- Previously: decode exactly one command from a network read.
- Now: decode multiple concatenated RESP commands from the same input buffer.
- Introduced a concept like
Redis CMDS(an array/slice of commands). - Decoder modification: the
decodefunction now returns[]interface{}/ multiple decoded values, iterating until the buffer ends. - Uses the
delta(number of bytes consumed) to advance through concatenated RESP frames.
-
Output generation changes (eval/response side)
- Previously:
respondhandled one command and wrote its result directly. - Now:
respondhandles multiple commands (even if only one command is present). - Evaluation switches from “write immediately” to:
- evaluate each command
- buffer each encoded response
- send all responses in one shot at the end.
- Previously:
-
Eval functions refactored for pipelining
- Instead of returning values/errors by writing to the socket directly,
evalfunctions return RESP-encoded response bytes (including encoded error replies like-...). - The top-level
eval/respondloop appends all results to a buffer (buff.write(...)), then flushes once (c.write(buff.bytes)).
- Instead of returning values/errors by writing to the socket directly,
-
Example behavior inside the loop
pingresponse encodedset k vresponse encodedget kresponse encoded- All three are written as a single combined response stream.
Testing / verification
- Runs
main.goand connects to a different port (e.g.,7379). - Sends the same pipelined command sequence and confirms the combined output:
pong+OK(fromset)- bulk string result for
get k
Key takeaway from the tutorial
- To implement pipelining: keep the internal command execution logic mostly unchanged, and focus modifications on:
- parsing multiple commands from one read
- buffering multiple responses
- sending one combined response
- Also emphasizes code structuring: making I/O boundaries generic makes adding features like pipelining easier.
Main speakers / sources
- Speaker: “Atta” (explicitly thanked at the end: “Thanks, Atta.”)
- Source referenced: Redis RESP protocol (conceptually) and the tutorial’s codebase at github.com/dicedb/dice (specific commit mentioned).