vantage_live/cache/mod.rs
1//! Cache backend abstraction for `LiveTable`.
2//!
3//! `LiveTable` only knows about [`Cache`] — concrete backends like
4//! [`MemCache`], [`NoCache`], or `RedbCache` plug in via this trait. The
5//! storage shape is `key → CachedRows`; keys are caller-supplied
6//! `cache_key` plus a per-page suffix produced by `LiveTable`. See
7//! `DESIGN.md` for the keying contract.
8
9use async_trait::async_trait;
10use indexmap::IndexMap;
11use std::time::SystemTime;
12use vantage_core::Result;
13use vantage_types::Record;
14
15mod mem;
16mod noop;
17mod redb;
18
19pub use mem::MemCache;
20pub use noop::NoCache;
21pub use redb::RedbCache;
22
23/// A row set as stored in the cache, with the wall-clock instant the master
24/// fetch completed. Fetch time isn't used in v1 (no TTL), but we keep it
25/// because every backend can provide it cheaply and it's the obvious knob
26/// to add later.
27#[derive(Clone, Debug)]
28pub struct CachedRows {
29 pub rows: IndexMap<String, Record<ciborium::Value>>,
30 pub fetched_at: SystemTime,
31}
32
33impl CachedRows {
34 pub fn new(rows: IndexMap<String, Record<ciborium::Value>>) -> Self {
35 Self {
36 rows,
37 fetched_at: SystemTime::now(),
38 }
39 }
40}
41
42/// Cache backend interface. Implementations must be safe to share across
43/// the read path, the write-queue worker, and the live-event consumer.
44#[async_trait]
45pub trait Cache: Send + Sync {
46 /// Look up `key`; `Ok(None)` is a normal miss, `Err` is reserved for
47 /// real backend faults (disk error, redb panic recovery, etc.).
48 async fn get(&self, key: &str) -> Result<Option<CachedRows>>;
49
50 /// Store `rows` under `key`, replacing whatever was there.
51 async fn put(&self, key: &str, rows: CachedRows) -> Result<()>;
52
53 /// Drop every entry whose key starts with `prefix`. v1 callers always
54 /// pass the bare `cache_key` — every page suffix below it goes.
55 /// Implementations should accept any prefix (so finer-grained
56 /// invalidation can be added later without breaking the trait).
57 async fn invalidate_prefix(&self, prefix: &str) -> Result<()>;
58}