metrale_storage/
cascade_policy.rs1use std::collections::{HashMap, VecDeque};
13
14use crate::group::GroupKey;
15
16#[derive(Clone, Copy, Debug, PartialEq, Eq)]
20pub struct WritePlan {
21 pub slot: u32,
22 pub flush_victim: Option<(GroupKey, u32)>,
23}
24
25pub struct SlotCache {
28 cap_slots: u32,
29 lookup: HashMap<GroupKey, u32>,
30 slot_key: Vec<Option<GroupKey>>,
31 free: VecDeque<u32>,
32 lru: VecDeque<u32>,
35}
36
37impl SlotCache {
38 pub fn new(cap_slots: u32) -> Self {
39 assert!(cap_slots > 0, "SlotCache needs at least one slot");
40 Self {
41 cap_slots,
42 lookup: HashMap::new(),
43 slot_key: vec![None; cap_slots as usize],
44 free: (0..cap_slots).collect(),
45 lru: VecDeque::with_capacity(cap_slots as usize),
46 }
47 }
48
49 pub fn capacity(&self) -> u32 {
50 self.cap_slots
51 }
52
53 fn move_to_back(&mut self, slot: u32) {
54 if let Some(pos) = self.lru.iter().position(|&s| s == slot) {
55 self.lru.remove(pos);
56 }
57 self.lru.push_back(slot);
58 }
59
60 pub fn touch(&mut self, slot: u32) {
63 self.move_to_back(slot);
64 }
65
66 pub fn plan_write(&mut self, key: GroupKey) -> WritePlan {
70 if let Some(&slot) = self.lookup.get(&key) {
71 self.move_to_back(slot);
72 return WritePlan {
73 slot,
74 flush_victim: None,
75 };
76 }
77 if let Some(slot) = self.free.pop_front() {
78 self.install(key, slot);
79 return WritePlan {
80 slot,
81 flush_victim: None,
82 };
83 }
84 let victim_slot = self.lru.pop_front().expect("full cache has an LRU entry");
85 let victim_key = self.slot_key[victim_slot as usize]
86 .take()
87 .expect("occupied slot has a key");
88 self.lookup.remove(&victim_key);
89 self.install(key, victim_slot);
90 WritePlan {
91 slot: victim_slot,
92 flush_victim: Some((victim_key, victim_slot)),
93 }
94 }
95
96 fn install(&mut self, key: GroupKey, slot: u32) {
97 self.lookup.insert(key, slot);
98 self.slot_key[slot as usize] = Some(key);
99 self.lru.push_back(slot);
100 }
101
102 pub fn plan_read(&self, keys: &[GroupKey]) -> (Vec<(usize, u32)>, Vec<usize>) {
106 let mut hits = Vec::new();
107 let mut misses = Vec::new();
108 for (i, k) in keys.iter().enumerate() {
109 match self.lookup.get(k) {
110 Some(&slot) => hits.push((i, slot)),
111 None => misses.push(i),
112 }
113 }
114 (hits, misses)
115 }
116
117 pub fn residents(&self) -> Vec<(GroupKey, u32)> {
119 self.lookup.iter().map(|(&k, &s)| (k, s)).collect()
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126 use crate::group::KvKind;
127
128 fn k(block: u32) -> GroupKey {
129 GroupKey::new(0, block, 0, KvKind::K)
130 }
131
132 #[test]
133 fn free_slots_then_overwrite_in_place() {
134 let mut c = SlotCache::new(3);
135 let p0 = c.plan_write(k(0));
136 assert_eq!(p0.flush_victim, None);
137 let p1 = c.plan_write(k(1));
138 assert_ne!(p1.slot, p0.slot);
139 let p0b = c.plan_write(k(0));
140 assert_eq!(p0b.slot, p0.slot);
141 assert_eq!(p0b.flush_victim, None);
142 }
143
144 #[test]
145 fn fill_then_evict_lru_tail() {
146 let mut c = SlotCache::new(2);
147 let s0 = c.plan_write(k(0)).slot;
148 let _s1 = c.plan_write(k(1)).slot;
149 let (hits, _) = c.plan_read(&[k(0)]);
150 c.touch(hits[0].1);
151 let p2 = c.plan_write(k(2));
152 assert_eq!(p2.flush_victim.map(|(vk, _)| vk), Some(k(1)));
153 assert_ne!(p2.slot, s0);
154 }
155
156 #[test]
157 fn hit_miss_partition() {
158 let mut c = SlotCache::new(4);
159 c.plan_write(k(0));
160 c.plan_write(k(2));
161 let (hits, misses) = c.plan_read(&[k(0), k(1), k(2), k(3)]);
162 let hit_idx: Vec<usize> = hits.iter().map(|(i, _)| *i).collect();
163 assert_eq!(hit_idx, vec![0, 2]);
164 assert_eq!(misses, vec![1, 3]);
165 }
166
167 #[test]
168 fn residents_lists_all_live_groups() {
169 let mut c = SlotCache::new(3);
170 c.plan_write(k(0));
171 c.plan_write(k(1));
172 let mut r: Vec<GroupKey> = c.residents().into_iter().map(|(k, _)| k).collect();
173 r.sort_by_key(|g| g.block);
174 assert_eq!(r, vec![k(0), k(1)]);
175 }
176
177 #[test]
178 fn evicted_group_is_no_longer_a_hit() {
179 let mut c = SlotCache::new(1);
180 c.plan_write(k(0));
181 let p = c.plan_write(k(1));
182 assert_eq!(p.flush_victim.map(|(vk, _)| vk), Some(k(0)));
183 let (hits, misses) = c.plan_read(&[k(0), k(1)]);
184 assert_eq!(hits.iter().map(|(i, _)| *i).collect::<Vec<_>>(), vec![1]);
185 assert_eq!(misses, vec![0]);
186 }
187}