One thread, one command at a time — the Redis event loop and slow-command stalls

Redis runs every command on a single event-loop thread, so atomicity is free but one slow command 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

  1. Watch
  2. Try it
  3. Predict
  4. Capture
CLIENTS · 3Client Aidleno in-flightwait 0 µsClient Bidleno in-flightwait 0 µsClient Cidleno in-flightwait 0 µsredis · event loopsingle thread · runs one command at a timeio-threads (parses sockets)parses RESP · queues commands · never executescount = 1 (effectively disabled)↔io0COMMAND QUEUECURRENT COMMANDloop idleELAPSED0 µsDATASET · 6 keysuser:42{…}session:9okcart:1[…]counter127user:73{…}pingpongClient latencyacross all clientsP500 µsP990 µsMAX0 µs
One thread pulls one command at a time.
What to watch for

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.

Continue unlocks when the animation finishes.
Implementation

Highlighted lines are the ones running in the diagram right now.

EventLoop.runOnce
the ae loop: one command, start to finish, before the next
while server.running:
ready = epoll_wait(fds)
for fd in ready:
readQueryFromClient(fd) # parses RESP
cmd = nextReadyCommand()
if cmd is not None:
reply = execute(cmd) # touches the dataset
addReplyToClient(cmd.client, reply)
# nothing else runs until execute() returns
flushPendingWrites()
Command.KEYS
scans the whole keyspace in one command — no yielding
def keysCommand(pattern):
matches = []
for key in server.db.dict: # every key, every time
if stringmatch(pattern, key):
matches.append(key)
# loop is held the entire time;
# no other client is served until we return
return matches
# contrast: SCAN returns a cursor after ~COUNT keys
# so the loop is free between batches

Where this sits in Build Redis

Scene 02 of 10, in the The loop act — One thread, one command — and the encoding under it.. Why a single event loop is fast — and why one slow command (KEYS *, big LRANGE, slow Lua) stalls every client.

Up next. Encodings — if one loop runs every command, the SHAPE of the value it's touching matters. Some shapes are cheap; some quietly aren't.

All 10 scenes in Build Redis · Every curriculum

Built with Arqly
Every scene in Build Redis builds on the one before it.All 10 Build Redis scenes