01

Why Messaging Systems Avoid Databases

Even though embedded databases are viable for storing messages, many messaging systems do not use them.

Databases are optimized for long-term storage and operations like create, update, delete, and range queries.

A messaging system only needs to store and delete messages quickly—no need for complex queries or aggregations.

Thus, a full-featured database is overkill and unnecessary.

02

A Simpler Log-Based Storage Solution

A log file with indexes is sufficient for messaging systems.

This design has been discussed previously.

However, additional concepts merit attention.

This video explores those concepts.

03

Maximizing Disk Throughput

To increase disk throughput, we must maximize both write and read performance.

Two primary inefficiencies occur during intensive disk operations:

  • Too many small I/O operations
  • Excessive byte copying
04

Solving Inefficiency Through Batching

To reduce small I/O operations, avoid writing messages one by one.

Instead, buffer multiple messages in memory, then write them as a single batch.

Batching reduces overhead: instead of incurring processing costs per message, the cost is paid once per batch.

This principle applies beyond messaging.

For example, in network communication, sending one message per request incurs connection, thread, and setup costs.

Combining messages into a single request reduces these costs—paying them only once.

05

Key Questions About Batching

Where and how should messages be buffered before writing to disk?

The answer lies in leveraging the operating system’s page cache.

06

What Is a Page Cache?

A page cache is a disk cache in Linux.

A page or virtual page is a fixed-length block of virtual memory—smallest unit in memory management.

When data is written to a file system, it first goes to the page cache.

Data remains in the page cache until it must be written to disk.

07

When Is Data Written to Disk?

Flashing to disk occurs in two cases:

  • When an application explicitly requests it
  • When the OS decides to do so based on its own flushing policy
08

How Is Flushing Performed?

Flushing is handled by background threads.

09

How Does Reading Work?

When reading a file from disk, file blocks are written to the page cache.

If the file is read again later, the blocks are served directly from the page cache—no disk access required.

10

Memory Usage of the Page Cache

Most physical memory not directly assigned to applications is used by the OS for the page cache.

When Linux needs more memory for applications, unused page cache areas are automatically freed.

11

How Page Cache Boosts Throughput

When storing messages:

  • Open a segment file
  • Write messages to it
  • Data enters the page cache

The OS enhances throughput using two mechanisms:

  1. Batching consecutive small writes into larger physical writes
  2. Attempting to reduce redundant disk reads by serving data from memory
12

Re-sequencing Writes to Minimize Disk Head Movement

Re-sequence writes to reduce physical movement of the disk head. This improves I/O efficiency by organizing write operations in a more predictable order.

13

Page Cache Flushing Strategies

The page cache can be flushed to disk in two ways:

  • Every few seconds
  • When a certain number of messages accumulate in the cache

We can implement message batching using a local in-memory cache. The page cache offers an OS-native alternative to custom in-memory caching.

14

Virtual Memory Structure in Linux

Linux divides virtual memory into two distinct areas:

  • Kernel space: where the operating system kernel runs
  • User space: where applications execute
15

Message Retrieval Process

When an application retrieves a message from a segment file:

  1. The OS reads data from disk into the page cache (kernel space)
  2. The application reads data from kernel space into a user space buffer
  3. The application writes data back into kernel space via a socket buffer
  4. The OS copies data from the socket buffer to the network interface card buffer for transmission
16

Data Copying Overhead

This process results in four data copies, contributing to the excessive byte-copying problem.

17

Zero-Copy Read Technique

Zero-copy read avoids user space entirely. The OS transfers data directly from the page cache to the network interface.

This enables message consumption rates approaching the network connection’s bandwidth limit.

18

Limitations of Zero-Copy Read

Zero-copy read only works when no message manipulation occurs in user space.

If filtering or routing logic is required, data must be copied into user space.

19

Message Loss Risk During Write

A message may be lost if:

  • The broker accepts it and adds it to the page cache
  • Sends an acknowledgment immediately
  • The broker crashes before writing the message to disk

The message remains in memory and is never persisted.

20

Broader In-Memory Buffer Vulnerability

This loss risk is not unique to page caches. Any in-memory buffer is susceptible to data loss upon system failure.

21

Flushing Every Message Is Inefficient

Flushing the page cache after every message write reduces throughput due to high I/O overhead.

22

Message Replication as a Solution

Replication of messages can mitigate data loss risks.

23

Apache Kafka’s Use of Page Cache

Apache Kafka relies heavily on page cache for both read and write operations.

This design underpins Kafka’s ability to guarantee message durability.

Questions

  • How can we reduce the number of disk I/O operations when writing messages?
  • What is the role of the page cache in a messaging system?
  • How does the operating system handle data when reading from a segment file?
  • Why does the current read process involve four data copies?
  • Can we avoid copying data into user space during message consumption?
  • What happens if a broker crashes before flushing a message from the page cache to disk?

Key takeaways

  • The page cache is a Linux-level disk cache that stores data in memory before writing to disk.
  • Data is first written to the page cache, not directly to disk, which reduces the number of small I/O operations.
  • The OS batches small writes into larger physical writes to improve disk throughput.
  • The OS may re-sequence writes to minimize disk head movement, improving performance.
  • When reading messages, data goes from disk → page cache → user space → socket buffer → network, resulting in four copies.
  • Zero-copy read allows data to go directly from page cache to network, avoiding user space and reducing copying.
  • A broker crash before flushing to disk can result in message loss, since the message only existed in memory.