20 How Does Redis Handle File Events and Time Events

20 How Does Redis Handle File Events and Time Events #

In the previous lesson, we learned about the system architecture of Redis. In the following lessons, I will provide a detailed analysis of these modules and designs. First, I will analyze Redis’s event-driven model.

Redis Event-driven Model #

Redis is an event-driven program, but unlike Memcached, Redis does not use open-source libraries like libevent or libev. Instead, Redis directly developed its own event loop component. The reason given by the Redis author is to minimize external dependencies, and the self-developed event model is simple, lightweight, efficient, and easier to control. Redis’s event-driven model mechanism is encapsulated in related structures such as aeEventLoop. Almost all core operations in Redis, including network connection, command reading, execution, and reply, data persistence, key eviction and recycling, are handled through the ae event model.

img

Redis’s event-driven model handles two types of events:

  • File events, such as connection establishment, command reception, response sending, etc.
  • Time events, such as periodic tasks executed in Redis, key eviction, buffered data writing, rehashing, etc.
Handling File Events #

img

Redis’s file events are processed using the typical Reactor pattern. Redis’s file event handling mechanism is divided into four parts:

  • Connection socket
  • I/O Multiplexing program
  • File event dispatcher
  • Event handler

File events are an abstraction of operations on a connection socket. When the port listening socket is ready to accept a new connection, or when the connection socket is ready to read requests, write responses, or close, a file event is generated. The I/O multiplexing program is responsible for simultaneously monitoring multiple sockets. When these sockets generate file events, the event notification is triggered, and the file dispatcher will perceive and retrieve these events.

Although multiple file events may occur concurrently, the I/O multiplexing program always puts all events into a queue. Through this queue, these file events are notified to the file dispatcher in an ordered manner.

I/O Multiplexing #

Redis encapsulates four types of I/O multiplexing programs, each providing the same API implementation. During compilation, the best I/O multiplexing function is selected as the underlying implementation based on performance and system platform. The selection order is: first try to choose the evport function in Solaries, if not available, then try to choose the epoll function in Linux, otherwise choose kqueue, which is supported by most UNIX systems. These three multiplexing functions directly use internal structures of the kernel and can handle tens of thousands of file descriptors.

If none of the above functions are available in the current compilation environment, select is chosen as the underlying implementation solution. The performance of the select solution is poor. When an event occurs, it scans all the monitored descriptors. The event complexity is O(n), and can only serve a limited number of file descriptors concurrently. For a 32-bit machine, the default is 1024, and for a 64-bit machine, the default is 2048. Therefore, select is not usually chosen as the running solution in production environments. These four implementations in Redis are respectively contained in the files ae_evport, ae_epoll, ae_kqueue, and ae_select.

File Event Collection and Dispatcher #

The file event dispatcher in Redis is the function aeProcessEvents. It first calculates the maximum time to wait and then waits for file events to occur using functions like aeApiPoll. If events are notified by the I/O multiplexing program within the waiting time, all generated file events will be polled immediately, and the file events will be placed into the aeFiredEvents structure array in the aeEventLoop. Each fired event records the socket and the Redis read/write event type.

Here, the event types in multiplexing are converted into the event types in Redis’s ae event-driven model. Taking epoll in Linux as an example, EPOLLIN in epoll is converted to AE_READABLE type, and EPOLLOUT, EPOLLERR, and EPOLLHUP are converted to AE_WRITABLE events.

Once aeProcessEvents receives the triggered events, it dispatches the file events to their corresponding event handlers based on the event type. If a socket has both read and write events at the same time, the Redis dispatcher first dispatches the read event and then the write event.

Classification of File Event Handler Functions #

In Redis, the registration and handling of file event functions are mainly divided into three types.

  • acceptTcpHandler: When Redis is started, it registers a read event for the listening socket in the initServer function. The event handler for this event is acceptTcpHandler. When a new connection enters, it will be dispatched as a read task by the dispatcher. In handling this read task, a new connection will be accepted, and the IP and port of the caller will be obtained. A client structure will be created for the new connection. If there are a large number of connections entering simultaneously, Redis will handle a maximum of 1000 connection requests at a time.

  • readQueryFromClient: When creating a client in the connection function, a read event is registered for the new connection socket. The event handler for this read event is readQueryFromClient. When there is a request command arriving at the connection socket, the I/O multiplexing program will retrieve and trigger the file event. Then, this read event will be dispatched to the corresponding processing function by the dispatcher. readQueryFromClient reads data from the connection socket and stores it in the client’s query buffer. It then parses the command according to the two request formats currently supported by Redis: inline format and multibulk format. After the parsing is completed, the client will obtain the corresponding redisCommand from the command table based on the requested command. If the corresponding command is available, the client will start to verify the request parameters and the current server’s memory, disk, and other statuses. After the verification is completed, the client will actually execute the processing function of redisCommand to execute the specific command. Finally, the execution result will be written into the client’s write buffer as a response.

  • sendReplyToClient: When Redis needs to send a response to the client, the event loop in Redis registers a write event for the client’s connection socket. The event handler for this write event is sendReplyToClient. By registering the write event, the client’s socket is indirectly associated with AE_WRITABLE. When the client’s file descriptor is writable, the write event will be triggered, and this function will send the data in the write buffer to the caller.

  • Time Events in Redis: Time events in Redis refer to events that need to be executed at specific times. Multiple time events in Redis form a linked list in the aeEventLoop, which is used by Redis to poll and execute in the ae event loop.

There are currently two main time event handling functions in Redis:

  • serverCron
  • moduleTimerHandler

Time events in Redis can be divided into two types:

  • One-time events: after execution, the time event ends.
  • Periodic events: after the event is executed, the next execution time will be set, so that it can continue to execute when the time is reached, and repeat continuously.

A time event consists of five attributes:

  • Event ID: Redis creates a globally unique ID for each time event, which increases in ascending order.
  • Execution time when_sec and when_ms: accurate to milliseconds, record the time when the event can be executed.
  • Time event handler timeProc: when the time event arrives, Redis will call the corresponding timeProc to process the event.
  • Associated data clientData: when calling timeProc, this associated data is used as a parameter.
  • Linked list pointers prev and next: used to maintain time events as a doubly linked list, making it easy to insert and search for the time events to be executed.

The processing of time events is performed in the aeProcessEvents function in the event loop. The execution process is as follows:

  1. First, iterate through all time events.
  2. Compare the event time with the current time to find the time events that can be executed.
  3. Then, execute the timeProc function of the time event.
  4. After execution, for periodic events, set the next execution time; for one-time events, set the event ID to -1, and delete it from the linked list in subsequent aeProcessEvents executions of the event loop.