1use std::{hash::Hash, marker::PhantomData};
5
6use reifydb_codec::row::operator::{OperatorState, decode};
7use reifydb_value::Result;
8
9use crate::{key::operator_state::IntoGroupStateKey, metrics::heap::HeapSize, state::store::StateStore};
10
11pub struct StateCache<K, V> {
12 marker: PhantomData<fn(K) -> V>,
13}
14
15impl<K, V> Default for StateCache<K, V> {
16 fn default() -> Self {
17 Self::new()
18 }
19}
20
21impl<K, V> StateCache<K, V> {
22 pub fn new() -> Self {
23 Self {
24 marker: PhantomData,
25 }
26 }
27}
28
29impl<K, V> StateCache<K, V>
30where
31 K: Hash + Eq + Clone + HeapSize,
32 for<'a> &'a K: IntoGroupStateKey,
33 V: Clone + OperatorState + HeapSize,
34{
35 pub fn get(&mut self, store: &mut dyn StateStore, key: &K) -> Result<Option<V>> {
36 let encoded_key = key.into_group_state_key();
37 match store.state_get(&encoded_key)? {
38 Some(bytes) => Ok(Some(decode::<V>(&bytes)?)),
39 None => Ok(None),
40 }
41 }
42
43 pub fn set(&mut self, store: &mut dyn StateStore, key: &K, value: &V) -> Result<()> {
44 let encoded_key = key.into_group_state_key();
45 let payload = value.encode_state(store.written_at())?;
46 store.state_set(&encoded_key, payload)
47 }
48
49 pub fn put(&mut self, store: &mut dyn StateStore, key: &K, value: V) -> Result<()> {
50 self.set(store, key, &value)
51 }
52
53 pub fn modify<R>(&mut self, store: &mut dyn StateStore, key: &K, f: impl FnOnce(&mut V) -> R) -> Result<R>
54 where
55 V: Default,
56 {
57 let encoded_key = key.into_group_state_key();
58 let now = store.written_at();
59 let mut value = match store.state_get(&encoded_key)? {
60 Some(bytes) => decode::<V>(&bytes)?,
61 None => V::default(),
62 };
63 let result = f(&mut value);
64 store.state_set(&encoded_key, value.encode_state(now)?)?;
65 Ok(result)
66 }
67
68 pub fn remove(&mut self, store: &mut dyn StateStore, key: &K) -> Result<()> {
69 let encoded_key = key.into_group_state_key();
70 store.state_remove(&encoded_key)
71 }
72}
73
74impl<K, V> StateCache<K, V>
75where
76 K: Hash + Eq + Clone + HeapSize,
77 for<'a> &'a K: IntoGroupStateKey,
78 V: Clone + Default + OperatorState + HeapSize,
79{
80 pub fn get_or_default(&mut self, store: &mut dyn StateStore, key: &K) -> Result<V> {
81 match self.get(store, key)? {
82 Some(value) => Ok(value),
83 None => Ok(V::default()),
84 }
85 }
86
87 pub fn update<U>(&mut self, store: &mut dyn StateStore, key: &K, updater: U) -> Result<V>
88 where
89 U: FnOnce(&mut V) -> Result<()>,
90 {
91 let mut value = self.get_or_default(store, key)?;
92 updater(&mut value)?;
93 self.set(store, key, &value)?;
94 Ok(value)
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use std::{collections::HashMap, ops::Bound};
101
102 use reifydb_codec::{
103 key::encoded::{EncodedKey, EncodedKeyRange},
104 row::operator::EncodedOperatorRow,
105 };
106 use reifydb_macro::operator_state;
107 use reifydb_value::value::{datetime::DateTime, row_number::RowNumber};
108
109 use super::*;
110 use crate::{
111 key::operator_state::{GroupId, GroupStateKey, Keyspace},
112 state::store::{TimerKind, TimerStore},
113 };
114
115 #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
118 struct Key(String);
119
120 impl Key {
121 fn new(key: impl Into<String>) -> Self {
122 Self(key.into())
123 }
124 }
125
126 impl HeapSize for Key {
127 fn heap_size(&self) -> usize {
128 self.0.capacity()
129 }
130 }
131
132 impl IntoGroupStateKey for &Key {
133 fn into_group_state_key(self) -> GroupStateKey {
134 GroupStateKey::root(Keyspace::CUSTOM, self.0.as_bytes())
135 }
136 }
137
138 #[operator_state]
139 #[derive(Debug, Clone, Copy, Default, PartialEq)]
140 struct Cell {
141 value: i32,
142 }
143
144 impl HeapSize for Cell {
145 fn heap_size(&self) -> usize {
146 0
147 }
148 }
149
150 fn cell(value: i32) -> Cell {
151 Cell {
152 value,
153 }
154 }
155
156 #[derive(Default)]
157 struct MockStore {
158 data: HashMap<Vec<u8>, EncodedOperatorRow>,
159 groups: HashMap<Vec<u8>, GroupId>,
160 removes: usize,
161 sets: usize,
162 gets: usize,
163 now: DateTime,
165 }
166
167 impl TimerStore for MockStore {
168 fn arm_timer(&mut self, _due: DateTime, _kind: TimerKind, _key: &EncodedKey) -> Result<()> {
169 unreachable!("the window engine never arms timers; only the shell above it does")
170 }
171
172 fn disarm_timer(&mut self, _due: DateTime, _kind: TimerKind, _key: &EncodedKey) -> Result<()> {
173 unreachable!("the window engine never disarms timers; only the shell above it does")
174 }
175
176 fn flow_watermark(&mut self) -> Result<Option<DateTime>> {
177 Ok(None)
178 }
179 }
180
181 impl StateStore for MockStore {
182 fn intern_groups(&mut self, groups: &[EncodedKey]) -> Result<Vec<(GroupId, bool)>> {
183 let mut interned = Vec::with_capacity(groups.len());
184 for group in groups {
185 let bytes = group.as_bytes().to_vec();
186 match self.groups.get(&bytes) {
187 Some(id) => interned.push((*id, false)),
188 None => {
189 let next = GroupId(self.groups.len() as u64 + GroupId::FIRST.0);
190 self.groups.insert(bytes, next);
191 interned.push((next, true));
192 }
193 }
194 }
195 Ok(interned)
196 }
197
198 fn lookup_groups(&mut self, groups: &[EncodedKey]) -> Result<Vec<Option<GroupId>>> {
199 Ok(groups.iter().map(|group| self.groups.get(group.as_bytes()).copied()).collect())
200 }
201
202 fn state_get(&mut self, key: &GroupStateKey) -> Result<Option<EncodedOperatorRow>> {
203 self.gets += 1;
204 Ok(self.data.get(key.as_slice()).cloned())
205 }
206
207 fn state_get_many_visit(
208 &mut self,
209 keys: &[GroupStateKey],
210 visit: &mut dyn FnMut(GroupStateKey, EncodedOperatorRow) -> Result<()>,
211 ) -> Result<()> {
212 for key in keys {
213 if let Some(b) = self.data.get(key.as_slice()) {
214 visit(key.clone(), b.clone())?;
215 }
216 }
217 Ok(())
218 }
219
220 fn state_set(&mut self, key: &GroupStateKey, payload: EncodedOperatorRow) -> Result<()> {
221 self.sets += 1;
222 self.data.insert(key.as_slice().to_vec(), payload);
223 Ok(())
224 }
225
226 fn state_remove(&mut self, key: &GroupStateKey) -> Result<()> {
227 self.removes += 1;
228 self.data.remove(key.as_slice());
229 Ok(())
230 }
231
232 fn state_range_visit(
233 &mut self,
234 range: EncodedKeyRange,
235 limit: Option<usize>,
236 visit: &mut dyn FnMut(GroupStateKey, EncodedOperatorRow) -> Result<()>,
237 ) -> Result<()> {
238 let after_start = |k: &[u8]| match &range.start {
239 Bound::Included(s) => k >= s.as_bytes(),
240 Bound::Excluded(s) => k > s.as_bytes(),
241 Bound::Unbounded => true,
242 };
243 let before_end = |k: &[u8]| match &range.end {
244 Bound::Included(e) => k <= e.as_bytes(),
245 Bound::Excluded(e) => k < e.as_bytes(),
246 Bound::Unbounded => true,
247 };
248 let mut matched: Vec<(Vec<u8>, EncodedOperatorRow)> = self
249 .data
250 .iter()
251 .filter(|(k, _)| after_start(k) && before_end(k))
252 .map(|(k, v)| (k.clone(), v.clone()))
253 .collect();
254 matched.sort_by(|a, b| a.0.cmp(&b.0));
255 if let Some(limit) = limit {
256 matched.truncate(limit);
257 }
258 for (k, b) in matched {
259 let k = GroupStateKey::from_framed(EncodedKey::new(k))
260 .expect("fake store holds an unframed state key");
261 visit(k, b)?;
262 }
263 Ok(())
264 }
265
266 fn get_or_create_row_numbers(
267 &mut self,
268 _group: GroupId,
269 keys: &[EncodedKey],
270 ) -> Result<Vec<(RowNumber, bool)>> {
271 Ok(keys.iter().enumerate().map(|(i, _)| (RowNumber(i as u64 + 1), true)).collect())
272 }
273
274 fn get_or_create_row_numbers_for_pairs(
275 &mut self,
276 pairs: &[(GroupId, EncodedKey)],
277 ) -> Result<Vec<(RowNumber, bool)>> {
278 Ok(pairs.iter().enumerate().map(|(i, _)| (RowNumber(i as u64 + 1), true)).collect())
279 }
280
281 fn remove_row_number(&mut self, _group: GroupId, _key: &EncodedKey) -> Result<()> {
282 Ok(())
283 }
284
285 fn written_at(&self) -> DateTime {
286 self.now
287 }
288 }
289
290 #[test]
291 fn set_reaches_the_store_without_waiting_for_flush() {
292 let mut store = MockStore::default();
294 let mut cache: StateCache<Key, Cell> = StateCache::new();
295
296 cache.set(&mut store, &Key::new("a"), &cell(7)).unwrap();
297
298 assert_eq!(store.sets, 1, "set must issue exactly one state_set");
299 assert!(!store.data.is_empty(), "the value must be in the store before any flush");
300 assert_eq!(cache.get(&mut store, &Key::new("a")).unwrap(), Some(cell(7)));
301 }
302
303 #[test]
304 fn get_reads_through_to_the_store_every_time() {
305 let mut store = MockStore::default();
307 let mut cache: StateCache<Key, Cell> = StateCache::new();
308 cache.set(&mut store, &Key::new("a"), &cell(1)).unwrap();
309
310 cache.get(&mut store, &Key::new("a")).unwrap();
311 cache.get(&mut store, &Key::new("a")).unwrap();
312
313 assert_eq!(store.gets, 2, "each get must be served by the store, never from residency");
314 }
315
316 #[test]
317 fn a_miss_consults_the_store_rather_than_proving_absence() {
318 let mut store = MockStore::default();
320 let mut cache: StateCache<Key, Cell> = StateCache::new();
321
322 assert_eq!(cache.get(&mut store, &Key::new("absent")).unwrap(), None);
323 assert_eq!(store.gets, 1, "the miss must have consulted the store");
324 }
325
326 #[test]
327 fn a_read_leaves_the_row_in_the_store_for_the_next_reader() {
328 let mut store = MockStore::default();
330 let mut cache: StateCache<Key, Cell> = StateCache::new();
331 cache.set(&mut store, &Key::new("a"), &cell(3)).unwrap();
332
333 assert_eq!(cache.get(&mut store, &Key::new("a")).unwrap(), Some(cell(3)));
334 assert_eq!(store.removes, 0, "a read must not issue a state_remove");
335 assert_eq!(cache.get(&mut store, &Key::new("a")).unwrap(), Some(cell(3)));
336 }
337
338 #[test]
339 fn read_then_persist_round_trips_a_mutation() {
340 let mut store = MockStore::default();
342 let mut cache: StateCache<Key, Cell> = StateCache::new();
343 cache.set(&mut store, &Key::new("a"), &cell(1)).unwrap();
344
345 let mut value = cache.get(&mut store, &Key::new("a")).unwrap().unwrap();
346 value.value += 41;
347 cache.set(&mut store, &Key::new("a"), &value).unwrap();
348
349 assert_eq!(cache.get(&mut store, &Key::new("a")).unwrap(), Some(cell(42)));
350 }
351
352 #[test]
353 fn remove_issues_a_state_remove_and_the_key_reads_back_absent() {
354 let mut store = MockStore::default();
355 let mut cache: StateCache<Key, Cell> = StateCache::new();
356 cache.set(&mut store, &Key::new("a"), &cell(5)).unwrap();
357
358 cache.remove(&mut store, &Key::new("a")).unwrap();
359
360 assert_eq!(store.removes, 1);
361 assert_eq!(cache.get(&mut store, &Key::new("a")).unwrap(), None);
362 }
363
364 #[test]
365 fn modify_mutates_the_stored_value_and_persists_it() {
366 let mut store = MockStore::default();
368 let mut cache: StateCache<Key, Cell> = StateCache::new();
369 cache.set(&mut store, &Key::new("a"), &cell(1)).unwrap();
370
371 let returned = cache
372 .modify(&mut store, &Key::new("a"), |value| {
373 value.value += 6;
374 value.value
375 })
376 .unwrap();
377
378 assert_eq!(returned, 7);
379 assert_eq!(cache.get(&mut store, &Key::new("a")).unwrap(), Some(cell(7)));
380 }
381
382 #[test]
383 fn modify_persists_the_row_with_a_refreshed_time() {
384 let mut store = MockStore::default();
386 let mut cache: StateCache<Key, Cell> = StateCache::new();
387 cache.set(&mut store, &Key::new("a"), &cell(1)).unwrap();
388 store.now = DateTime::from_nanos(4_000);
389
390 cache.modify(&mut store, &Key::new("a"), |value| {
391 value.value = 3;
392 })
393 .unwrap();
394
395 let key = (&Key::new("a")).into_group_state_key();
396 let stored = store.data.get(key.as_slice()).expect("the row was written");
397 assert_eq!(stored.time(), DateTime::from_nanos(4_000));
398 }
399
400 #[test]
401 fn modify_on_a_miss_starts_from_default_and_persists() {
402 let mut store = MockStore::default();
404 let mut cache: StateCache<Key, Cell> = StateCache::new();
405
406 cache.modify(&mut store, &Key::new("fresh"), |value| {
407 value.value = 5;
408 })
409 .unwrap();
410
411 assert_eq!(cache.get(&mut store, &Key::new("fresh")).unwrap(), Some(cell(5)));
412 }
413
414 #[test]
415 fn get_or_default_returns_the_default_only_for_an_absent_key() {
416 let mut store = MockStore::default();
417 let mut cache: StateCache<Key, Cell> = StateCache::new();
418 cache.set(&mut store, &Key::new("present"), &cell(8)).unwrap();
419
420 assert_eq!(cache.get_or_default(&mut store, &Key::new("present")).unwrap(), cell(8));
421 assert_eq!(cache.get_or_default(&mut store, &Key::new("absent")).unwrap(), Cell::default());
422 }
423
424 #[test]
425 fn update_persists_the_mutation_and_returns_the_new_value() {
426 let mut store = MockStore::default();
428 let mut cache: StateCache<Key, Cell> = StateCache::new();
429
430 let returned = cache
431 .update(&mut store, &Key::new("a"), |value| {
432 value.value += 4;
433 Ok(())
434 })
435 .unwrap();
436
437 assert_eq!(returned, cell(4));
438 assert_eq!(cache.get(&mut store, &Key::new("a")).unwrap(), Some(cell(4)));
439 }
440}