Open-Source Wikis

/

Redis

/

Systems

/

Event loop

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
#endif

The 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
    end

The 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 (performEvictions indirectly).
  • 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).
  • Networking — uses aeCreateFileEvent for read/write callbacks per client.
  • IO threads — uses eventnotifier to coordinate with the main loop.
  • redis-servermain() calls aeMain and never returns.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Event loop – Redis wiki | Factory