Skip to main content

awa_ui/
cache.rs

1use std::collections::HashMap;
2use std::sync::Arc;
3use std::time::Duration;
4
5use awa_model::admin;
6use awa_model::job::JobState;
7use awa_model::AwaError;
8use moka::future::Cache;
9
10/// Typed, TTL-based caches for the dashboard's hot endpoints.
11///
12/// Each endpoint gets its own `moka::future::Cache` so values are
13/// fully typed. Moka handles expiry, eviction, and request coalescing
14/// (`try_get_with` ensures only one in-flight fetch per key while
15/// concurrent callers wait).
16///
17/// All caches use a single `()` key since each stores one aggregated
18/// response value.
19#[derive(Clone)]
20pub struct DashboardCache {
21    pub stats: Cache<(), HashMap<JobState, i64>>,
22    pub queues: Cache<(), Vec<admin::QueueOverview>>,
23    pub runtime: Cache<(), admin::RuntimeOverview>,
24    pub queue_runtime: Cache<(), Vec<admin::QueueRuntimeSummary>>,
25    pub storage: Cache<(), awa_model::storage::StorageStatusReport>,
26}
27
28fn build_cache<V: Clone + Send + Sync + 'static>(ttl: Duration) -> Cache<(), V> {
29    Cache::builder().time_to_live(ttl).max_capacity(1).build()
30}
31
32impl DashboardCache {
33    pub fn new(ttl: Duration) -> Self {
34        Self {
35            stats: build_cache(ttl),
36            queues: build_cache(ttl),
37            runtime: build_cache(ttl),
38            queue_runtime: build_cache(ttl),
39            storage: build_cache(ttl),
40        }
41    }
42}
43
44/// Shared error wrapper so moka's `try_get_with` can propagate errors.
45///
46/// `AwaError` is not `Clone`, but moka requires the error from
47/// `try_get_with` to be `Clone + Send + Sync`. We wrap it in an `Arc`.
48#[derive(Debug, Clone)]
49pub struct CacheError(pub Arc<AwaError>);
50
51impl From<AwaError> for CacheError {
52    fn from(err: AwaError) -> Self {
53        Self(Arc::new(err))
54    }
55}