bext_plugin_api/cache.rs
1//! Cache backend plugin trait and types for the two-tier (L1 in-memory / L2 plugin)
2//! ISR cache, including [`CacheEntry`], lookup status, and tag-based invalidation.
3
4/// Serializable cache entry for cross-backend compatibility.
5///
6/// When a `CacheBackend` plugin is registered, the ISR cache writes entries
7/// to both the local in-memory LRU (L1) and the backend (L2). On reads,
8/// L1 is checked first; on miss, the backend is queried and L1 is populated.
9#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
10pub struct CacheEntry {
11 pub html: String,
12 pub headers: Vec<(String, String)>,
13 pub etag: String,
14 pub tags: Vec<String>,
15 pub created_at_ms: u64,
16}
17
18/// Result of a cache lookup.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum CacheLookupStatus {
21 /// Fresh entry found.
22 Hit,
23 /// Entry found but past TTL (within SWR window).
24 Stale,
25 /// No entry found.
26 Miss,
27}
28
29/// Alternative cache storage backend (e.g., Redis, S3, Cloudflare KV).
30///
31/// Replaces or wraps the built-in LRU cache for ISR entries.
32/// Only one backend can be active at a time.
33pub trait CacheBackend: Send + Sync {
34 /// Unique identifier (e.g., `"redis"`, `"cloudflare-kv"`).
35 fn name(&self) -> &str;
36
37 /// Look up a cached entry. Returns `Ok(None)` on miss.
38 fn get(&self, key: &str) -> Result<Option<CacheEntry>, String>;
39
40 /// Store an entry with optional TTL in milliseconds.
41 fn set(&self, key: &str, entry: CacheEntry, ttl_ms: Option<u64>) -> Result<(), String>;
42
43 /// Delete a single entry by key. Returns whether it existed.
44 fn delete(&self, key: &str) -> Result<bool, String>;
45
46 /// Invalidate all entries tagged with `tag`. Returns count invalidated.
47 fn invalidate_tag(&self, tag: &str) -> Result<u32, String>;
48
49 /// Invalidate entries matching a path glob. Returns count invalidated.
50 fn invalidate_path(&self, pattern: &str) -> Result<u32, String>;
51
52 /// Health check. Default returns `true`.
53 fn is_healthy(&self) -> bool {
54 true
55 }
56}