Skip to main content

micromegas_object_cache/
bounded_memory_backend.rs

1use async_trait::async_trait;
2use bytes::Bytes;
3use foyer_memory::{Cache, CacheBuilder, LfuConfig};
4
5use super::backend::{FillHint, RangeCacheBackend};
6
7/// A byte-weighted, sharded, in-memory `RangeCacheBackend` bounded to a fixed
8/// capacity, built on the standalone `foyer-memory` crate's `Cache` -- not the
9/// umbrella `foyer` crate (which `FoyerBackend` uses via `HybridCache`, and
10/// which transitively pulls in a disk-backed storage engine). `foyer-memory`
11/// is the same in-memory engine `HybridCache` uses internally for its RAM
12/// tier, so this reuses that eviction implementation without the disk-IO
13/// dependencies, via `LfuConfig` rather than `FoyerBackend`'s `LruConfig`.
14///
15/// Used as the shared RAM budget backing the in-process L1 cache
16/// (`l1_store.rs`).
17pub struct BoundedMemoryBackend {
18    cache: Cache<String, Bytes>,
19}
20
21impl BoundedMemoryBackend {
22    /// `budget_bytes` bounds the total weighted (byte) size of cached
23    /// entries; foyer handles eviction and sharded concurrency internally.
24    pub fn new(budget_bytes: usize) -> Self {
25        // The explicit `Cache<String, Bytes>` type here (matching the field's
26        // type) pins `build`'s inferred `Properties` generic to `Cache`'s
27        // default (`CacheProperties`); `build` alone has no default for it.
28        let cache: Cache<String, Bytes> = CacheBuilder::new(budget_bytes)
29            .with_weighter(|_key: &String, value: &Bytes| value.len())
30            .with_eviction_config(LfuConfig::default())
31            .build();
32        Self { cache }
33    }
34
35    /// Current weighted (byte) usage, exposed for tests.
36    pub fn usage(&self) -> usize {
37        self.cache.usage()
38    }
39}
40
41#[async_trait]
42impl RangeCacheBackend for BoundedMemoryBackend {
43    async fn get(&self, key: &str) -> Option<Bytes> {
44        self.cache.get(key).map(|entry| entry.value().clone())
45    }
46
47    async fn put(&self, key: String, value: Bytes, _hint: FillHint) {
48        // No disk tier, so demand and prefetch fills are treated identically
49        // (see `FillHint`'s docs and the L1 design notes): there is no
50        // SSD-only admission path to route a prefetch fill through, and
51        // `foyer_memory::Cache` exposes only a plain `insert`.
52        self.cache.insert(key, value);
53    }
54}