Skip to main content

sva_engine/cache/
tiered.rs

1// Concern: fronts a persistent store with this process's heap, promoting what the back answers | Non-concern: either tier's medium or budget | IO: (Hash) -> a payload + traces, and its tier
2
3use std::path::Path;
4use std::time::Duration;
5
6use sva_formula::Hash;
7use sva_samples::{FilterTrace, Label};
8
9use super::{Cache, Entry, Expected, Medium, MemoryCache, Pack, Payload, PayloadKind};
10
11/// The back is a pack and nothing else: `store` carries no cost, and a pack admits by kind alone,
12/// so no back gated on cost can sit here and silently keep nothing.
13pub struct Tiered<M: Medium> {
14    pub front: MemoryCache,
15    pub back: Pack<M>,
16}
17
18impl<M: Medium> Tiered<M> {
19    pub fn new(front: MemoryCache, back: Pack<M>) -> Tiered<M> {
20        Tiered { front, back }
21    }
22}
23
24impl<M: Medium> Cache for Tiered<M> {
25    fn load(&self, key: Hash, node: &str, expected: Expected) -> Option<Entry> {
26        if let Some(entry) = self.front.load(key, node, expected) {
27            return Some(entry);
28        }
29        let entry = self.back.load(key, node, expected)?;
30        self.front
31            .store(key, &entry.payload, &entry.traces, entry.label.as_ref());
32        Some(entry)
33    }
34
35    fn store(&self, key: Hash, payload: &Payload, traces: &[FilterTrace], label: Option<&Label>) {
36        self.front.store(key, payload, traces, label);
37        if self
38            .back
39            .worth_storing(Duration::ZERO, payload.bytes(), payload.kind())
40        {
41            self.back.store(key, payload, traces, label);
42        }
43    }
44
45    fn holds(&self, key: Hash) -> bool {
46        self.front.holds(key) || self.back.holds(key)
47    }
48
49    fn worth_storing(&self, cost: Duration, bytes: usize, kind: PayloadKind) -> bool {
50        self.front.worth_storing(cost, bytes, kind) || self.back.worth_storing(cost, bytes, kind)
51    }
52
53    fn sweep(&self) {
54        self.front.sweep();
55        self.back.sweep();
56    }
57
58    /// Both tiers' own bytes, summed: a value in both is held twice.
59    fn held_bytes(&self) -> u64 {
60        self.front.held_bytes() + self.back.held_bytes()
61    }
62
63    fn evicted_bytes(&self) -> u64 {
64        self.front.evicted_bytes() + self.back.evicted_bytes()
65    }
66
67    fn faults(&self) -> u64 {
68        self.front.faults() + self.back.faults()
69    }
70
71    fn max_bytes(&self) -> u64 {
72        self.front.max_bytes().saturating_add(self.back.max_bytes())
73    }
74
75    fn dir(&self) -> Option<&Path> {
76        self.back.dir()
77    }
78}