1use parking_lot::Mutex;
19
20use metrale_telemetry::prefix_cache::{EvictedBlocks, PrefixCache, PrefixMatch};
21
22mod inner;
23mod partial_tail;
24mod snapshot;
25mod snapshot_insert;
26mod snapshot_session;
27mod snapshot_stats;
28mod snapshot_tier;
29
30#[cfg(test)]
31mod tests;
32
33use inner::RadixTreeInner;
34use snapshot::SsmSnapshotIndex;
35
36pub(crate) fn hash_token_prefix(tokens: &[u32], count: usize, adapter_id: u64) -> u64 {
41 let mut h: u64 = 0xcbf29ce484222325;
42 if adapter_id != 0 {
43 h ^= adapter_id;
44 h = h.wrapping_mul(0x100000001b3);
45 }
46 for &t in &tokens[..count] {
47 h ^= t as u64;
48 h = h.wrapping_mul(0x100000001b3);
49 }
50 h
51}
52
53pub struct RadixTree {
56 inner: Mutex<RadixTreeInner>,
57 snapshot_index: Mutex<SsmSnapshotIndex>,
58}
59
60impl Default for RadixTree {
61 fn default() -> Self {
62 Self::new()
63 }
64}
65
66impl RadixTree {
67 pub fn new() -> Self {
68 Self {
69 inner: Mutex::new(RadixTreeInner::new()),
70 snapshot_index: Mutex::new(SsmSnapshotIndex::new()),
71 }
72 }
73}
74
75impl PrefixCache for RadixTree {
76 fn lookup(
77 &self,
78 tokens: &[u32],
79 block_size: usize,
80 session_hash: u64,
81 adapter_id: u64,
82 ) -> PrefixMatch {
83 let (matched_blocks, matched_disk_block_ids, matched_tokens) = {
85 let mut inner = self.inner.lock();
86 let (blocks, disk, matched) = inner.walk(tokens, block_size, adapter_id);
87 if matched > 0 {
88 inner.inc_refs(tokens, block_size, matched, adapter_id);
89 metrale_telemetry::prefix_cache::record_cache_hit(matched);
90 } else {
91 metrale_telemetry::prefix_cache::record_cache_miss();
92 }
93 (blocks, disk, matched)
94 };
95 let mut ssm_snapshot = None;
100 let mut ssm_snapshot_tokens = 0;
101 let mut ssm_snapshot_tier_key = None;
102 let mut ssm_snapshot_tier_tokens = 0;
103 let mut ssm_snapshot_is_tail = false;
104 if matched_tokens > 0 {
105 let mut idx = self.snapshot_index.lock();
106 if let Some(m) = idx.lookup_tiered(tokens, matched_tokens, session_hash, adapter_id) {
107 ssm_snapshot_is_tail = m.is_tail;
108 match m.loc {
109 snapshot::SnapLoc::Hbm(slot) => {
110 ssm_snapshot = Some(slot);
111 ssm_snapshot_tokens = m.token_count;
112 }
113 snapshot::SnapLoc::Tier(key) => {
114 ssm_snapshot_tier_key = Some(key);
115 ssm_snapshot_tier_tokens = m.token_count;
116 }
117 }
118 }
119 }
120 let matched_disk_block_ids = if matched_disk_block_ids.iter().all(|&id| id == u32::MAX) {
124 Vec::new()
125 } else {
126 matched_disk_block_ids
127 };
128 PrefixMatch {
129 matched_blocks,
130 matched_disk_block_ids,
131 matched_tokens,
132 ssm_snapshot,
133 ssm_snapshot_tokens,
134 ssm_snapshot_tier_key,
135 ssm_snapshot_tier_tokens,
136 ssm_snapshot_is_tail,
137 }
138 }
139
140 fn peek_matched_tokens(&self, tokens: &[u32], block_size: usize, adapter_id: u64) -> usize {
141 self.inner.lock().walk(tokens, block_size, adapter_id).2
142 }
143
144 fn pin_prefix(&self, tokens: &[u32], block_size: usize, adapter_id: u64) -> usize {
145 let mut inner = self.inner.lock();
146 let matched = inner.walk(tokens, block_size, adapter_id).2;
147 if matched > 0 {
148 inner.inc_refs(tokens, block_size, matched, adapter_id);
149 }
150 matched
151 }
152
153 fn insert(
154 &self,
155 tokens: &[u32],
156 block_table: &[u32],
157 disk_block_ids: &[u32],
158 block_size: usize,
159 matched_tokens: usize,
160 adapter_id: u64,
161 ) -> metrale_telemetry::prefix_cache::InsertAcquired {
162 self.inner.lock().insert(
163 tokens,
164 block_table,
165 disk_block_ids,
166 block_size,
167 matched_tokens,
168 adapter_id,
169 )
170 }
171
172 fn insert_with_snapshot(
173 &self,
174 tokens: &[u32],
175 block_table: &[u32],
176 disk_block_ids: &[u32],
177 block_size: usize,
178 snapshot_id: usize,
179 session_hash: u64,
180 matched_tokens: usize,
181 adapter_id: u64,
182 ) -> (
183 Option<usize>,
184 metrale_telemetry::prefix_cache::InsertAcquired,
185 ) {
186 let newly_acquired = self.inner.lock().insert(
189 tokens,
190 block_table,
191 disk_block_ids,
192 block_size,
193 matched_tokens,
194 adapter_id,
195 );
196 let prefix_hash = hash_token_prefix(tokens, tokens.len(), adapter_id);
197 let mut idx = self.snapshot_index.lock();
198 let displaced = idx.insert(prefix_hash, snapshot_id, session_hash, tokens.len());
199 (displaced, newly_acquired)
200 }
201
202 fn insert_tail_snapshot(
203 &self,
204 tokens: &[u32],
205 snapshot_id: usize,
206 session_hash: u64,
207 adapter_id: u64,
208 ) -> Vec<usize> {
209 let prefix_hash = hash_token_prefix(tokens, tokens.len(), adapter_id);
211 self.snapshot_index
212 .lock()
213 .insert_tail(prefix_hash, snapshot_id, session_hash, tokens.len())
214 }
215
216 fn insert_tail_sibling_snapshot(
217 &self,
218 tokens: &[u32],
219 snapshot_id: usize,
220 session_hash: u64,
221 adapter_id: u64,
222 ) -> Option<usize> {
223 let prefix_hash = hash_token_prefix(tokens, tokens.len(), adapter_id);
225 self.snapshot_index.lock().insert_tail_sibling(
226 prefix_hash,
227 snapshot_id,
228 session_hash,
229 tokens.len(),
230 )
231 }
232
233 fn insert_intermediate_snapshot(
234 &self,
235 tokens: &[u32],
236 _block_table: &[u32],
237 _disk_block_ids: &[u32],
238 _block_size: usize,
239 snapshot_id: usize,
240 session_hash: u64,
241 _matched_tokens: usize,
242 adapter_id: u64,
243 ) -> Option<usize> {
244 let prefix_hash = hash_token_prefix(tokens, tokens.len(), adapter_id);
247 let mut idx = self.snapshot_index.lock();
248 idx.insert(prefix_hash, snapshot_id, session_hash, tokens.len())
249 }
250
251 fn release(&self, tokens: &[u32], block_size: usize, adapter_id: u64) {
252 self.inner
253 .lock()
254 .dec_refs(tokens, block_size, tokens.len(), adapter_id);
255 }
256
257 fn release_matched(
258 &self,
259 tokens: &[u32],
260 block_size: usize,
261 matched_tokens: usize,
262 adapter_id: u64,
263 ) {
264 self.inner
265 .lock()
266 .dec_refs(tokens, block_size, matched_tokens, adapter_id);
267 }
268
269 fn evict(&self, num_blocks: usize) -> EvictedBlocks {
270 let (physical, disk) = self.inner.lock().evict(num_blocks);
271 let disk_block_ids: Vec<u32> = disk.into_iter().filter(|&id| id != u32::MAX).collect();
274 EvictedBlocks {
275 physical,
276 disk_block_ids,
277 }
278 }
279
280 fn evict_snapshot_lru(&self) -> Option<usize> {
281 self.snapshot_index.lock().evict_lru()
282 }
283
284 fn evict_snapshot_to_tier(
285 &self,
286 min_tokens: usize,
287 ) -> Option<metrale_telemetry::prefix_cache::TierEvict> {
288 self.snapshot_index.lock().evict_to_tier(min_tokens)
289 }
290
291 fn promote_snapshot(&self, key: u64, new_slot: usize) -> bool {
292 self.snapshot_index.lock().promote(key, new_slot)
293 }
294
295 fn forget_snapshot_tier_key(&self, key: u64) -> bool {
296 self.snapshot_index.lock().forget_tiered(key)
297 }
298
299 fn snapshot_count(&self) -> usize {
300 self.snapshot_index.lock().len()
301 }
302
303 fn stats(&self) -> (usize, usize) {
304 let inner = self.inner.lock();
305 let entries = inner.num_entries();
306 (entries, entries)
307 }
308}