subms_lsm_tree/features/
block_cache_integration.rs1use std::collections::HashMap;
20use std::sync::Arc;
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
24pub struct BlockKey {
25 pub sstable_id: u64,
26 pub block_offset: u64,
27}
28
29impl BlockKey {
30 pub fn new(sstable_id: u64, block_offset: u64) -> Self {
31 Self {
32 sstable_id,
33 block_offset,
34 }
35 }
36}
37
38pub type Block = Arc<[u8]>;
41
42pub trait BlockCache: Send + Sync {
45 fn get(&self, key: &BlockKey) -> Option<Block>;
47
48 fn put(&self, key: BlockKey, block: Block);
50
51 fn len(&self) -> usize;
53
54 fn is_empty(&self) -> bool {
56 self.len() == 0
57 }
58
59 fn clear(&self);
61}
62
63pub struct LruBlockCache {
70 inner: std::sync::Mutex<LruInner>,
71}
72
73struct LruInner {
74 capacity: usize,
75 map: HashMap<BlockKey, usize>,
76 nodes: Vec<Node>,
77 head: Option<usize>,
78 tail: Option<usize>,
79 free: Vec<usize>,
80 hits: u64,
81 misses: u64,
82}
83
84struct Node {
85 key: BlockKey,
86 block: Block,
87 prev: Option<usize>,
88 next: Option<usize>,
89}
90
91impl LruBlockCache {
92 pub fn new(capacity: usize) -> Self {
93 let cap = capacity.max(1);
94 Self {
95 inner: std::sync::Mutex::new(LruInner {
96 capacity: cap,
97 map: HashMap::with_capacity(cap),
98 nodes: Vec::with_capacity(cap),
99 head: None,
100 tail: None,
101 free: Vec::new(),
102 hits: 0,
103 misses: 0,
104 }),
105 }
106 }
107
108 pub fn capacity(&self) -> usize {
109 self.inner.lock().unwrap().capacity
110 }
111
112 pub fn hits(&self) -> u64 {
113 self.inner.lock().unwrap().hits
114 }
115
116 pub fn misses(&self) -> u64 {
117 self.inner.lock().unwrap().misses
118 }
119}
120
121impl BlockCache for LruBlockCache {
122 fn get(&self, key: &BlockKey) -> Option<Block> {
123 let mut g = self.inner.lock().unwrap();
124 match g.map.get(key).copied() {
125 Some(idx) => {
126 let block = g.nodes[idx].block.clone();
127 g.move_to_front(idx);
128 g.hits += 1;
129 Some(block)
130 }
131 None => {
132 g.misses += 1;
133 None
134 }
135 }
136 }
137
138 fn put(&self, key: BlockKey, block: Block) {
139 let mut g = self.inner.lock().unwrap();
140 if let Some(&idx) = g.map.get(&key) {
142 g.nodes[idx].block = block;
143 g.move_to_front(idx);
144 return;
145 }
146 if g.map.len() >= g.capacity {
148 if let Some(tail_idx) = g.tail {
149 let tail_key = g.nodes[tail_idx].key;
150 g.detach(tail_idx);
151 g.map.remove(&tail_key);
152 g.free.push(tail_idx);
153 }
154 }
155 let idx = if let Some(slot) = g.free.pop() {
157 g.nodes[slot] = Node {
158 key,
159 block,
160 prev: None,
161 next: None,
162 };
163 slot
164 } else {
165 g.nodes.push(Node {
166 key,
167 block,
168 prev: None,
169 next: None,
170 });
171 g.nodes.len() - 1
172 };
173 g.map.insert(key, idx);
174 g.push_front(idx);
175 }
176
177 fn len(&self) -> usize {
178 self.inner.lock().unwrap().map.len()
179 }
180
181 fn clear(&self) {
182 let mut g = self.inner.lock().unwrap();
183 g.map.clear();
184 g.nodes.clear();
185 g.free.clear();
186 g.head = None;
187 g.tail = None;
188 }
189}
190
191impl LruInner {
192 fn push_front(&mut self, idx: usize) {
193 self.nodes[idx].prev = None;
194 self.nodes[idx].next = self.head;
195 if let Some(h) = self.head {
196 self.nodes[h].prev = Some(idx);
197 }
198 self.head = Some(idx);
199 if self.tail.is_none() {
200 self.tail = Some(idx);
201 }
202 }
203
204 fn detach(&mut self, idx: usize) {
205 let prev = self.nodes[idx].prev;
206 let next = self.nodes[idx].next;
207 if let Some(p) = prev {
208 self.nodes[p].next = next;
209 } else {
210 self.head = next;
211 }
212 if let Some(n) = next {
213 self.nodes[n].prev = prev;
214 } else {
215 self.tail = prev;
216 }
217 self.nodes[idx].prev = None;
218 self.nodes[idx].next = None;
219 }
220
221 fn move_to_front(&mut self, idx: usize) {
222 if self.head == Some(idx) {
223 return;
224 }
225 self.detach(idx);
226 self.push_front(idx);
227 }
228}
229
230#[cfg(test)]
231#[path = "block_cache_integration_tests.rs"]
232mod tests;