Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 

README.md

09: Event loop

One thread. Many clients. No locks. This is how real Redis is shaped.

Run it

make 09-event-loop

Now open four redis-cli -p 6380 sessions and hammer them in parallel. They all get served by one Python thread.

Why this matters

Step 7 used one thread per client. That works for tens of clients. For thousands, you spend more time context-switching than serving requests, and each thread costs an 8 MB stack.

The event loop trades concurrency-via-threads for concurrency-via-non-blocking-IO. The kernel tracks which sockets have data ready and tells us in batches. We process each in turn. While one client's command is running, the others are paused, but only for microseconds at a time.

The shape

sel = selectors.DefaultSelector()
sel.register(server_sock, EVENT_READ, "ACCEPTOR")

while True:
    events = sel.select(timeout=1.0)
    for key, mask in events:
        if key.data == "ACCEPTOR":
            on_accept(key.fileobj)
        else:
            on_readable / on_writable

That's the whole loop. sel.select() blocks until at least one socket is ready. We dispatch by what kind of event happened.

Per-client state

We keep an in_buf and out_buf per client. Bytes arrive into in_buf, get parsed as RESP frames, dispatched, and the replies go into out_buf. When the socket becomes writable, we drain out_buf. If it can't take it all at once, we keep the rest and try again on the next writable event.

This is what makes the event loop work with slow clients: their stuck send() doesn't block the whole server.

What we dropped

  • Replication (step 8) — would compose, just more state to thread through
  • Pub/Sub (step 7) — would also compose, with channel -> set of states

Adding those back is the natural next exercise. Real Redis runs ALL of this in one event loop.

What's next

Step 10 is the capstone: a benchmark that compares our server against real Redis on a few standard workloads, and the lessons we can extract from the gap.