我构建了 Foyer:一个能大幅降低 S3 延迟的 Rust 混合缓存。
I built Foyer: a Rust hybrid cache that slashes S3 latency

原始链接: https://medium.com/@yingjunwu/the-case-for-hybrid-cache-for-object-stores-4b1f02ec6c9a

## Foyer:利用混合缓存提升S3性能 现代数据系统越来越多地依赖于像Amazon S3这样的对象存储,因为它具有可扩展性和成本效益。然而,S3的高延迟构成了一个重大挑战,尤其是在实时应用程序中。RisingWave,一个流数据库,通过构建**Foyer**,一个基于Rust的混合缓存库,解决了这个问题。 Foyer将快速的内存缓存与更大的基于磁盘的缓存相结合,协同工作以最大限度地减少S3请求。内存缓存利用分片和灵活的驱逐策略(LRU、FIFO等),而磁盘缓存则提供针对不同数据大小和I/O方法的优化模块化引擎。主要特性包括请求去重和零拷贝抽象以提高效率。 RisingWave将Foyer集成到三层存储系统中:内存用于热数据,磁盘用于温数据,S3用于持久存储。这大大减少了对S3的访问,提高了延迟并控制了成本。诸如基于块的读取、稀疏索引、预取和缓存补水等策略进一步优化了性能。 Foyer不仅仅是一个组件,它还深度集成到RisingWave的存储堆栈中,管理I/O并动态调整缓存策略。这使得RisingWave能够在不牺牲实时流处理至关重要的低延迟的情况下,利用S3的优势。

## Foyer:一个用于S3的Rust混合缓存 Sheldon_fun推出了Foyer,一个Rust库,旨在通过实现混合缓存来降低S3延迟。其核心思想是将经常访问的S3对象缓存到内存和磁盘中,与直接访问S3相比,显著减少响应时间(可能将首次字节时间从50-150毫秒降低到接近零)。 讨论集中在诸如依赖操作系统交换或磁盘缓存等替代方案上,一些人认为显式内存管理并非总是必要的。然而,另一些人强调了控制延迟和避免由交换引起的不可预测的性能下降的好处。 许多评论员指出Foyer在诸如Distributed Chroma和ZeroFS等项目中的应用。 与AWS Storage Gateway和rclone等工具进行了比较,强调了Foyer作为应用程序级缓存库的角色,而不是一个完整的设备或文件系统抽象。缓存与直接S3请求的成本效益也进行了辩论,并提出了需要多大的请求量才能证明缓存的开销。 一个常见的抱怨是关于Medium在Firefox上的可用性,用户报告了滚动问题。
相关文章

原文

How RisingWave built Foyer, a hybrid caching library in Rust to beat S3 latency

Press enter or click to view image in full size

Foyer GitHub repo: https://github.com/foyer-rs/foyer

RisingWave GitHub repo: https://github.com/risingwavelabs/risingwave

S3 Is Slow; You Need a Cache

In recent years, many modern data systems have been designed around Amazon S3 or other object storage services. Projects such as Neon, TiKV, and Warpstream all treat S3 as their primary storage layer. This trend is not accidental. Object storage offers practically unlimited capacity, strong durability, and very low cost compared to block storage or SSDs. For database developers, it also removes the burden of managing disks directly and allows them to focus on building features while relying on the cloud provider for durability and scalability. With these advantages, S3 has become the de facto backbone for new database and data infrastructure projects.

However, S3 is not built for low-latency access. Every read requires a network round trip, and every request adds cost. Latency is already orders of magnitude higher than memory or local SSD, and when applications repeatedly fetch the same data these delays accumulate, making systems sluggish while inflating storage bills. Many systems that rely solely on S3 face this constant tradeoff between low cost and acceptable performance.

Press enter or click to view image in full size
Object storage offers low cost, durability, and scalability, but its high latency and per-access cost become major bottlenecks, especially when the working set is large and requires frequent data exchange with the cache. Image Source: the author.

For RisingWave, the challenge is even greater. As a streaming system, it executes continuous queries where results depend on processing data in real time. A single S3 access may already add hundreds of milliseconds. If multiple S3 reads are needed within one second, the delays compound rapidly until they become unacceptable. This extreme sensitivity to latency is why we felt the pain of S3 more acutely than others.

A cache provides a way to close this gap. By keeping hot data in memory and warm data on local disk, a cache reduces the number of requests sent to S3. This lowers latency, stabilizes performance, and reduces recurring storage costs. Applications no longer need to fetch the same data repeatedly from remote storage but can instead serve most queries locally while still relying on S3 for durability and scale. To make this practical for real-time systems like RisingWave, we built Foyer, a hybrid caching library in Rust that unifies memory and disk caching into one coherent layer.

Inside Foyer’s Architecture

Now that we have seen why caching is critical for S3-based systems, the next question is how to design a cache that works at both the speed of memory and the scale of disk. Foyer answers this by combining multiple components into a single hybrid system. At a high level, it consists of three parts: a memory cache for low-latency access, a disk cache for expanded capacity, and a coordinator that unifies the two layers.

The core architecture of Foyer: a cooperator orchestrates memory cache and disk cache to provide a unified hybrid caching system. Image Source: the author.

Memory Cache: The First Layer

The memory cache, implemented in the foyer-memory crate, is the fastest path for lookups. It supports sharding to handle high concurrency and provides flexibility in eviction strategies, including LRU, FIFO, w-TinyLFU, S3-FIFO, and SIEVE. Each shard maintains its own index and eviction logic, which reduces contention under heavy load.

Foyer also optimizes for correctness and efficiency. When several requests for the same key arrive at the same time and the key is missing, only one fetch is issued, while the others wait for the result. This request deduplication avoids redundant trips to disk or remote storage. Rust’s zero-copy abstractions further reduce overhead by minimizing unnecessary serialization and copying.

The design of Foyer’s memory cache: a flexible and composable framework with pluggable index structures and eviction policies. Image Source: the author.

Disk Cache: Extending the Working Set

When memory does not contain the requested data, Foyer falls back to the disk cache. This layer is implemented in the foyer-storage crate and provides much larger capacity than RAM, without the latency of repeated S3 calls. The disk cache is modular, with multiple engines suited for different data profiles. The block engine is tuned for medium-sized records, the set-associative engine is designed for small entries, and an object engine is under development for large objects.

The I/O layer underneath is also flexible. Developers can select between synchronous reads and writes or modern asynchronous interfaces like io_uring. The storage backend itself can be a file, a raw block device, or a directory, giving operators the ability to adapt Foyer to their hardware and deployment model.

The design of Foyer’s disk cache: a flexible and composable framework with pluggable cache engines, I/O backends, and storage devices. Image Source: the author.

Coordinator: The Glue

The coordinator, inside the main foyer crate, ties memory and disk into a unified system. It promotes entries from disk to memory when they are accessed frequently, writes new data back after a miss, and ensures the API stays consistent regardless of which layer the data comes from. It also consolidates concurrent fetches to prevent storms of redundant requests when many clients look for the same key.

Modes of Operation

Foyer supports two distinct modes of operation: hybrid mode and memory-only mode.

In hybrid mode, both memory and disk caches are active. Hot data is served directly from memory, while warm data that does not fit in memory can still be retrieved from disk. Only when data is absent from both layers does Foyer issue a fetch to S3 or another remote source. Once the data is retrieved, it is written back to both disk and memory so that future requests are served locally. This design dramatically reduces the number of S3 calls and makes the system suitable for workloads where cost and latency must be controlled at scale.

In memory-only mode, the disk cache is disabled, and all operations are performed in memory. This mode is useful for smaller workloads, ephemeral jobs, or testing environments where persistence and larger capacity are unnecessary. Importantly, the API remains identical across both modes. Developers can start with a simple memory cache and later add disk caching without changing their application logic, which makes adoption smoother.

Benefits and Trade-offs

The hybrid cache design brings significant benefits. Latency improves because most queries are served locally. Cost is reduced because far fewer requests hit S3. Capacity increases since disk supplements RAM, allowing a much larger portion of the working set to stay near the application. Foyer also emphasizes configurability. Developers can choose eviction strategies, disk engines, and I/O mechanisms that fit their workloads. Built-in observability allows integration with Prometheus, Grafana, and OpenTelemetry so operators can monitor hit rates, latencies, and cache behavior in production.

At the same time, hybrid caching introduces trade-offs. The system is more complex than a simple in-memory cache, which means configuration and tuning are important. A poorly chosen eviction algorithm or disk engine can reduce effectiveness. Disk is slower than memory, so capacity planning must ensure that hot data stays in RAM to keep latency low. Monitoring is essential because silent cache degradation can easily ripple into application-level performance problems.

How RisingWave Uses Foyer in Production

RisingWave is a streaming database that was built from day one to use S3 as its primary storage. Unlike many systems that rely on S3 only for backups or cold data, RisingWave stores state, materialized views, operator outputs, and recovery logs directly in S3. This architecture makes the system highly elastic and cost-efficient, but it also brings real challenges for latency-sensitive workloads. Foyer plays a critical role in addressing these challenges.

Press enter or click to view image in full size
Without Foyer: frequent data swapping between memory and S3. Image Source: the author.

A Three-Tier Storage Architecture

RisingWave adopts a three-tier storage design that integrates memory, local disk, and S3:

  • Memory holds the hottest data and provides the fastest response path.
  • Local disk acts as an intermediate cache, buffering data that does not fit in memory but is accessed frequently. This layer greatly reduces the number of requests sent to S3.
  • S3 remains the durable and scalable storage layer, but it is consulted only when data is not found in either memory or disk.
With Foyer: hybrid memory–disk cache reduces S3 accesses. Image Source: the author.

Foyer is the component that manages the disk cache. It coordinates the interaction between memory and S3, ensuring that most queries can be served without directly hitting object storage.

Controlling Latency and Reducing Costs

In practice, RisingWave observed that even simple S3 reads often have a time-to-first-byte of 30 ms, and under load this can stretch to 100–300 ms. For stream processing workloads, where operators continuously read and update state, such delays quickly accumulate. Without a cache, each operator could incur multiple S3 calls per second, making real-time performance impossible.

With Foyer, RisingWave significantly reduces the number of direct S3 accesses. Most requests are served either from memory or from disk, with only a small fraction reaching S3. This keeps query latency within acceptable bounds while also avoiding the explosion of costs caused by frequent small S3 requests.

Press enter or click to view image in full size
A comparison of enabling and disabling cache refill under a certain load. Image Source: the author.

Cache Management and Strategies

The integration of Foyer into RisingWave is not limited to storing blocks on disk. Several strategies enhance its effectiveness:

  • Block-level reads: multiple logical rows are packed into larger blocks, reducing the number of S3 requests.
  • Sparse indexes: each table or materialized view maintains indexes that point to the relevant S3 objects and offsets, allowing the system to skip unnecessary lookups.
  • Prefetching: when the query plan expects to scan multiple blocks, subsequent blocks are proactively loaded into cache.
  • Cache hydration: newly written data is immediately preloaded into memory and disk caches so that subsequent reads do not fall back to S3.

Deep Integration with RisingWave

Foyer is not a bolt-on component but a deeply integrated part of RisingWave’s storage stack.

  • It manages disk-level I/O, including read and write scheduling as well as cache invalidation and recovery.
  • It feeds back information about access patterns, helping RisingWave adapt caching strategies dynamically.
  • It works closely with RisingWave’s compaction process, ensuring that recent or frequently accessed data remains cached while older cold data is pushed out.

Through this design, Foyer makes it possible for RisingWave to combine the elasticity and cost benefits of S3 with the low-latency requirements of stream processing.

联系我们 contact @ memedata.com