1use std::hash::Hash;
5
6use reifydb_codec::row::operator::state::{OperatorState, decode};
7use reifydb_core::{
8 key::operator::state::{IntoGroupStateKey, row_number_counter_key},
9 metrics::heap::HeapSize,
10 state::timer::StateStore,
11};
12use reifydb_value::{Result, value::row_number::RowNumber};
13
14pub fn mint_row_numbers<S>(store: &mut S, count: u64) -> Result<RowNumber>
15where
16 S: StateStore + ?Sized,
17{
18 let key = row_number_counter_key();
19 let seed = match store.state_get(&key)? {
20 Some(row) => decode::<u64>(&row)?,
21 None => 1,
22 };
23 store.state_set(&key, (seed + count).encode_state()?)?;
24 Ok(RowNumber(seed))
25}
26
27pub fn get<K, V>(store: &mut dyn StateStore, key: &K) -> Result<Option<V>>
28where
29 K: Hash + Eq + Clone + HeapSize,
30 for<'a> &'a K: IntoGroupStateKey,
31 V: Clone + OperatorState + HeapSize,
32{
33 let encoded_key = key.into_group_state_key();
34 match store.state_get(&encoded_key)? {
35 Some(bytes) => Ok(Some(decode::<V>(&bytes)?)),
36 None => Ok(None),
37 }
38}
39
40pub fn get_classified<K, V>(store: &mut dyn StateStore, key: &K) -> Result<Option<V>>
41where
42 K: Hash + Eq + Clone + HeapSize,
43 for<'a> &'a K: IntoGroupStateKey,
44 V: Clone + OperatorState + HeapSize,
45{
46 let encoded_key = key.into_group_state_key();
47 let (value, pre) = match store.state_get(&encoded_key)? {
48 Some(bytes) => (Some(decode::<V>(&bytes)?), Some(bytes.byte_size())),
49 None => (None, None),
50 };
51 store.state_classify(&encoded_key, pre);
52 Ok(value)
53}
54
55pub fn set<K, V>(store: &mut dyn StateStore, key: &K, value: &V) -> Result<()>
56where
57 K: Hash + Eq + Clone + HeapSize,
58 for<'a> &'a K: IntoGroupStateKey,
59 V: Clone + OperatorState + HeapSize,
60{
61 let encoded_key = key.into_group_state_key();
62 let payload = value.encode_state()?;
63 store.state_set(&encoded_key, payload)
64}
65
66pub fn put<K, V>(store: &mut dyn StateStore, key: &K, value: V) -> Result<()>
67where
68 K: Hash + Eq + Clone + HeapSize,
69 for<'a> &'a K: IntoGroupStateKey,
70 V: Clone + OperatorState + HeapSize,
71{
72 set(store, key, &value)
73}
74
75pub fn modify<K, V, R>(store: &mut dyn StateStore, key: &K, f: impl FnOnce(&mut V) -> R) -> Result<R>
76where
77 K: Hash + Eq + Clone + HeapSize,
78 for<'a> &'a K: IntoGroupStateKey,
79 V: Clone + Default + OperatorState + HeapSize,
80{
81 let encoded_key = key.into_group_state_key();
82 let (mut value, pre) = match store.state_get(&encoded_key)? {
83 Some(bytes) => (decode::<V>(&bytes)?, Some(bytes.byte_size())),
84 None => (V::default(), None),
85 };
86 store.state_classify(&encoded_key, pre);
87 let result = f(&mut value);
88 store.state_set(&encoded_key, value.encode_state()?)?;
89 Ok(result)
90}
91
92pub fn remove<K>(store: &mut dyn StateStore, key: &K) -> Result<()>
93where
94 K: Hash + Eq + Clone + HeapSize,
95 for<'a> &'a K: IntoGroupStateKey,
96{
97 let encoded_key = key.into_group_state_key();
98 store.state_remove(&encoded_key)
99}
100
101pub fn get_or_default<K, V>(store: &mut dyn StateStore, key: &K) -> Result<V>
102where
103 K: Hash + Eq + Clone + HeapSize,
104 for<'a> &'a K: IntoGroupStateKey,
105 V: Clone + Default + OperatorState + HeapSize,
106{
107 match get(store, key)? {
108 Some(value) => Ok(value),
109 None => Ok(V::default()),
110 }
111}
112
113pub fn update<K, V, U>(store: &mut dyn StateStore, key: &K, updater: U) -> Result<V>
114where
115 K: Hash + Eq + Clone + HeapSize,
116 for<'a> &'a K: IntoGroupStateKey,
117 V: Clone + Default + OperatorState + HeapSize,
118 U: FnOnce(&mut V) -> Result<()>,
119{
120 let encoded_key = key.into_group_state_key();
121 let (mut value, pre) = match store.state_get(&encoded_key)? {
122 Some(bytes) => (decode::<V>(&bytes)?, Some(bytes.byte_size())),
123 None => (V::default(), None),
124 };
125 store.state_classify(&encoded_key, pre);
126 updater(&mut value)?;
127 store.state_set(&encoded_key, value.encode_state()?)?;
128 Ok(value)
129}
130
131#[cfg(test)]
132mod tests {
133 use std::{collections::HashMap, ops::Bound};
134
135 use reifydb_codec::{
136 key::encoded::{EncodedKey, EncodedKeyRange},
137 row::pod::EncodedPodRow,
138 };
139 use reifydb_core::{
140 key::operator::state::{GroupId, GroupStateKey, custom_not_cached_key},
141 state::timer::{TimerKind, TimerStore},
142 };
143 use reifydb_macro::operator_state;
144 use reifydb_value::{
145 byte_size::ByteSize,
146 value::{datetime::DateTime, row_number::RowNumber},
147 };
148
149 use super::*;
150
151 #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
154 struct Key(String);
155
156 impl Key {
157 fn new(key: impl Into<String>) -> Self {
158 Self(key.into())
159 }
160 }
161
162 impl HeapSize for Key {
163 fn heap_size(&self) -> usize {
164 self.0.capacity()
165 }
166 }
167
168 impl IntoGroupStateKey for &Key {
169 fn into_group_state_key(self) -> GroupStateKey {
170 custom_not_cached_key(self.0.as_bytes())
171 .expect("a custom state key must be at most sixteen bytes")
172 }
173 }
174
175 #[operator_state]
176 #[derive(Debug, Clone, Copy, Default, PartialEq)]
177 struct Cell {
178 value: i32,
179 }
180
181 impl HeapSize for Cell {
182 fn heap_size(&self) -> usize {
183 0
184 }
185 }
186
187 fn cell(value: i32) -> Cell {
188 Cell {
189 value,
190 }
191 }
192
193 #[derive(Default)]
194 struct MockStore {
195 data: HashMap<Vec<u8>, EncodedPodRow>,
196 removes: usize,
197 sets: usize,
198 gets: usize,
199 classifications: Vec<(Vec<u8>, Option<ByteSize>)>,
200 now: DateTime,
202 }
203
204 impl TimerStore for MockStore {
205 fn arm_timer(&mut self, _due: DateTime, _kind: TimerKind, _key: &EncodedKey) -> Result<()> {
206 unreachable!("the window engine never arms timers; only the shell above it does")
207 }
208
209 fn disarm_timer(&mut self, _due: DateTime, _kind: TimerKind, _key: &EncodedKey) -> Result<()> {
210 unreachable!("the window engine never disarms timers; only the shell above it does")
211 }
212
213 fn flow_watermark(&mut self) -> Result<Option<DateTime>> {
214 Ok(None)
215 }
216 }
217
218 impl StateStore for MockStore {
219 fn state_get(&mut self, key: &GroupStateKey) -> Result<Option<EncodedPodRow>> {
220 self.gets += 1;
221 Ok(self.data.get(key.as_slice()).cloned())
222 }
223
224 fn state_get_many_visit(
225 &mut self,
226 keys: &[GroupStateKey],
227 visit: &mut dyn FnMut(GroupStateKey, EncodedPodRow) -> Result<()>,
228 ) -> Result<()> {
229 for key in keys {
230 if let Some(b) = self.data.get(key.as_slice()) {
231 visit(key.clone(), b.clone())?;
232 }
233 }
234 Ok(())
235 }
236
237 fn state_classify(&mut self, key: &GroupStateKey, pre: Option<ByteSize>) {
238 self.classifications.push((key.as_slice().to_vec(), pre));
239 }
240
241 fn state_set(&mut self, key: &GroupStateKey, payload: EncodedPodRow) -> Result<()> {
242 self.sets += 1;
243 self.data.insert(key.as_slice().to_vec(), payload);
244 Ok(())
245 }
246
247 fn state_remove(&mut self, key: &GroupStateKey) -> Result<()> {
248 self.removes += 1;
249 self.data.remove(key.as_slice());
250 Ok(())
251 }
252
253 fn state_page_inner(
254 &mut self,
255 range: EncodedKeyRange,
256 limit: Option<usize>,
257 ) -> Result<Vec<(GroupStateKey, EncodedPodRow)>> {
258 let after_start = |k: &[u8]| match &range.start {
259 Bound::Included(s) => k >= s.as_bytes(),
260 Bound::Excluded(s) => k > s.as_bytes(),
261 Bound::Unbounded => true,
262 };
263 let before_end = |k: &[u8]| match &range.end {
264 Bound::Included(e) => k <= e.as_bytes(),
265 Bound::Excluded(e) => k < e.as_bytes(),
266 Bound::Unbounded => true,
267 };
268 let mut matched: Vec<(Vec<u8>, EncodedPodRow)> = self
269 .data
270 .iter()
271 .filter(|(k, _)| after_start(k) && before_end(k))
272 .map(|(k, v)| (k.clone(), v.clone()))
273 .collect();
274 matched.sort_by(|a, b| a.0.cmp(&b.0));
275 if let Some(limit) = limit {
276 matched.truncate(limit);
277 }
278 Ok(matched
279 .into_iter()
280 .map(|(k, b)| {
281 let k = GroupStateKey::from_framed(EncodedKey::new(k))
282 .expect("fake store holds an unframed state key");
283 (k, b)
284 })
285 .collect())
286 }
287
288 fn get_or_create_row_numbers(
289 &mut self,
290 _group: GroupId,
291 keys: &[EncodedKey],
292 ) -> Result<Vec<(RowNumber, bool)>> {
293 Ok(keys.iter().enumerate().map(|(i, _)| (RowNumber(i as u64 + 1), true)).collect())
294 }
295
296 fn get_or_create_row_numbers_for_groups(
297 &mut self,
298 groups: &[GroupId],
299 ) -> Result<Vec<(RowNumber, bool)>> {
300 Ok(groups.iter().enumerate().map(|(i, _)| (RowNumber(i as u64 + 1), true)).collect())
301 }
302
303 fn remove_row_number(&mut self, _group: GroupId, _key: &EncodedKey) -> Result<()> {
304 Ok(())
305 }
306
307 fn remove_row_number_for_group(&mut self, _group: GroupId) -> Result<()> {
308 Ok(())
309 }
310
311 fn written_at(&self) -> DateTime {
312 self.now
313 }
314 }
315
316 #[test]
317 fn set_reaches_the_store_without_waiting_for_flush() {
318 let mut store = MockStore::default();
320
321 set(&mut store, &Key::new("a"), &cell(7)).unwrap();
322
323 assert_eq!(store.sets, 1, "set must issue exactly one state_set");
324 assert!(!store.data.is_empty(), "the value must be in the store before any flush");
325 assert_eq!(get::<_, Cell>(&mut store, &Key::new("a")).unwrap(), Some(cell(7)));
326 }
327
328 #[test]
329 fn get_reads_through_to_the_store_every_time() {
330 let mut store = MockStore::default();
332 set(&mut store, &Key::new("a"), &cell(1)).unwrap();
333
334 get::<_, Cell>(&mut store, &Key::new("a")).unwrap();
335 get::<_, Cell>(&mut store, &Key::new("a")).unwrap();
336
337 assert_eq!(store.gets, 2, "each get must be served by the store, never from residency");
338 }
339
340 #[test]
341 fn a_miss_consults_the_store_rather_than_proving_absence() {
342 let mut store = MockStore::default();
344
345 assert_eq!(get::<_, Cell>(&mut store, &Key::new("absent")).unwrap(), None);
346 assert_eq!(store.gets, 1, "the miss must have consulted the store");
347 }
348
349 #[test]
350 fn a_read_leaves_the_row_in_the_store_for_the_next_reader() {
351 let mut store = MockStore::default();
353 set(&mut store, &Key::new("a"), &cell(3)).unwrap();
354
355 assert_eq!(get::<_, Cell>(&mut store, &Key::new("a")).unwrap(), Some(cell(3)));
356 assert_eq!(store.removes, 0, "a read must not issue a state_remove");
357 assert_eq!(get::<_, Cell>(&mut store, &Key::new("a")).unwrap(), Some(cell(3)));
358 }
359
360 #[test]
361 fn read_then_persist_round_trips_a_mutation() {
362 let mut store = MockStore::default();
364 set(&mut store, &Key::new("a"), &cell(1)).unwrap();
365
366 let mut value = get::<_, Cell>(&mut store, &Key::new("a")).unwrap().unwrap();
367 value.value += 41;
368 set(&mut store, &Key::new("a"), &value).unwrap();
369
370 assert_eq!(get::<_, Cell>(&mut store, &Key::new("a")).unwrap(), Some(cell(42)));
371 }
372
373 #[test]
374 fn remove_issues_a_state_remove_and_the_key_reads_back_absent() {
375 let mut store = MockStore::default();
376 set(&mut store, &Key::new("a"), &cell(5)).unwrap();
377
378 remove(&mut store, &Key::new("a")).unwrap();
379
380 assert_eq!(store.removes, 1);
381 assert_eq!(get::<_, Cell>(&mut store, &Key::new("a")).unwrap(), None);
382 }
383
384 #[test]
385 fn modify_mutates_the_stored_value_and_persists_it() {
386 let mut store = MockStore::default();
388 set(&mut store, &Key::new("a"), &cell(1)).unwrap();
389
390 let returned = modify(&mut store, &Key::new("a"), |value: &mut Cell| {
391 value.value += 6;
392 value.value
393 })
394 .unwrap();
395
396 assert_eq!(returned, 7);
397 assert_eq!(get::<_, Cell>(&mut store, &Key::new("a")).unwrap(), Some(cell(7)));
398 }
399
400 #[test]
401 fn modify_on_a_miss_starts_from_default_and_persists() {
402 let mut store = MockStore::default();
404
405 modify(&mut store, &Key::new("fresh"), |value: &mut Cell| {
406 value.value = 5;
407 })
408 .unwrap();
409
410 assert_eq!(get::<_, Cell>(&mut store, &Key::new("fresh")).unwrap(), Some(cell(5)));
411 }
412
413 #[test]
414 fn get_or_default_returns_the_default_only_for_an_absent_key() {
415 let mut store = MockStore::default();
416 set(&mut store, &Key::new("present"), &cell(8)).unwrap();
417
418 assert_eq!(get_or_default::<_, Cell>(&mut store, &Key::new("present")).unwrap(), cell(8));
419 assert_eq!(get_or_default::<_, Cell>(&mut store, &Key::new("absent")).unwrap(), Cell::default());
420 }
421
422 #[test]
423 fn update_persists_the_mutation_and_returns_the_new_value() {
424 let mut store = MockStore::default();
426
427 let returned = update(&mut store, &Key::new("a"), |value: &mut Cell| {
428 value.value += 4;
429 Ok(())
430 })
431 .unwrap();
432
433 assert_eq!(returned, cell(4));
434 assert_eq!(get::<_, Cell>(&mut store, &Key::new("a")).unwrap(), Some(cell(4)));
435 }
436
437 #[test]
438 fn modify_hands_the_write_the_size_it_already_read() {
439 let mut store = MockStore::default();
443 set(&mut store, &Key::new("a"), &cell(123_456_789)).unwrap();
444 let durable = store.data.values().next().expect("the seed write is in the store").bytes().len();
445 assert!(
446 durable > 1,
447 "the seed must encode to more than one byte or the size assertion cannot discriminate"
448 );
449 store.classifications.clear();
450
451 modify::<_, Cell, _>(&mut store, &Key::new("a"), |c| c.value += 1).unwrap();
452
453 assert_eq!(store.gets, 1, "modify must classify from its own read, never pay a second one");
454 assert_eq!(
455 store.classifications.len(),
456 1,
457 "the write must be handed exactly one pre-image, or it falls back to reading the key again"
458 );
459 assert_eq!(
460 store.classifications[0].1,
461 Some(ByteSize::from_bytes(durable as u64)),
462 "the size handed down must be what a durable read of the row would measure"
463 );
464 }
465
466 #[test]
467 fn modify_of_a_key_that_is_not_there_hands_down_an_absence() {
468 let mut store = MockStore::default();
470
471 modify::<_, Cell, _>(&mut store, &Key::new("missing"), |c| c.value = 3).unwrap();
472
473 assert_eq!(
474 store.classifications,
475 vec![(Key::new("missing").into_group_state_key().as_slice().to_vec(), None)],
476 "a key the read did not find must be handed down as absent, not left for the write to discover"
477 );
478 }
479
480 #[test]
481 fn update_hands_the_write_the_size_it_already_read() {
482 let mut store = MockStore::default();
485 set(&mut store, &Key::new("a"), &cell(123_456_789)).unwrap();
486 let durable = store.data.values().next().expect("the seed write is in the store").bytes().len();
487 assert!(
488 durable > 1,
489 "the seed must encode to more than one byte or the size assertion cannot discriminate"
490 );
491 store.classifications.clear();
492
493 update::<_, Cell, _>(&mut store, &Key::new("a"), |c| {
494 c.value += 5;
495 Ok(())
496 })
497 .unwrap();
498
499 assert_eq!(store.gets, 1, "update must classify from its own read, never pay a second one");
500 assert_eq!(
501 store.classifications[0].1,
502 Some(ByteSize::from_bytes(durable as u64)),
503 "the size handed down must be what a durable read of the row would measure"
504 );
505 }
506
507 #[test]
508 fn update_of_a_key_that_is_not_there_hands_down_an_absence() {
509 let mut store = MockStore::default();
511
512 update::<_, Cell, _>(&mut store, &Key::new("missing"), |c: &mut Cell| {
513 c.value = 9;
514 Ok(())
515 })
516 .unwrap();
517
518 assert_eq!(
519 store.classifications,
520 vec![(Key::new("missing").into_group_state_key().as_slice().to_vec(), None)],
521 "a key the read did not find must be handed down as absent, not left for the write to discover"
522 );
523 }
524
525 #[test]
526 fn get_classified_hands_the_write_the_size_it_already_read() {
527 let mut store = MockStore::default();
530 set(&mut store, &Key::new("a"), &cell(123_456_789)).unwrap();
531 let durable = store.data.values().next().expect("the seed write is in the store").bytes().len();
532 assert!(
533 durable > 1,
534 "the seed must encode to more than one byte or the size assertion cannot discriminate"
535 );
536 store.classifications.clear();
537 store.gets = 0;
538
539 let value: Option<Cell> = get_classified(&mut store, &Key::new("a")).unwrap();
540
541 assert_eq!(value, Some(cell(123_456_789)), "classifying must not disturb the value it returns");
542 assert_eq!(store.gets, 1, "the classification must ride the read it already paid for");
543 assert_eq!(
544 store.classifications[0].1,
545 Some(ByteSize::from_bytes(durable as u64)),
546 "the size handed down must be what a durable read of the row would measure"
547 );
548 }
549
550 #[test]
551 fn get_classified_of_a_key_that_is_not_there_hands_down_an_absence() {
552 let mut store = MockStore::default();
555
556 let value: Option<Cell> = get_classified(&mut store, &Key::new("missing")).unwrap();
557
558 assert_eq!(value, None, "a missing key still reads as missing");
559 assert_eq!(
560 store.classifications,
561 vec![(Key::new("missing").into_group_state_key().as_slice().to_vec(), None)],
562 "a key the read did not find must be handed down as absent, not left for the write to discover"
563 );
564 }
565}