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 peek(&self, key: Hash, node: &str, expected: Expected) -> Option<Entry> {
36        self.front
37            .peek(key, node, expected)
38            .or_else(|| self.back.peek(key, node, expected))
39    }
40
41    fn store(&self, key: Hash, payload: &Payload, traces: &[FilterTrace], label: Option<&Label>) {
42        self.front.store(key, payload, traces, label);
43        if self
44            .back
45            .worth_storing(Duration::ZERO, payload.bytes(), payload.kind())
46        {
47            self.back.store(key, payload, traces, label);
48        }
49    }
50
51    fn holds(&self, key: Hash) -> bool {
52        self.front.holds(key) || self.back.holds(key)
53    }
54
55    fn worth_storing(&self, cost: Duration, bytes: usize, kind: PayloadKind) -> bool {
56        self.front.worth_storing(cost, bytes, kind) || self.back.worth_storing(cost, bytes, kind)
57    }
58
59    fn sweep(&self) {
60        self.front.sweep();
61        self.back.sweep();
62    }
63
64    /// Both tiers' own bytes, summed: a value in both is held twice.
65    fn held_bytes(&self) -> u64 {
66        self.front.held_bytes() + self.back.held_bytes()
67    }
68
69    fn evicted_bytes(&self) -> u64 {
70        self.front.evicted_bytes() + self.back.evicted_bytes()
71    }
72
73    fn faults(&self) -> u64 {
74        self.front.faults() + self.back.faults()
75    }
76
77    fn max_bytes(&self) -> u64 {
78        self.front.max_bytes().saturating_add(self.back.max_bytes())
79    }
80
81    fn dir(&self) -> Option<&Path> {
82        self.back.dir()
83    }
84}