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