redis/redis
Event loop
Active contributors: antirez, Oran Agra, debing.sun.
Purpose
Redis is event-driven. A single thread (the main thread) services all clients by multiplexing reads, writes, and timers through one aeEventLoop. The implementation is src/ae.c plus one of four backend files chosen at compile time.
Source layout
| File | Role |
|---|---|
src/ae.c |
The portable event-loop API. State, scheduling, file/time event registration. |
src/ae.h |
Public API. aeCreateEventLoop, aeMain, aeCreateFileEvent, aeCreateTimeEvent, aeStop, etc. |
src/ae_epoll.c |
Linux backend (epoll). Used when __linux__. |
src/ae_kqueue.c |
BSD/macOS backend (kqueue). |
src/ae_evport.c |
Solaris/Illumos backend (event ports). |
src/ae_select.c |
Last-resort POSIX backend. |
src/eventnotifier.c, src/eventnotifier.h |
Wrap eventfd/pipe so other threads can wake the main thread. |
The chosen backend is decided in src/ae.c via:
#ifdef HAVE_EVPORT
#include "ae_evport.c"
#else
#ifdef HAVE_EPOLL
#include "ae_epoll.c"
#else
#ifdef HAVE_KQUEUE
#include "ae_kqueue.c"
#else
#include "ae_select.c"
#endif
#endif
#endifThe probes for HAVE_EPOLL etc. are in src/config.h.
Key abstractions
| Type | Purpose |
|---|---|
aeEventLoop |
The whole loop. Holds the file-event table, the time-event linked list, and the polling state. |
aeFileEvent |
An (fd, mask, readProc, writeProc, clientData) tuple. mask is AE_READABLE/AE_WRITABLE/AE_BARRIER. |
aeTimeEvent |
A (id, when_ms, timeProc, clientData) tuple in a singly linked list. |
aeFiredEvent |
Output of one aeApiPoll call: which fds fired and which masks. |
AE_BARRIER is a special mask that tells the loop "if you also have a write event for this fd, dispatch the write first". This matters for AOF + reply ordering: if a write would happen before the AOF flush, a sudden crash could replicate something the master hadn't durably persisted yet.
How a tick happens
sequenceDiagram
participant App
participant ae as aeMain
participant Hooks
participant Poll as aeApiPoll
Note over App: aeMain is invoked from main()
loop forever
ae->>Hooks: beforeSleep
ae->>Poll: aeApiPoll(timeout)
Poll-->>ae: list of fired fds
ae->>Hooks: aftersleep (rare)
ae->>ae: dispatch read/write callbacks
ae->>ae: process due time events
endThe beforeSleep hook is set in src/server.c to serverBeforeSleep. It does:
- Run the lazy-free queue.
- Apply pending tracking invalidations.
- Drain pending replies into output buffers.
- Flush the AOF buffer.
- Apply pending IO-thread handoffs.
- Send pending cluster-bus packets.
- Run module periodic callbacks.
- If keyspace events are pending, propagate them.
The serverCron callback is registered as a time event with id 0 and runs server.hz times per second. It does:
- Active expiration (
activeExpireCycle). - Active eviction (
performEvictionsindirectly). - Update server statistics.
- Cluster cron.
- Replication cron (heartbeats, backlog trim, replica reaping).
- AOF rewrite scheduling.
- Connection limits enforcement.
- Track maxmemory.
The HZ rate is adaptive: when the number of connected clients is high, the loop bumps hz up to 500 to keep cron work small per tick. The dynamic-hz config flag (default on) controls this.
Time events
Time events are stored in a singly linked list. On every iteration processTimeEvents() walks the list. Each event returns either:
AE_NOMORE— remove from the list.- A positive integer N — reschedule for N ms in the future.
There is no priority queue — there are typically only a handful of time events (serverCron, module timer callbacks, blocked-client timeout sweeper) so a linked-list scan is cheap. For modules that need lots of timers, the module API provides its own internal timer wheel implemented in src/module.c.
Cross-thread wakeups
The IO threads and BIO threads need a way to interrupt the main thread when work is pending. This is the eventnotifier:
/* src/eventnotifier.c */
typedef struct eventNotifier eventNotifier;
eventNotifier *createEventNotifier(void); /* eventfd on Linux, pipe elsewhere */
int getReadEventFd(eventNotifier *en);
int triggerEventNotifier(eventNotifier *en); /* write 1 byte */
int handleEventNotifier(eventNotifier *en); /* read & drain */The main loop registers each eventnotifier as a readable fd. When an IO thread wants the main thread to come pick up clients, it triggerEventNotifiers — the main thread wakes, runs the registered handler, and processes the pending list. See IO threads.
Tuning
| Knob | What it changes |
|---|---|
hz |
Base ticks per second of serverCron. |
dynamic-hz |
Allow hz to scale up to 500 under load. |
tcp-backlog |
listen() backlog on bound sockets. |
timeout |
Idle client disconnect (the cron checks every fdis). |
Related pages
- Networking — uses
aeCreateFileEventfor read/write callbacks per client. - IO threads — uses
eventnotifierto coordinate with the main loop. - redis-server —
main()callsaeMainand never returns.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.