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}