1use std::{
7 any::Any,
8 sync::{Arc, Mutex},
9};
10
11use async_trait::async_trait;
12use bytes::Bytes;
13use krabka_units::prelude::*;
14use tokio::sync::Mutex as AsyncMutex;
15
16use crate::{
17 processor::{record::RecordContext, serde::Serde},
18 store::{
19 api::StateStore,
20 byte::{ByteKeyValueStore, InMemoryBytes},
21 cache::{named::NamedCache, window::CachingWindowStore},
22 window_schema::{key_bytes_of, store_key, unwrap_value, window_start_of, wrap_value},
23 },
24};
25
26enum Backing {
32 Plain(Box<dyn ByteKeyValueStore>),
33 Cached(CachingWindowStore),
34}
35
36impl Backing {
37 async fn get(&self, key: &[u8]) -> Option<Bytes> {
41 match self {
42 Backing::Plain(b) => b.get(key).await,
43 Backing::Cached(c) => c.get(key).await,
44 }
45 }
46 async fn range(&self, lo: &[u8], hi: &[u8]) -> Vec<(Bytes, Bytes)> {
47 match self {
48 Backing::Plain(b) => b.range(lo, hi).await,
49 Backing::Cached(c) => c.range(lo, hi).await,
50 }
51 }
52 async fn scan_all(&self) -> Vec<(Bytes, Bytes)> {
53 match self {
54 Backing::Plain(b) => b.scan_all().await,
55 Backing::Cached(c) => c.scan_all().await,
56 }
57 }
58
59 async fn put(&mut self, key: Bytes, value: Bytes, ctx: RecordContext) {
64 match self {
65 Backing::Plain(b) => b.put(key, value).await,
66 Backing::Cached(c) => c.put(key, value, ctx),
67 }
68 }
69
70 async fn apply(&mut self, key: Bytes, value: Option<Bytes>) {
74 match (self, value) {
75 (Backing::Plain(b), Some(v)) => b.put(key, v).await,
76 (Backing::Plain(b), None) => {
77 b.delete(&key).await;
78 }
79 (Backing::Cached(c), Some(v)) => c.put_inner(key, v).await,
80 (Backing::Cached(c), None) => c.delete_inner(&key).await,
81 }
82 }
83
84 async fn clear(&mut self) {
85 match self {
86 Backing::Plain(b) => b.clear().await,
87 Backing::Cached(c) => c.clear().await,
88 }
89 }
90
91 async fn snapshot(&self) -> Bytes {
93 crate::store::snapshot::encode_byte_entries(&self.scan_all().await)
94 }
95
96 async fn restore(&mut self, data: &[u8]) -> Result<(), crate::error::StreamsClientError> {
99 let entries = crate::store::snapshot::decode_byte_entries(data)?;
100 self.clear().await;
101 for (key, value) in entries {
102 self.apply(key, Some(value)).await;
103 }
104 Ok(())
105 }
106}
107
108#[async_trait]
114pub trait WindowStore<K: Send + Sync, V: Send>: StateStore {
115 async fn fetch_single(&self, key: &K, window_start: i64) -> Option<(i64, V)>;
116 async fn fetch(&self, key: &K, time_from: i64, time_to: i64) -> Vec<(i64, V)>;
117 async fn fetch_with_ts(&self, key: &K, time_from: i64, time_to: i64) -> Vec<(i64, i64, V)>;
123 async fn fetch_all_in_range(&self, start_from: i64, start_to: i64) -> Vec<(K, i64, i64, V)>;
130 async fn put(&mut self, key: K, window_start: i64, value: V, record_ts: i64);
131}
132
133pub struct WindowBytesStore<K, V> {
134 name: String,
135 changelog_topic: String,
136 backing: Backing,
137 key_serde: Box<dyn Serde<K>>,
138 value_serde: Box<dyn Serde<V>>,
139 changelog: Vec<(Bytes, Option<Bytes>)>,
140 logging: bool,
141 window_size: Time,
146 pending_ctx: Option<RecordContext>,
150}
151
152impl<K: 'static, V: 'static> WindowBytesStore<K, V> {
153 #[must_use]
154 pub(crate) fn new(
155 name: String,
156 backend: Box<dyn ByteKeyValueStore>,
157 key_serde: Box<dyn Serde<K>>,
158 value_serde: Box<dyn Serde<V>>,
159 changelog_topic: String,
160 window_size: Time,
161 ) -> Self {
162 Self {
163 name,
164 changelog_topic,
165 backing: Backing::Plain(backend),
166 key_serde,
167 value_serde,
168 changelog: Vec::new(),
169 logging: true,
170 window_size,
171 pending_ctx: None,
172 }
173 }
174
175 #[must_use]
176 pub fn in_memory(
177 name: String,
178 key_serde: Box<dyn Serde<K>>,
179 value_serde: Box<dyn Serde<V>>,
180 changelog_topic: String,
181 window_size: Time,
182 ) -> Self {
183 Self::new(
184 name,
185 Box::new(InMemoryBytes::default()),
186 key_serde,
187 value_serde,
188 changelog_topic,
189 window_size,
190 )
191 }
192
193 pub(crate) fn enable_cache(&mut self, cache: Arc<Mutex<NamedCache>>) {
199 if !matches!(self.backing, Backing::Plain(_)) {
200 return; }
202 let placeholder = Backing::Plain(Box::new(InMemoryBytes::default()));
203 let Backing::Plain(backend) = std::mem::replace(&mut self.backing, placeholder) else {
204 unreachable!("guarded by the matches! above")
205 };
206 let inner: Arc<AsyncMutex<Box<dyn ByteKeyValueStore>>> = Arc::new(AsyncMutex::new(backend));
207 self.backing = Backing::Cached(CachingWindowStore::with_name(
208 cache,
209 inner,
210 self.name.clone(),
211 ));
212 }
213
214 #[must_use]
216 pub(crate) fn is_cached(&self) -> bool {
217 matches!(self.backing, Backing::Cached(_))
218 }
219
220 fn write_ctx(&self) -> RecordContext {
226 self.pending_ctx.clone().unwrap_or(RecordContext {
227 topic: self.changelog_topic.clone(),
228 partition: 0,
229 offset: 0,
230 timestamp: 0,
231 })
232 }
233}
234
235#[async_trait]
236impl<K: Send + 'static, V: Send + 'static> StateStore for WindowBytesStore<K, V> {
237 fn name(&self) -> &str {
238 &self.name
239 }
240 async fn flush(&mut self) {}
241 fn close(&mut self) {}
242 fn as_any_mut(&mut self) -> &mut dyn Any {
243 self
244 }
245 fn changelog_topic(&self) -> &str {
246 &self.changelog_topic
247 }
248 fn take_changelog(&mut self) -> Vec<(Bytes, Option<Bytes>)> {
249 std::mem::take(&mut self.changelog)
250 }
251 async fn apply_changelog(&mut self, key: Bytes, value: Option<Bytes>) {
252 self.backing.apply(key, value).await;
255 }
256 fn set_logging(&mut self, on: bool) {
257 self.logging = on;
258 }
259 fn as_iq(&self) -> Option<&dyn crate::store::iq::IqQueryable> {
260 Some(self)
261 }
262 fn set_record_context(&mut self, ctx: RecordContext) {
263 self.pending_ctx = Some(ctx);
264 }
265 #[allow(private_interfaces)]
266 fn enable_cache_erased(&mut self, cache: Arc<Mutex<NamedCache>>) -> bool {
267 self.enable_cache(cache);
268 true
269 }
270 fn is_cached_erased(&self) -> bool {
271 self.is_cached()
272 }
273 #[allow(private_interfaces)]
274 async fn flush_cache_into(
275 &mut self,
276 buffer: &mut std::collections::VecDeque<(usize, crate::processor::erased::ErasedRecord)>,
277 children: &[usize],
278 ) {
279 use crate::{
280 dsl::{
281 processors::change::Change,
282 windows::{Window, Windowed},
283 },
284 processor::erased::ErasedRecord,
285 };
286 let Backing::Cached(cache) = &self.backing else {
287 return;
288 };
289 let drained = cache.flush_with_old().await;
292 for (sk, old_wrapped, new_wrapped, ctx) in drained {
293 if self.logging {
296 self.changelog.push((sk.clone(), new_wrapped.clone()));
297 }
298 let window_start = window_start_of(&sk);
300 let key_bytes = key_bytes_of(&sk);
301 for &child in children {
302 let key: K = self
303 .key_serde
304 .deserialize(&self.changelog_topic, key_bytes)
305 .expect("flush_cache_into window key deserialize");
306 let old: Option<V> = old_wrapped.as_ref().map(|w| {
309 let (_ts, raw) = unwrap_value(w);
310 self.value_serde
311 .deserialize(&self.changelog_topic, raw)
312 .expect("flush_cache_into window old value deserialize")
313 });
314 let new: Option<V> = new_wrapped.as_ref().map(|w| {
315 let (_ts, raw) = unwrap_value(w);
316 self.value_serde
317 .deserialize(&self.changelog_topic, raw)
318 .expect("flush_cache_into window new value deserialize")
319 });
320 let windowed = Windowed {
321 key,
322 window: Window {
323 start: window_start,
324 end: window_start + self.window_size.millis_i64(),
325 },
326 };
327 let change = Change { old, new };
328 buffer.push_back((
329 child,
330 ErasedRecord::new(Some(Box::new(windowed)), Box::new(change), ctx.timestamp),
331 ));
332 }
333 }
334 }
335 async fn clear(&mut self) {
336 self.backing.clear().await;
337 self.changelog.clear();
338 }
339 async fn snapshot(&mut self) -> Bytes {
340 self.backing.snapshot().await
341 }
342 async fn restore_snapshot(
343 &mut self,
344 data: Bytes,
345 ) -> Result<(), crate::error::StreamsClientError> {
346 self.backing.restore(&data).await?;
347 self.changelog.clear();
349 Ok(())
350 }
351}
352
353#[async_trait::async_trait]
356impl<K: Send + 'static, V: Send + 'static> crate::store::iq::IqQueryable
357 for WindowBytesStore<K, V>
358{
359 fn kind(&self) -> crate::store::iq::StoreKind {
360 crate::store::iq::StoreKind::Window
361 }
362 async fn iq_window_fetch_single(&self, key: &[u8], window_start: i64) -> Option<bytes::Bytes> {
363 let sk = store_key(key, window_start, 0);
364 let wrapped = self.backing.get(&sk).await?;
365 let (_ts, raw) = unwrap_value(&wrapped);
366 Some(bytes::Bytes::copy_from_slice(raw))
367 }
368 async fn iq_window_fetch(
369 &self,
370 key: &[u8],
371 time_from: i64,
372 time_to: i64,
373 ) -> Vec<(i64, bytes::Bytes)> {
374 let lo = store_key(key, time_from, 0);
375 let hi = store_key(key, time_to.saturating_add(1), 0);
376 let mut out = Vec::new();
377 for (k, wrapped) in self.backing.range(&lo, &hi).await {
378 if key_bytes_of(&k) != key {
379 continue;
380 }
381 let (_ts, raw) = unwrap_value(&wrapped);
382 out.push((window_start_of(&k), bytes::Bytes::copy_from_slice(raw)));
383 }
384 out
385 }
386
387 async fn iq2_execute(
388 &self,
389 query: &crate::store::iq::Iq2Query,
390 ) -> Result<Box<dyn Any + Send>, crate::store::iq::Iq2Failure> {
391 use crate::store::iq::{Iq2Failure, Iq2Query};
392
393 let ser = |b: &dyn Any| -> Result<bytes::Bytes, Iq2Failure> {
397 let k = b.downcast_ref::<K>().ok_or(Iq2Failure::KeyTypeMismatch)?;
398 Ok(self.key_serde.serialize(&self.changelog_topic, k))
399 };
400
401 match query {
402 Iq2Query::WindowKey {
403 key,
404 from_ts,
405 to_ts,
406 } => {
407 let kb = ser(&**key)?;
408 let from = (*from_ts).max(0);
413 let to = *to_ts;
414
415 let lo = store_key(&kb, from, 0);
416 let hi = store_key(&kb, to.saturating_add(1), 0);
417 let mut out: Vec<(i64, V)> = Vec::new();
418 for (sk, wrapped) in self.backing.range(&lo, &hi).await {
419 if key_bytes_of(&sk) != kb.as_ref() {
420 continue;
421 }
422 let (_ts, raw) = unwrap_value(&wrapped);
423 out.push((
424 window_start_of(&sk),
425 self.value_serde
426 .deserialize(&self.changelog_topic, raw)
427 .expect("iqv2 window value deserialize"),
428 ));
429 }
430 Ok(Box::new(out))
431 }
432 Iq2Query::WindowRange {
433 lo,
434 hi,
435 from_ts,
436 to_ts,
437 } => {
438 let lo_b = match lo {
439 Some(b) => Some(ser(&**b)?),
440 None => None,
441 };
442 let hi_b = match hi {
443 Some(b) => Some(ser(&**b)?),
444 None => None,
445 };
446 let from = *from_ts;
447 let to = *to_ts;
448
449 let mut out: Vec<((K, i64), V)> = Vec::new();
450 for (sk, wrapped) in self.backing.scan_all().await {
451 let ws = window_start_of(&sk);
452 if ws < from || ws > to {
453 continue;
454 }
455 let kbytes = key_bytes_of(&sk);
456 if lo_b.as_ref().is_some_and(|l| kbytes < l.as_ref()) {
457 continue;
458 }
459 if hi_b.as_ref().is_some_and(|h| kbytes > h.as_ref()) {
460 continue;
461 }
462 let key = self
463 .key_serde
464 .deserialize(&self.changelog_topic, kbytes)
465 .expect("iqv2 window range key deserialize");
466 let (_ts, raw) = unwrap_value(&wrapped);
467 let value = self
468 .value_serde
469 .deserialize(&self.changelog_topic, raw)
470 .expect("iqv2 window range value deserialize");
471 out.push(((key, ws), value));
472 }
473 Ok(Box::new(out))
474 }
475 _ => Err(Iq2Failure::UnknownQueryType),
476 }
477 }
478}
479
480#[async_trait]
481impl<K: Send + Sync + 'static, V: Send + 'static> WindowStore<K, V> for WindowBytesStore<K, V> {
482 async fn fetch_single(&self, key: &K, window_start: i64) -> Option<(i64, V)> {
483 let kb = self.key_serde.serialize(&self.changelog_topic, key);
484 let sk = store_key(&kb, window_start, 0);
485 let wrapped = self.backing.get(&sk).await?;
486 let (ts, raw) = unwrap_value(&wrapped);
487 Some((
488 ts,
489 self.value_serde
490 .deserialize(&self.changelog_topic, raw)
491 .expect("window value deserialize"),
492 ))
493 }
494
495 async fn fetch(&self, key: &K, time_from: i64, time_to: i64) -> Vec<(i64, V)> {
496 let kb = self.key_serde.serialize(&self.changelog_topic, key);
497 let lo = store_key(&kb, time_from, 0);
498 let hi = store_key(&kb, time_to.saturating_add(1), 0);
499 let mut out = Vec::new();
500 for (k, wrapped) in self.backing.range(&lo, &hi).await {
501 if key_bytes_of(&k) != kb.as_ref() {
503 continue;
504 }
505 let (_ts, raw) = unwrap_value(&wrapped);
506 out.push((
507 window_start_of(&k),
508 self.value_serde
509 .deserialize(&self.changelog_topic, raw)
510 .expect("window value deserialize"),
511 ));
512 }
513 out
514 }
515
516 async fn fetch_with_ts(&self, key: &K, time_from: i64, time_to: i64) -> Vec<(i64, i64, V)> {
517 let kb = self.key_serde.serialize(&self.changelog_topic, key);
518 let lo = store_key(&kb, time_from, 0);
519 let hi = store_key(&kb, time_to.saturating_add(1), 0);
520 let mut out = Vec::new();
521 for (k, wrapped) in self.backing.range(&lo, &hi).await {
522 if key_bytes_of(&k) != kb.as_ref() {
523 continue;
524 }
525 let (ts, raw) = unwrap_value(&wrapped);
526 out.push((
527 window_start_of(&k),
528 ts,
529 self.value_serde
530 .deserialize(&self.changelog_topic, raw)
531 .expect("window value deserialize"),
532 ));
533 }
534 out
535 }
536
537 async fn fetch_all_in_range(&self, start_from: i64, start_to: i64) -> Vec<(K, i64, i64, V)> {
538 let mut out = Vec::new();
539 for (k, wrapped) in self.backing.scan_all().await {
540 let ws = window_start_of(&k);
541 if ws < start_from || ws > start_to {
542 continue;
543 }
544 let key = self
545 .key_serde
546 .deserialize(&self.changelog_topic, key_bytes_of(&k))
547 .expect("window key deserialize");
548 let (ts, raw) = unwrap_value(&wrapped);
549 let value = self
550 .value_serde
551 .deserialize(&self.changelog_topic, raw)
552 .expect("window value deserialize");
553 out.push((key, ws, ts, value));
554 }
555 out
556 }
557
558 async fn put(&mut self, key: K, window_start: i64, value: V, record_ts: i64) {
559 let kb = self.key_serde.serialize(&self.changelog_topic, &key);
560 let sk = store_key(&kb, window_start, 0);
561 let raw = self.value_serde.serialize(&self.changelog_topic, &value);
562 let wrapped = wrap_value(record_ts, &raw);
563 match &self.backing {
564 Backing::Plain(_) => {
566 self.backing
567 .put(sk.clone(), wrapped.clone(), self.write_ctx())
568 .await;
569 if self.logging {
570 self.changelog.push((sk, Some(wrapped)));
571 }
572 }
573 Backing::Cached(_) => {
576 let ctx = self.write_ctx();
577 self.backing.put(sk, wrapped, ctx).await;
578 }
579 }
580 }
581}
582
583#[cfg(test)]
584mod tests {
585 use super::*;
586 use crate::processor::serde::{I64Serde, StringSerde};
587
588 #[tokio::test]
589 async fn put_fetch_single_and_range() {
590 let mut s = WindowBytesStore::<String, i64>::in_memory(
591 "w".into(),
592 Box::new(StringSerde),
593 Box::new(I64Serde),
594 "app-w-changelog".into(),
595 millis(10),
596 );
597 s.put("k".to_string(), 0, 1, 5).await;
598 s.put("k".to_string(), 0, 2, 7).await;
599 s.put("k".to_string(), 10, 9, 11).await;
600 assert_eq!(s.fetch_single(&"k".to_string(), 0).await, Some((7, 2)));
601 assert_eq!(s.fetch_single(&"k".to_string(), 10).await, Some((11, 9)));
602 assert_eq!(s.fetch_single(&"k".to_string(), 99).await, None);
603 assert_eq!(
604 s.fetch(&"k".to_string(), 0, 10).await,
605 vec![(0, 2), (10, 9)]
606 );
607 assert_eq!(s.take_changelog().len(), 3);
608 }
609
610 #[tokio::test]
611 async fn fetch_with_ts_returns_window_start_and_record_ts() {
612 let mut s = WindowBytesStore::<String, i64>::in_memory(
613 "w".into(),
614 Box::new(StringSerde),
615 Box::new(I64Serde),
616 "app-w-changelog".into(),
617 millis(10),
618 );
619 s.put("k".into(), 0, 10, 5).await; s.put("k".into(), 10, 20, 17).await; let got = s.fetch_with_ts(&"k".to_string(), 0, 10).await;
622 assert_eq!(got, vec![(0, 5, 10), (10, 17, 20)]); }
624
625 #[tokio::test]
626 async fn fetch_all_in_range_scans_across_keys() {
627 let mut s = WindowBytesStore::<String, i64>::in_memory(
628 "w".into(),
629 Box::new(StringSerde),
630 Box::new(I64Serde),
631 "app-w-changelog".into(),
632 millis(10),
633 );
634 s.put("a".into(), 0, 1, 5).await;
636 s.put("b".into(), 0, 7, 6).await;
637 s.put("a".into(), 10, 9, 12).await;
638
639 let mut got = s.fetch_all_in_range(0, 0).await;
641 got.sort();
642 assert_eq!(
643 got,
644 vec![("a".to_string(), 0, 5, 1), ("b".to_string(), 0, 6, 7)]
645 );
646
647 assert_eq!(s.fetch_all_in_range(0, 10).await.len(), 3);
649 assert!(s.fetch_all_in_range(11, 100).await.is_empty());
651 }
652
653 #[tokio::test]
654 async fn iq2_window_key_and_range() {
655 use crate::store::iq::{Iq2Query, IqQueryable};
656 let mut s = WindowBytesStore::<String, i64>::in_memory(
657 "w".into(),
658 Box::new(StringSerde),
659 Box::new(I64Serde),
660 "w-changelog".into(),
661 secs(1),
662 );
663 s.put("a".into(), 0, 10, 5).await;
664 s.put("a".into(), 1000, 20, 1005).await;
665 s.put("b".into(), 0, 30, 6).await;
666 let q: &dyn IqQueryable = s.as_iq().unwrap();
667
668 let wk = q
670 .iq2_execute(&Iq2Query::WindowKey {
671 key: Box::new("a".to_string()),
672 from_ts: 0,
673 to_ts: 1000,
674 })
675 .await
676 .unwrap();
677 assert_eq!(
678 *wk.downcast::<Vec<(i64, i64)>>().unwrap(),
679 vec![(0, 10), (1000, 20)]
680 );
681
682 let wr = q
684 .iq2_execute(&Iq2Query::WindowRange {
685 lo: None,
686 hi: None,
687 from_ts: 0,
688 to_ts: 0,
689 })
690 .await
691 .unwrap();
692 assert_eq!(
693 *wr.downcast::<Vec<((String, i64), i64)>>().unwrap(),
694 vec![(("a".to_string(), 0), 10), (("b".to_string(), 0), 30)]
695 );
696
697 let wr_b = q
699 .iq2_execute(&Iq2Query::WindowRange {
700 lo: Some(Box::new("b".to_string())),
701 hi: Some(Box::new("b".to_string())),
702 from_ts: 0,
703 to_ts: 2000,
704 })
705 .await
706 .unwrap();
707 assert_eq!(
708 *wr_b.downcast::<Vec<((String, i64), i64)>>().unwrap(),
709 vec![(("b".to_string(), 0), 30)]
710 );
711 }
712
713 #[tokio::test]
719 async fn iq2_window_key_default_bounds_returns_all_windows() {
720 use crate::store::iq::{Iq2Query, IqQueryable};
721 let mut s = WindowBytesStore::<String, i64>::in_memory(
722 "w".into(),
723 Box::new(StringSerde),
724 Box::new(I64Serde),
725 "w-changelog".into(),
726 secs(1),
727 );
728 s.put("a".into(), 0, 10, 5).await;
729 s.put("a".into(), 1000, 20, 1005).await;
730 let q: &dyn IqQueryable = s.as_iq().unwrap();
731 let wk = q
732 .iq2_execute(&Iq2Query::WindowKey {
733 key: Box::new("a".to_string()),
734 from_ts: i64::MIN,
735 to_ts: i64::MAX,
736 })
737 .await
738 .unwrap();
739 assert_eq!(
740 *wk.downcast::<Vec<(i64, i64)>>().unwrap(),
741 vec![(0, 10), (1000, 20)]
742 );
743 }
744
745 fn cached_store(window_size: Time) -> WindowBytesStore<String, i64> {
748 let mut s = WindowBytesStore::<String, i64>::in_memory(
749 "w".into(),
750 Box::new(StringSerde),
751 Box::new(I64Serde),
752 "w-changelog".into(),
753 window_size,
754 );
755 s.enable_cache(Arc::new(Mutex::new(NamedCache::new("w".into()))));
756 s
757 }
758
759 fn ctx_at(ts: i64) -> RecordContext {
760 RecordContext {
761 topic: "t".into(),
762 partition: 0,
763 offset: 0,
764 timestamp: ts,
765 }
766 }
767
768 #[tokio::test]
769 async fn cached_window_store_reads_your_writes() {
770 let mut s = cached_store(millis(10));
771 s.set_record_context(ctx_at(0));
772 s.put("a".into(), 0, 1, 5).await;
774 s.put("a".into(), 0, 2, 7).await;
775 assert_eq!(s.fetch_single(&"a".to_string(), 0).await, Some((7, 2)));
777 assert!(s.take_changelog().is_empty());
779 }
780
781 #[tokio::test]
782 async fn flush_cache_into_emits_deduped_windowed_change() {
783 use crate::dsl::{
784 processors::change::Change,
785 windows::{Window, Windowed},
786 };
787 let mut s = cached_store(millis(10));
788 s.set_record_context(ctx_at(0));
790 s.put("a".into(), 0, 1, 5).await;
791 let mut seed = std::collections::VecDeque::new();
792 s.flush_cache_into(&mut seed, &[0]).await;
793 let _ = s.take_changelog();
794
795 s.set_record_context(ctx_at(7));
797 s.put("a".into(), 0, 2, 11).await;
798 s.put("a".into(), 0, 3, 13).await;
799
800 let mut buffer = std::collections::VecDeque::new();
801 s.flush_cache_into(&mut buffer, &[7]).await;
802 assert_eq!(buffer.len(), 1);
804 let (child, rec) = &buffer[0];
805 assert_eq!(*child, 7);
806 assert_eq!(rec.timestamp, 7);
808 let key = rec
810 .key
811 .as_ref()
812 .unwrap()
813 .downcast_ref::<Windowed<String>>()
814 .unwrap();
815 assert_eq!(key.key, "a");
816 assert_eq!(key.window, Window { start: 0, end: 10 }); let change = rec.value.downcast_ref::<Change<i64>>().unwrap();
819 assert_eq!(change.old, Some(1));
820 assert_eq!(change.new, Some(3));
821
822 let cl = s.take_changelog();
824 assert_eq!(cl.len(), 1);
825 assert_eq!(
826 cl[0].0,
827 store_key(
828 &StringSerde.serialize("w-changelog", &"a".to_string()),
829 0,
830 0
831 )
832 );
833 assert_eq!(
834 cl[0].1,
835 Some(wrap_value(13, &I64Serde.serialize("w-changelog", &3)))
836 );
837
838 assert_eq!(s.fetch_single(&"a".to_string(), 0).await, Some((13, 3)));
840 }
841
842 #[tokio::test]
843 async fn cached_sliding_window_uses_true_size_not_retention_span_for_key_end() {
844 use crate::dsl::{
851 processors::change::Change,
852 windows::{Window, Windowed},
853 };
854
855 const D: Time = millis(10);
859 let mut s = cached_store(D);
860
861 let ws = 0;
863 s.set_record_context(ctx_at(7));
864 s.put("a".into(), ws, 1, 7).await;
865
866 let mut buffer = std::collections::VecDeque::new();
867 s.flush_cache_into(&mut buffer, &[0]).await;
868 assert_eq!(buffer.len(), 1);
869 let (_child, rec) = &buffer[0];
870 let key = rec
871 .key
872 .as_ref()
873 .unwrap()
874 .downcast_ref::<Windowed<String>>()
875 .unwrap();
876 assert_eq!(key.key, "a");
877 assert_eq!(
879 key.window,
880 Window {
881 start: ws,
882 end: ws + D.millis_i64()
883 }
884 );
885 let change = rec.value.downcast_ref::<Change<i64>>().unwrap();
886 assert_eq!(change.new, Some(1));
887 }
888
889 #[tokio::test]
893 async fn cached_window_store_fetch_overlays_cache() {
894 let mut s = cached_store(millis(10));
895 s.set_record_context(ctx_at(0));
896 s.put("a".into(), 0, 1, 5).await;
898 s.put("a".into(), 10, 2, 15).await;
899 s.put("a".into(), 20, 3, 25).await;
900
901 assert_eq!(
903 s.fetch(&"a".to_string(), 0, 20).await,
904 vec![(0, 1), (10, 2), (20, 3)]
905 );
906 assert_eq!(
908 s.fetch_with_ts(&"a".to_string(), 0, 20).await,
909 vec![(0, 5, 1), (10, 15, 2), (20, 25, 3)]
910 );
911 assert_eq!(
913 s.fetch(&"a".to_string(), 0, 10).await,
914 vec![(0, 1), (10, 2)]
915 );
916 }
917
918 #[tokio::test]
921 async fn cached_window_store_fetch_all_overlays_cache() {
922 let mut s = cached_store(millis(10));
923 s.set_record_context(ctx_at(0));
924 s.put("a".into(), 0, 1, 5).await;
925 s.put("b".into(), 50, 2, 55).await;
926
927 assert_eq!(
929 s.fetch_all_in_range(0, 10).await,
930 vec![("a".to_string(), 0, 5, 1)]
931 );
932 assert_eq!(
934 s.fetch_all_in_range(0, 50).await,
935 vec![("a".to_string(), 0, 5, 1), ("b".to_string(), 50, 55, 2),]
936 );
937 }
938
939 #[tokio::test]
943 async fn cached_window_store_apply_changelog_goes_below_cache() {
944 let mut s = cached_store(millis(10));
945 let sk = store_key(
946 &StringSerde.serialize("w-changelog", &"a".to_string()),
947 0,
948 0,
949 );
950 let wrapped = wrap_value(7, &I64Serde.serialize("w-changelog", &9));
951 s.apply_changelog(sk.clone(), Some(wrapped)).await;
952 assert_eq!(s.fetch_single(&"a".to_string(), 0).await, Some((7, 9)));
953 let mut buffer = std::collections::VecDeque::new();
955 s.flush_cache_into(&mut buffer, &[0]).await;
956 assert!(buffer.is_empty());
957 assert!(s.take_changelog().is_empty());
958
959 s.apply_changelog(sk, None).await;
961 assert_eq!(s.fetch_single(&"a".to_string(), 0).await, None);
962 }
963
964 #[tokio::test]
967 async fn cached_window_store_clear_empties_everything() {
968 let mut s = cached_store(millis(10));
969 s.set_record_context(ctx_at(0));
970 s.put("a".into(), 0, 1, 5).await;
971 StateStore::clear(&mut s).await;
972 assert_eq!(s.fetch_single(&"a".to_string(), 0).await, None);
973 assert!(s.fetch_all_in_range(i64::MIN, i64::MAX).await.is_empty());
974 let mut buffer = std::collections::VecDeque::new();
975 s.flush_cache_into(&mut buffer, &[0]).await;
976 assert!(buffer.is_empty());
977 }
978
979 #[tokio::test]
982 async fn enable_cache_is_idempotent() {
983 let mut s = WindowBytesStore::<String, i64>::in_memory(
984 "w".into(),
985 Box::new(StringSerde),
986 Box::new(I64Serde),
987 "w-changelog".into(),
988 millis(10),
989 );
990 assert!(!s.is_cached());
991 s.enable_cache(Arc::new(Mutex::new(NamedCache::new("w".into()))));
992 assert!(s.is_cached());
993 s.enable_cache(Arc::new(Mutex::new(NamedCache::new("w".into()))));
994 assert!(s.is_cached());
995 }
996
997 #[tokio::test]
998 async fn plain_window_store_unchanged() {
999 let mut s = WindowBytesStore::<String, i64>::in_memory(
1002 "w".into(),
1003 Box::new(StringSerde),
1004 Box::new(I64Serde),
1005 "w-changelog".into(),
1006 millis(10),
1007 );
1008 s.put("a".into(), 0, 1, 5).await;
1009 assert_eq!(s.fetch_single(&"a".to_string(), 0).await, Some((5, 1)));
1010 let cl = s.take_changelog();
1011 assert_eq!(cl.len(), 1);
1012 let mut buffer = std::collections::VecDeque::new();
1013 s.flush_cache_into(&mut buffer, &[0]).await;
1014 assert!(buffer.is_empty());
1015 }
1016
1017 #[tokio::test]
1022 async fn plain_window_store_lifecycle_and_apply_changelog() {
1023 let mut s = WindowBytesStore::<String, i64>::in_memory(
1024 "w".into(),
1025 Box::new(StringSerde),
1026 Box::new(I64Serde),
1027 "w-changelog".into(),
1028 millis(10),
1029 );
1030 s.set_logging(false);
1032 s.put("a".into(), 0, 1, 5).await;
1033 assert!(s.take_changelog().is_empty());
1034
1035 let sk = store_key(
1037 &StringSerde.serialize("w-changelog", &"b".to_string()),
1038 0,
1039 0,
1040 );
1041 s.apply_changelog(
1042 sk.clone(),
1043 Some(wrap_value(7, &I64Serde.serialize("w-changelog", &9))),
1044 )
1045 .await;
1046 assert_eq!(s.fetch_single(&"b".to_string(), 0).await, Some((7, 9)));
1047 assert!(s.take_changelog().is_empty());
1048 s.apply_changelog(sk, None).await;
1050 assert_eq!(s.fetch_single(&"b".to_string(), 0).await, None);
1051
1052 s.flush().await;
1054 s.close();
1055 StateStore::clear(&mut s).await;
1056 assert_eq!(s.fetch_single(&"a".to_string(), 0).await, None);
1057 assert!(s.take_changelog().is_empty());
1058 }
1059}