fivenines

Theory / 10 min

Event Loop

Redis is famous for doing a surprising amount of work with a small number of threads. The trick is not that one thread is magical. The trick is that Redis is built around work that is usually short, memory-local, and driven by I/O readiness.

An event loop asks the operating system a practical question:

which sockets can make progress right now?

Then it runs the appropriate callbacks.

register interests -> wait for readiness -> handle events -> repeat

This lets one execution thread manage many clients without dedicating a thread to each connection.

Readiness Is Not Completion

A readable socket does not mean a full Redis command is available. It means reading from the socket will not block. The server may still receive half a RESP frame.

A writable socket does not mean the entire reply will be written. It means at least some bytes can probably be written now.

This distinction is the quiet foundation of evented servers. The event loop deals in opportunities to make progress, not guarantees that application-level work will finish in one call.

File Events And Time Events

Redis-style loops usually manage two kinds of work:

file events: sockets that are readable or writable
time events: scheduled maintenance and recurring server tasks

File events move client conversations forward. Time events handle work such as expiration sampling, server cron logic, replication checks, statistics, and background-task coordination.

A simplified loop looks like this:

while server is running:
  timeout = time until next scheduled task
  events = poll(registered file descriptors, timeout)
  handle ready file events
  run due time events

The exact polling primitive depends on the platform: epoll, kqueue, select, poll, or another mechanism. Redis hides those differences behind an abstraction so the rest of the server can think in terms of events.

Single-Threaded Command Execution

The event loop explains how Redis can handle many connected clients. The command execution model explains why its internal state can stay coherent.

For the core command path, Redis executes one command at a time. A handler runs, mutates or reads the keyspace, produces a reply, and returns. Another command does not barge into the middle of that handler and mutate the same data structure at the same time.

This is not the same as serving one client at a time. Many clients are connected, readable, writable, idle, blocked, or queued. The event loop multiplexes them. The command path remains serialized where that simplicity matters.

The Cost Of Long Work

The price of this model is obvious once you see it: a long-running command blocks the loop.

If a command scans too much data, a script runs too long, persistence does heavy synchronous work, or eviction tries to free memory in an unbounded loop, other clients wait. Redis performance is therefore not just about big-O complexity in the abstract. It is about preserving short, predictable turns through the event loop.

That is why Redis often favors incremental work: incremental rehashing, sampled expiration, approximate eviction, background snapshotting, and buffered I/O. The system is designed to avoid monopolizing the loop.

The Event Loop As A Design Constraint

Once the event loop is visible, Redis design decisions become less mysterious. Commands should finish quickly. Maintenance tasks need budgets. Slow clients need output limits. Blocking operations require special handling. Even elegant features are suspect if they hold the loop too long.

The event loop is not just an implementation mechanism. It is the rhythm that the rest of the server has to respect.

Next step

See what actually stuck.

Take the practice scenarios now.