Library / SDK
foyer-rs/foyer avatar
foyer-rs/foyer

foyer keeps the hot set in memory and spills on eviction, not on a timer

Hybrid in-memory and disk cache in Rust

1,836 stars111 forksRustApache-2.0

At a glance

What is it?
foyer is an Apache-2.0 Rust hybrid cache with a pluggable in-memory eviction policy and a block-based disk engine, built for Rust 1.95 and edition 2024. Its default hybrid policy writes to storage when an in-memory entry is evicted.
Who is it for?
foyer suits a Rust service that needs a second-level cache where Redis would add a network hop and an LRU map would throw entries away, and that is prepared to tune capacity and admission per workload. It is the wrong choice if you want a stable API on an old toolchain, since the workspace declares rust-version 1.95.0 and edition 2024.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 14 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A memory cache you can build in one line

foyer is a Rust hybrid cache library, Apache-2.0 licensed, and its first claim is that the in-memory side is nearly free to use.

The dependency is a Cargo.toml line, and the nightly variant is a feature flag on the same line:

toml
foyer = "0.22"

with foyer = { version = "0.22", features = ["nightly"] } when the project is on the nightly toolchain.

A working memory cache is a builder with a capacity, an insert and a get:

rust
use foyer::{Cache, CacheBuilder};

let cache: Cache<String, String> = CacheBuilder::new(16).build();

let entry = cache.insert("hello".to_string(), "world".to_string());
let e = cache.get("hello").unwrap();

assert_eq!(entry.value(), e.value());

Two API details are worth pausing on. insert returns an entry handle whose value method you can read, and get returns an Option you unwrap, so the cache returns references rather than copies, which is what the zero-copy in-memory abstraction in the feature list refers to.

The project says it draws inspiration from Facebook's CacheLib and from Caffeine, and states plainly that it is not just a rewrite in Rust but adds new and optimised features.

Eviction is the trigger for writing to disk

The hybrid path is where the design decisions are visible. The device is declared separately with a capacity, and the cache is given a memory size plus a storage engine:

rust
let hybrid: HybridCache<u64, String> = HybridCacheBuilder::new()
    .memory(64 * 1024 * 1024)
    .storage()
    // use block-based disk cache engine with default configuration
    .with_engine_config(BlockEngineConfig::new(device))
    .build()
    .await?;

The policy matters more than the sizes. The full configuration example sets HybridCachePolicy::WriteOnEviction, which is the whole idea in one name: an entry that is about to be dropped from memory is written to storage on the way out. Nothing writes to disk for entries that stay hot, and nothing writes entries that never become hot enough to be admitted.

That gives you a memory budget that behaves like a working set and a disk that behaves like overflow, rather than a write-through cache that pays disk IOPS for every write whether or not anyone reads it again.

The device builder is what stands in front of the storage. FsDeviceBuilder takes a directory and a capacity, and with_capacity in the example sets 256 MB in the simple case and 64 MB in the fully tuned one, with a comment that these are per-device capacities rather than a global setting.

The tuning surface is the documentation

The fully configured example in the README is the closest thing to a reference, and it shows how many axes there are.

On the device side there is a Throttle with read and write IOPS limits and read and write throughput limits, in this example 4000 read IOPS, 2000 write IOPS, 100 MB/s write throughput and 800 MB/s read throughput, plus an IopsCounter that decides how a throttle request is accounted for, here PerIoSize at 128 KB. There is also RecoverMode and RejectAll in the imports, which govern what happens to the device when it cannot be opened in the expected state.

On the cache side there is with_name, with_policy, memory, with_shards set to 4, an eviction config carrying high_priority_pool_ratio at 0.1, a BuildHasherDefault for the hasher, a weigher closure that in the example returns value.len(), and a filter closure that returns true. Those last two are the interesting ones: the weigher makes capacity a function of your own notion of size rather than entry count, and the filter is an admission predicate, so you can reject entries before they ever consume capacity.

The storage side has an I/O engine choice, PsyncIoEngineConfig in the example, and engine settings including with_block_size at 16 MB, with_indexer_shards at 64, with_recover_concurrency at 8 and with_flushers at 2.

None of this is documented in prose in the README. The example is the specification, and docs.rs is where the semantics live.

Replaceable algorithms, and observability in one line

The feature list calls out plug-and-play algorithms, meaning the in-memory eviction policy is replaceable rather than fixed. The example configures LruConfig with a high_priority_pool_ratio, which implies more than one policy type exists and that the split between a high-priority pool and the rest is a property of the policy.

The observability claim is that popular observation systems integrate in one line, naming Prometheus, Grafana, OpenTelemetry and Jaeger. The repository backs that with the dependency list, which includes fastrace and fastrace-opentelemetry, console-subscriber, hdrhistogram and datasketches, and by example files named export_metrics_prometheus_hyper.rs and tail_based_tracing.rs. The tracing integration is tail-based, which matters because a hot cache path is exactly where per-span logging would cost you.

Concurrency is another listed property, described as handling high concurrency with thread-safe mechanisms. The example also exposes a with_shards option; sharding can reduce lock contention in a memory cache.

The workspace also has a separate foyer-bench member and uses criterion in the dependency list, so there is a benchmark harness in the tree even though the README publishes no numbers.

A workspace, not a single crate

The Cargo.toml makes the internal structure visible, and it explains why foyer has siblings rather than modules.

The workspace members are examples, foyer, foyer-bench, foyer-common, foyer-memory, foyer-opendal, foyer-storage, foyer-tokio and xtask. The main foyer crate depends on foyer-common, foyer-memory and foyer-storage as separate crates, so the memory cache, the storage engine and the shared types are independently versioned abstractions that the hybrid cache composes.

foyer-opendal is the interesting one for deployment. The workspace includes foyer-opendal and examples/opendal.rs.

The workspace metadata sets edition 2024 and rust-version 1.95.0 at the workspace level, with version 0.22.6 shared across members. A toolchain requirement of that recency is the practical constraint to plan around, since it rules out older build images.

The top-level tree also carries a docker-compose.yaml, an xtask for repository automation,.licenserc.toml for licence header checking,.typos.toml and separate rustfmt.toml plus rustfmt.nightly.toml, and the LICENSE checker has its own CI workflow.

Where foyer stops, and what to compare it against

Three boundaries are worth naming.

It is a library rather than a service. If several application instances need one shared cache, you either accept per-instance caches or keep a networked tier for the shared parts.

Disk storage durability is a design question you configure rather than a default you accept. RecoverMode and RejectAll appear in the tuning example, which tells you the library expects you to decide how a device behaves when it finds state it did not expect. That decision belongs in your runbook.

The dependency surface includes asyncband, fs4, bitflags, hashbrown, bytes, bincode and the tracing stack, along with a nightly feature flag. That is a normal price for a caching library with pluggable engines, and it is a cost to weigh against writing a small sharded map.

The realistic comparison is CacheLib and Caffeine in other languages, or a plain lru crate plus your own spill logic. Those give you less machinery and no disk tier. The projects listed as using foyer give a sense of the workload shape instead: RisingWave, Chroma, SlateDB, ZeroFS and Percas, which are database and storage systems where a second-level cache in the same process is a natural fit.

Release cadence is high and the version is still 0.x: v0.22.6, v0.22.5 and v0.22.4 in the space of a few weeks in late 2026, with the last push on 2026-10-09. Expect the API to move and pin accordingly.

Editorial conclusion

foyer suits a Rust service that needs a second-level cache where Redis would add a network hop and an LRU map would throw entries away, and that is prepared to tune capacity and admission per workload. It is the wrong choice if you want a stable API on an old toolchain, since the workspace declares rust-version 1.95.0 and edition 2024. Verify first which eviction policy and capacity model you want, because HybridCachePolicy::WriteOnEviction and a per-entry weighter change what your memory and disk numbers mean, and check that your nightly feature is only enabled if you actually need it.

Frequently asked questions

How do I add foyer to a Rust project?

Add foyer = "0.22" to the dependencies section of Cargo.toml, or foyer = { version = "0.22", features = ["nightly"] } if you are on the nightly toolchain.

When does foyer write cache entries to disk?

Under the HybridCachePolicy::WriteOnEviction policy used in the README's fully configured example, an entry is written to storage when it is evicted from memory rather than on every write, so hot entries never pay disk IOPS.

What Rust version does foyer require?

The workspace declares rust-version 1.95.0 with edition 2024, and an MSRV badge is published for the crate. A nightly feature exists for projects on the nightly toolchain.

Official sources

  1. foyer-rs/foyer on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/foyer-rs-foyer.svg)](https://hysenlabs.com/projects/foyer-rs-foyer)

Was this useful?