1use std::{collections::HashMap, hash::Hash, mem, sync::Arc};
5
6use reifydb_codec::key::encoded::{EncodedKey, IntoEncodedKey};
7use reifydb_runtime::cache::slab::SlabLru;
8use reifydb_value::Result;
9use serde::{Serialize, de::DeserializeOwned};
10
11use crate::window::store::WindowStore;
12
13#[derive(Clone, Copy, Debug)]
14pub enum StateBackend {
15 Data,
16
17 Internal,
18}
19
20pub struct StateCache<K, V> {
21 cache: SlabLru<K, Arc<V>>,
22 dirty: HashMap<K, Option<Arc<V>>>,
23 backend: StateBackend,
24}
25
26impl<K, V> StateCache<K, V>
27where
28 K: Hash + Eq + Clone,
29 for<'a> &'a K: IntoEncodedKey,
30 V: Clone + Serialize + DeserializeOwned,
31{
32 pub fn new(capacity: usize) -> Self {
33 Self::with_backend(capacity, StateBackend::Data)
34 }
35
36 pub fn new_internal(capacity: usize) -> Self {
37 Self::with_backend(capacity, StateBackend::Internal)
38 }
39
40 fn with_backend(capacity: usize, backend: StateBackend) -> Self {
41 Self {
42 cache: SlabLru::new(capacity),
43 dirty: HashMap::new(),
44 backend,
45 }
46 }
47
48 pub fn get_arc(&mut self, store: &mut impl WindowStore, key: &K) -> Result<Option<Arc<V>>> {
49 if let Some(cached) = self.cache.get(key) {
50 return Ok(Some(cached));
51 }
52
53 if let Some(slot) = self.dirty.get(key) {
54 return Ok(slot.clone());
55 }
56
57 let encoded_key = key.into_encoded_key();
58 let loaded = match self.backend {
59 StateBackend::Data => store.state_get::<V>(&encoded_key)?,
60 StateBackend::Internal => store.internal_get::<V>(&encoded_key)?,
61 };
62 match loaded {
63 Some(value) => {
64 let arc = Arc::new(value);
65 self.cache.put(key.clone(), arc.clone());
66 Ok(Some(arc))
67 }
68 None => Ok(None),
69 }
70 }
71
72 pub fn get(&mut self, store: &mut impl WindowStore, key: &K) -> Result<Option<V>> {
73 Ok(self.get_arc(store, key)?.map(|arc| (*arc).clone()))
74 }
75
76 pub fn warm(&mut self, store: &mut impl WindowStore, keys: &[K]) -> Result<()> {
77 let mut to_load: Vec<K> = Vec::new();
78 for key in keys {
79 if self.cache.contains_key(key) || self.dirty.contains_key(key) {
80 continue;
81 }
82 to_load.push(key.clone());
83 }
84 if to_load.is_empty() {
85 return Ok(());
86 }
87
88 let mut by_encoded: HashMap<Vec<u8>, K> = HashMap::with_capacity(to_load.len());
89 let mut encoded_keys: Vec<EncodedKey> = Vec::with_capacity(to_load.len());
90 for key in &to_load {
91 let encoded = key.into_encoded_key();
92 by_encoded.insert(encoded.as_bytes().to_vec(), key.clone());
93 encoded_keys.push(encoded);
94 }
95
96 let cache = &mut self.cache;
97 let mut visit = |encoded: EncodedKey, value: V| -> Result<()> {
98 if let Some(key) = by_encoded.get(encoded.as_bytes()) {
99 cache.put(key.clone(), Arc::new(value));
100 }
101 Ok(())
102 };
103 match self.backend {
104 StateBackend::Data => store.state_get_many_visit::<V>(&encoded_keys, &mut visit)?,
105 StateBackend::Internal => store.internal_get_many_visit::<V>(&encoded_keys, &mut visit)?,
106 }
107 Ok(())
108 }
109
110 pub fn set(&mut self, _store: &mut impl WindowStore, key: &K, value: &V) -> Result<()> {
111 let arc = Arc::new(value.clone());
112 self.cache.put(key.clone(), arc.clone());
113 self.dirty.insert(key.clone(), Some(arc));
114 Ok(())
115 }
116
117 pub fn put(&mut self, _store: &mut impl WindowStore, key: &K, value: V) -> Result<()> {
118 let arc = Arc::new(value);
119 self.cache.put(key.clone(), arc.clone());
120 self.dirty.insert(key.clone(), Some(arc));
121 Ok(())
122 }
123
124 pub fn put_arc(&mut self, _store: &mut impl WindowStore, key: &K, value: Arc<V>) -> Result<()> {
125 self.cache.put(key.clone(), value.clone());
126 self.dirty.insert(key.clone(), Some(value));
127 Ok(())
128 }
129
130 pub fn modify<F>(&mut self, store: &mut impl WindowStore, key: &K, f: F) -> Result<()>
131 where
132 F: FnOnce(&mut V) -> Result<()>,
133 V: Default,
134 {
135 let mut arc = self.get_arc(store, key)?.unwrap_or_else(|| Arc::new(V::default()));
136 f(Arc::make_mut(&mut arc))?;
137 self.put_arc(store, key, arc)
138 }
139
140 pub fn remove(&mut self, _store: &mut impl WindowStore, key: &K) -> Result<()> {
141 self.cache.remove(key);
142 self.dirty.insert(key.clone(), None);
143 Ok(())
144 }
145
146 pub fn flush(&mut self, store: &mut impl WindowStore) -> Result<()> {
147 let dirty = mem::take(&mut self.dirty);
148 for (key, slot) in dirty {
149 let encoded_key = (&key).into_encoded_key();
150 match (slot, self.backend) {
151 (Some(value), StateBackend::Data) => store.state_set(&encoded_key, value.as_ref())?,
152 (Some(value), StateBackend::Internal) => {
153 store.internal_set(&encoded_key, value.as_ref())?
154 }
155 (None, StateBackend::Data) => store.state_drop(&encoded_key)?,
156 (None, StateBackend::Internal) => store.internal_drop(&encoded_key)?,
157 }
158 }
159 Ok(())
160 }
161
162 pub fn clear_cache(&mut self) {
163 self.cache.clear();
164 }
165
166 pub fn invalidate(&mut self, key: &K) {
167 self.cache.remove(key);
168 }
169
170 pub fn is_cached(&self, key: &K) -> bool {
171 self.cache.contains_key(key)
172 }
173
174 pub fn len(&self) -> usize {
175 self.cache.len()
176 }
177
178 pub fn is_empty(&self) -> bool {
179 self.cache.is_empty()
180 }
181
182 pub fn capacity(&self) -> usize {
183 self.cache.capacity()
184 }
185}
186
187impl<K, V> StateCache<K, V>
188where
189 K: Hash + Eq + Clone,
190 for<'a> &'a K: IntoEncodedKey,
191 V: Clone + Default + Serialize + DeserializeOwned,
192{
193 pub fn get_or_default(&mut self, store: &mut impl WindowStore, key: &K) -> Result<V> {
194 match self.get(store, key)? {
195 Some(value) => Ok(value),
196 None => Ok(V::default()),
197 }
198 }
199
200 pub fn update<U>(&mut self, store: &mut impl WindowStore, key: &K, updater: U) -> Result<V>
201 where
202 U: FnOnce(&mut V) -> Result<()>,
203 {
204 let mut value = self.get_or_default(store, key)?;
205 updater(&mut value)?;
206 self.set(store, key, &value)?;
207 Ok(value)
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use std::{collections::HashMap, ops::Bound};
214
215 use postcard::{from_bytes, to_allocvec};
216 use reifydb_codec::key::encoded::EncodedKeyRange;
217 use reifydb_value::value::row_number::RowNumber;
218
219 use super::*;
220
221 #[derive(Default)]
222 struct MockStore {
223 data: HashMap<Vec<u8>, Vec<u8>>,
224 internal: HashMap<Vec<u8>, Vec<u8>>,
225 }
226
227 impl WindowStore for MockStore {
228 fn state_get<V: DeserializeOwned>(&mut self, key: &EncodedKey) -> Result<Option<V>> {
229 Ok(self.data.get(key.as_bytes()).map(|b| from_bytes(b).expect("decode")))
230 }
231 fn state_get_many_visit<V: DeserializeOwned>(
232 &mut self,
233 keys: &[EncodedKey],
234 visit: &mut dyn FnMut(EncodedKey, V) -> Result<()>,
235 ) -> Result<()> {
236 for key in keys {
237 if let Some(b) = self.data.get(key.as_bytes()) {
238 visit(key.clone(), from_bytes(b).expect("decode"))?;
239 }
240 }
241 Ok(())
242 }
243 fn state_set<V: Serialize>(&mut self, key: &EncodedKey, value: &V) -> Result<()> {
244 self.data.insert(key.as_bytes().to_vec(), to_allocvec(value).expect("encode"));
245 Ok(())
246 }
247 fn state_remove(&mut self, key: &EncodedKey) -> Result<()> {
248 self.data.remove(key.as_bytes());
249 Ok(())
250 }
251 fn state_drop(&mut self, key: &EncodedKey) -> Result<()> {
252 self.data.remove(key.as_bytes());
253 Ok(())
254 }
255 fn internal_get<V: DeserializeOwned>(&mut self, key: &EncodedKey) -> Result<Option<V>> {
256 Ok(self.internal.get(key.as_bytes()).map(|b| from_bytes(b).expect("decode")))
257 }
258 fn internal_get_many_visit<V: DeserializeOwned>(
259 &mut self,
260 keys: &[EncodedKey],
261 visit: &mut dyn FnMut(EncodedKey, V) -> Result<()>,
262 ) -> Result<()> {
263 for key in keys {
264 if let Some(b) = self.internal.get(key.as_bytes()) {
265 visit(key.clone(), from_bytes(b).expect("decode"))?;
266 }
267 }
268 Ok(())
269 }
270 fn internal_set<V: Serialize>(&mut self, key: &EncodedKey, value: &V) -> Result<()> {
271 self.internal.insert(key.as_bytes().to_vec(), to_allocvec(value).expect("encode"));
272 Ok(())
273 }
274 fn internal_remove(&mut self, key: &EncodedKey) -> Result<()> {
275 self.internal.remove(key.as_bytes());
276 Ok(())
277 }
278 fn internal_drop(&mut self, key: &EncodedKey) -> Result<()> {
279 self.internal.remove(key.as_bytes());
280 Ok(())
281 }
282 fn internal_range_visit<V: DeserializeOwned>(
283 &mut self,
284 range: EncodedKeyRange,
285 limit: Option<usize>,
286 visit: &mut dyn FnMut(EncodedKey, V) -> Result<()>,
287 ) -> Result<()> {
288 let after_start = |k: &[u8]| match &range.start {
289 Bound::Included(s) => k >= s.as_bytes(),
290 Bound::Excluded(s) => k > s.as_bytes(),
291 Bound::Unbounded => true,
292 };
293 let before_end = |k: &[u8]| match &range.end {
294 Bound::Included(e) => k <= e.as_bytes(),
295 Bound::Excluded(e) => k < e.as_bytes(),
296 Bound::Unbounded => true,
297 };
298 let mut matched: Vec<(Vec<u8>, Vec<u8>)> = self
299 .internal
300 .iter()
301 .filter(|(k, _)| after_start(k) && before_end(k))
302 .map(|(k, v)| (k.clone(), v.clone()))
303 .collect();
304 matched.sort_by(|a, b| a.0.cmp(&b.0));
305 if let Some(limit) = limit {
306 matched.truncate(limit);
307 }
308 for (k, b) in matched {
309 visit(EncodedKey::new(k), from_bytes(&b).expect("decode"))?;
310 }
311 Ok(())
312 }
313 fn get_or_create_row_number(&mut self, _key: &EncodedKey) -> Result<(RowNumber, bool)> {
314 Ok((RowNumber(1), true))
315 }
316 fn get_or_create_row_numbers(&mut self, keys: &[EncodedKey]) -> Result<Vec<(RowNumber, bool)>> {
317 Ok(keys.iter().enumerate().map(|(i, _)| (RowNumber(i as u64 + 1), true)).collect())
318 }
319 fn drop_row_number(&mut self, _key: &EncodedKey) -> Result<()> {
320 Ok(())
321 }
322 fn allocate_row_numbers(&mut self, _count: u64) -> Result<RowNumber> {
323 Ok(RowNumber(1))
324 }
325 fn clock_now_nanos(&self) -> u64 {
326 0
327 }
328 }
329
330 #[test]
331 fn set_then_flush_persists_to_store_and_survives_cache_clear() {
332 let mut store = MockStore::default();
333 let mut cache: StateCache<String, i32> = StateCache::new(100);
334
335 cache.set(&mut store, &"a".to_string(), &7).unwrap();
337 assert_eq!(cache.get(&mut store, &"a".to_string()).unwrap(), Some(7));
338 assert!(store.data.is_empty());
340
341 cache.flush(&mut store).unwrap();
342 assert!(!store.data.is_empty(), "flush must write dirty entries to the store");
343
344 cache.clear_cache();
346 assert_eq!(cache.get(&mut store, &"a".to_string()).unwrap(), Some(7));
347 }
348
349 #[test]
350 fn warm_bulk_loads_present_keys_and_skips_absent() {
351 let mut store = MockStore::default();
352 {
353 let mut seed: StateCache<String, i32> = StateCache::new(100);
354 seed.set(&mut store, &"a".to_string(), &1).unwrap();
355 seed.set(&mut store, &"b".to_string(), &2).unwrap();
356 seed.flush(&mut store).unwrap();
357 }
358
359 let mut cache: StateCache<String, i32> = StateCache::new(100);
360 let keys = vec!["a".to_string(), "b".to_string(), "missing".to_string()];
361 cache.warm(&mut store, &keys).unwrap();
362
363 assert!(cache.is_cached(&"a".to_string()));
364 assert!(cache.is_cached(&"b".to_string()));
365 assert!(!cache.is_cached(&"missing".to_string()));
366 }
367
368 #[test]
369 fn dirty_write_shadows_committed_value_during_warm() {
370 let mut store = MockStore::default();
371 {
372 let mut seed: StateCache<String, i32> = StateCache::new(100);
373 seed.set(&mut store, &"a".to_string(), &1).unwrap();
374 seed.flush(&mut store).unwrap();
375 }
376
377 let mut cache: StateCache<String, i32> = StateCache::new(100);
378 cache.set(&mut store, &"a".to_string(), &99).unwrap();
379 cache.warm(&mut store, &["a".to_string()]).unwrap();
380 assert_eq!(
381 cache.get(&mut store, &"a".to_string()).unwrap(),
382 Some(99),
383 "pending write must shadow store"
384 );
385 }
386
387 #[test]
388 fn internal_backend_round_trips_through_internal_store() {
389 let mut store = MockStore::default();
390 let mut cache: StateCache<String, i32> = StateCache::new_internal(100);
391 cache.set(&mut store, &"a".to_string(), &5).unwrap();
392 cache.flush(&mut store).unwrap();
393 assert!(store.data.is_empty(), "internal backend must not write to the data store");
394 assert!(!store.internal.is_empty(), "internal backend must write to the internal store");
395 cache.clear_cache();
396 assert_eq!(cache.get(&mut store, &"a".to_string()).unwrap(), Some(5));
397 }
398}