Build Redis (10 scenes)
Scene 02 · One thread, one command at a time
Why a single event loop is fast — and why one slow command (KEYS *, big LRANGE, slow Lua) stalls every client.
Previously
You've named the parts. Here's the first thing that defines how Redis behaves — there is exactly ONE event loop between every client and every byte of data.
Scene 02
One thread, one command at a time
Diagram
Three clients on the left, each with its own queued commands and a wait-time strip that grows while it's blocked. They all funnel into one server box on the right that holds the dataset and runs a single event loop. The 'now executing' badge in the loop shows the command in flight — every other client's strip inflates until the loop frees up.
Three clients dispatch GETs and SETs into the loop. One thread pulls one command at a time, runs it to completion, then takes the next. With fast commands, every client gets sub-millisecond latency.
Implementation
EventLoop.runOnce
the ae loop: one command, start to finish, before the next
1while server.running:2 ready = epoll_wait(fds)3 for fd in ready:4 readQueryFromClient(fd) # parses RESP5 cmd = nextReadyCommand()6 if cmd is not None:7 reply = execute(cmd) # touches the dataset8 addReplyToClient(cmd.client, reply)9 # nothing else runs until execute() returns10 flushPendingWrites()
Command.KEYS
scans the whole keyspace in one command — no yielding
1def keysCommand(pattern):2 matches = []3 for key in server.db.dict: # every key, every time4 if stringmatch(pattern, key):5 matches.append(key)6 # loop is held the entire time;7 # no other client is served until we return8 return matches910# contrast: SCAN returns a cursor after ~COUNT keys11# so the loop is free between batches