1use std::{
7 any::Any,
8 sync::{Arc, Mutex},
9};
10
11use async_trait::async_trait;
12use bytes::Bytes;
13
14use crate::{
15 processor::{record::RecordContext, serde::Serde},
16 store::{
17 api::{KeyValueStore, StateStore},
18 byte::{ByteKeyValueStore, InMemoryBytes},
19 cache::{kv::CachingKeyValueStore, named::NamedCache},
20 },
21};
22
23enum Backing {
29 Plain(Box<dyn ByteKeyValueStore>),
30 Cached(CachingKeyValueStore),
33}
34
35impl Backing {
36 async fn get(&self, key: &[u8]) -> Option<Bytes> {
39 match self {
40 Backing::Plain(b) => b.get(key).await,
41 Backing::Cached(c) => c.get(key).await,
42 }
43 }
44 async fn range(&self, lo: &[u8], hi: &[u8]) -> Vec<(Bytes, Bytes)> {
45 match self {
46 Backing::Plain(b) => b.range(lo, hi).await,
47 Backing::Cached(c) => c.range(lo, hi).await,
48 }
49 }
50 async fn scan_all(&self) -> Vec<(Bytes, Bytes)> {
51 match self {
52 Backing::Plain(b) => b.scan_all().await,
53 Backing::Cached(c) => c.scan_all().await,
54 }
55 }
56 async fn approx_len(&self) -> u64 {
57 match self {
58 Backing::Plain(b) => b.approx_len().await,
59 Backing::Cached(c) => c.scan_all().await.len() as u64,
61 }
62 }
63
64 async fn put(&mut self, key: Bytes, value: Bytes, ctx: RecordContext) {
68 match self {
69 Backing::Plain(b) => b.put(key, value).await,
70 Backing::Cached(c) => c.put(key, value, ctx).await,
71 }
72 }
73
74 async fn delete(&mut self, key: Bytes, ctx: RecordContext) -> Option<Bytes> {
79 match self {
80 Backing::Plain(b) => b.delete(&key).await,
81 Backing::Cached(c) => {
82 let prev = c.get(&key).await;
83 c.delete(key, ctx).await;
84 prev
85 }
86 }
87 }
88
89 async fn apply(&mut self, key: Bytes, value: Option<Bytes>) {
92 match (self, value) {
93 (Backing::Plain(b), Some(v)) => b.put(key, v).await,
94 (Backing::Plain(b), None) => {
95 b.delete(&key).await;
96 }
97 (Backing::Cached(c), Some(v)) => c.put_inner(key, v).await,
98 (Backing::Cached(c), None) => c.delete_inner(&key).await,
99 }
100 }
101
102 async fn clear(&mut self) {
103 match self {
104 Backing::Plain(b) => b.clear().await,
105 Backing::Cached(c) => c.clear().await,
106 }
107 }
108
109 async fn snapshot(&self) -> Bytes {
111 crate::store::snapshot::encode_byte_entries(&self.scan_all().await)
112 }
113
114 async fn restore(&mut self, data: &[u8]) -> Result<(), crate::error::StreamsClientError> {
117 let entries = crate::store::snapshot::decode_byte_entries(data)?;
118 self.clear().await;
119 for (key, value) in entries {
120 self.apply(key, Some(value)).await;
121 }
122 Ok(())
123 }
124}
125
126pub struct KeyValueBytesStore<K, V> {
127 name: String,
128 changelog_topic: String,
129 backing: Backing,
130 key_serde: Box<dyn Serde<K>>,
131 value_serde: Box<dyn Serde<V>>,
132 changelog: Vec<(Bytes, Option<Bytes>)>,
133 logging: bool,
134 pending_ctx: Option<RecordContext>,
138}
139
140impl<K: 'static, V: 'static> KeyValueBytesStore<K, V> {
141 #[must_use]
142 pub(crate) fn new(
143 name: String,
144 backend: Box<dyn ByteKeyValueStore>,
145 key_serde: Box<dyn Serde<K>>,
146 value_serde: Box<dyn Serde<V>>,
147 changelog_topic: String,
148 ) -> Self {
149 Self {
150 name,
151 changelog_topic,
152 backing: Backing::Plain(backend),
153 key_serde,
154 value_serde,
155 changelog: Vec::new(),
156 logging: true,
157 pending_ctx: None,
158 }
159 }
160
161 pub(crate) fn enable_cache(&mut self, cache: Arc<Mutex<NamedCache>>) {
167 if !matches!(self.backing, Backing::Plain(_)) {
168 return; }
170 let placeholder = Backing::Plain(Box::new(InMemoryBytes::default()));
173 let Backing::Plain(backend) = std::mem::replace(&mut self.backing, placeholder) else {
174 unreachable!("guarded by the matches! above")
175 };
176 self.backing = Backing::Cached(CachingKeyValueStore::with_name(
177 cache,
178 backend,
179 self.name.clone(),
180 ));
181 }
182
183 #[must_use]
190 pub(crate) fn is_cached(&self) -> bool {
191 matches!(self.backing, Backing::Cached(_))
192 }
193
194 #[must_use]
196 pub fn in_memory(
197 name: String,
198 key_serde: Box<dyn Serde<K>>,
199 value_serde: Box<dyn Serde<V>>,
200 changelog_topic: String,
201 ) -> Self {
202 Self::new(
203 name,
204 Box::new(InMemoryBytes::default()),
205 key_serde,
206 value_serde,
207 changelog_topic,
208 )
209 }
210
211 fn write_ctx(&self) -> RecordContext {
219 self.pending_ctx.clone().unwrap_or(RecordContext {
220 topic: self.changelog_topic.clone(),
221 partition: 0,
222 offset: 0,
223 timestamp: 0,
224 })
225 }
226}
227
228#[async_trait]
229impl<K: Send + 'static, V: Send + 'static> StateStore for KeyValueBytesStore<K, V> {
230 fn name(&self) -> &str {
231 &self.name
232 }
233 async fn flush(&mut self) {}
234 fn close(&mut self) {}
235 fn as_any_mut(&mut self) -> &mut dyn Any {
236 self
237 }
238 fn changelog_topic(&self) -> &str {
239 &self.changelog_topic
240 }
241 fn take_changelog(&mut self) -> Vec<(Bytes, Option<Bytes>)> {
242 std::mem::take(&mut self.changelog)
243 }
244 async fn apply_changelog(&mut self, key: Bytes, value: Option<Bytes>) {
245 self.backing.apply(key, value).await;
248 }
249 fn set_logging(&mut self, on: bool) {
250 self.logging = on;
251 }
252 fn as_iq(&self) -> Option<&dyn crate::store::iq::IqQueryable> {
253 Some(self)
254 }
255 fn set_record_context(&mut self, ctx: RecordContext) {
256 self.pending_ctx = Some(ctx);
257 }
258 #[allow(private_interfaces)]
259 fn enable_cache_erased(&mut self, cache: Arc<Mutex<NamedCache>>) -> bool {
260 self.enable_cache(cache);
261 true
262 }
263 fn is_cached_erased(&self) -> bool {
264 self.is_cached()
265 }
266 #[allow(private_interfaces)]
267 async fn flush_cache_into(
268 &mut self,
269 buffer: &mut std::collections::VecDeque<(usize, crate::processor::erased::ErasedRecord)>,
270 children: &[usize],
271 ) {
272 use crate::{dsl::processors::change::Change, processor::erased::ErasedRecord};
273 let Backing::Cached(cache) = &self.backing else {
274 return;
275 };
276 let drained = cache.flush_with_old().await; for (kb, old_vb, new_vb, ctx) in drained {
278 if self.logging {
281 self.changelog.push((kb.clone(), new_vb.clone()));
282 }
283 for &child in children {
290 let k: K = self
291 .key_serde
292 .deserialize(&self.changelog_topic, &kb)
293 .expect("flush_cache_into key deserialize");
294 let old: Option<V> = old_vb.as_ref().map(|b| {
295 self.value_serde
296 .deserialize(&self.changelog_topic, b)
297 .expect("flush_cache_into old value deserialize")
298 });
299 let new: Option<V> = new_vb.as_ref().map(|b| {
300 self.value_serde
301 .deserialize(&self.changelog_topic, b)
302 .expect("flush_cache_into new value deserialize")
303 });
304 let change = Change { old, new };
305 buffer.push_back((
306 child,
307 ErasedRecord::new(Some(Box::new(k)), Box::new(change), ctx.timestamp),
308 ));
309 }
310 }
311 }
312 async fn clear(&mut self) {
313 self.backing.clear().await;
314 self.changelog.clear();
315 }
316 async fn snapshot(&mut self) -> Bytes {
317 self.backing.snapshot().await
318 }
319 async fn restore_snapshot(
320 &mut self,
321 data: Bytes,
322 ) -> Result<(), crate::error::StreamsClientError> {
323 self.backing.restore(&data).await?;
324 self.changelog.clear();
326 Ok(())
327 }
328}
329
330#[async_trait::async_trait]
335impl<K: Send + 'static, V: Send + 'static> crate::store::iq::IqQueryable
336 for KeyValueBytesStore<K, V>
337{
338 fn kind(&self) -> crate::store::iq::StoreKind {
339 crate::store::iq::StoreKind::KeyValue
340 }
341 async fn iq_kv_get(&self, key: &[u8]) -> Option<bytes::Bytes> {
342 self.backing.get(key).await
343 }
344 async fn iq_kv_range(&self, lo: &[u8], hi: &[u8]) -> Vec<(bytes::Bytes, bytes::Bytes)> {
345 let mut hi_succ = hi.to_vec();
349 hi_succ.push(0);
350 self.backing.range(lo, &hi_succ).await
351 }
352 async fn iq_kv_all(&self) -> Vec<(bytes::Bytes, bytes::Bytes)> {
353 self.backing.scan_all().await
354 }
355 async fn iq_kv_approx_count(&self) -> u64 {
356 self.backing.approx_len().await
357 }
358
359 async fn iq2_execute(
360 &self,
361 query: &crate::store::iq::Iq2Query,
362 ) -> Result<Box<dyn Any + Send>, crate::store::iq::Iq2Failure> {
363 use crate::store::iq::{Iq2Failure, Iq2Query};
364 let ser = |b: &Box<dyn Any + Send + Sync>| -> Result<bytes::Bytes, Iq2Failure> {
365 let k = b.downcast_ref::<K>().ok_or(Iq2Failure::KeyTypeMismatch)?;
366 Ok(self.key_serde.serialize(&self.changelog_topic, k))
367 };
368 match query {
369 Iq2Query::Key { key } => {
370 let kb = ser(key)?;
371 let out: Option<V> = self.backing.get(&kb).await.map(|vb| {
372 self.value_serde
373 .deserialize(&self.changelog_topic, &vb)
374 .expect("iqv2 kv value deserialize")
375 });
376 Ok(Box::new(out))
377 }
378 Iq2Query::Range { lo, hi, descending } => {
379 let lo_b = match lo {
380 Some(b) => Some(ser(b)?),
381 None => None,
382 };
383 let hi_b = match hi {
384 Some(b) => Some(ser(b)?),
385 None => None,
386 };
387 let mut rows: Vec<(K, V)> = Vec::new();
388 for (kb, vb) in self.backing.scan_all().await {
389 if lo_b.as_ref().is_some_and(|l| kb.as_ref() < l.as_ref()) {
390 continue;
391 }
392 if hi_b.as_ref().is_some_and(|h| kb.as_ref() > h.as_ref()) {
393 continue;
394 }
395 rows.push((
396 self.key_serde
397 .deserialize(&self.changelog_topic, &kb)
398 .expect("iqv2 kv range key deserialize"),
399 self.value_serde
400 .deserialize(&self.changelog_topic, &vb)
401 .expect("iqv2 kv range value deserialize"),
402 ));
403 }
404 if *descending {
405 rows.reverse();
406 }
407 Ok(Box::new(rows))
408 }
409 _ => Err(Iq2Failure::UnknownQueryType),
410 }
411 }
412}
413
414#[async_trait]
415impl<K: Send + Sync + 'static, V: Send + 'static> KeyValueStore<K, V> for KeyValueBytesStore<K, V> {
416 async fn get(&self, key: &K) -> Option<V> {
417 let kb = self.key_serde.serialize(&self.changelog_topic, key);
418 self.backing.get(&kb).await.map(|vb| {
419 self.value_serde
420 .deserialize(&self.changelog_topic, &vb)
421 .expect("store value deserialize")
422 })
423 }
424 async fn put(&mut self, key: K, value: V) {
425 let kb = self.key_serde.serialize(&self.changelog_topic, &key);
426 let vb = self.value_serde.serialize(&self.changelog_topic, &value);
427 match &self.backing {
428 Backing::Plain(_) => {
430 self.backing
431 .put(kb.clone(), vb.clone(), self.write_ctx())
432 .await;
433 if self.logging {
434 self.changelog.push((kb, Some(vb)));
435 }
436 }
437 Backing::Cached(_) => {
440 let ctx = self.write_ctx();
441 self.backing.put(kb, vb, ctx).await;
442 }
443 }
444 }
445 async fn delete(&mut self, key: &K) -> Option<V> {
446 let kb = self.key_serde.serialize(&self.changelog_topic, key);
447 let ctx = self.write_ctx();
448 let cached = matches!(self.backing, Backing::Cached(_));
449 let prev = self.backing.delete(kb.clone(), ctx).await.map(|vb| {
450 self.value_serde
451 .deserialize(&self.changelog_topic, &vb)
452 .expect("store value deserialize")
453 });
454 if self.logging && !cached {
456 self.changelog.push((kb, None));
457 }
458 prev
459 }
460 async fn range(&self, lo: &K, hi: &K) -> Vec<(K, V)> {
461 let lo_b = self.key_serde.serialize(&self.changelog_topic, lo);
462 let hi_b = self.key_serde.serialize(&self.changelog_topic, hi);
463 self.backing
464 .range(&lo_b, &hi_b)
465 .await
466 .into_iter()
467 .map(|(kb, vb)| {
468 (
469 self.key_serde
470 .deserialize(&self.changelog_topic, &kb)
471 .expect("kv range key deserialize"),
472 self.value_serde
473 .deserialize(&self.changelog_topic, &vb)
474 .expect("kv range value deserialize"),
475 )
476 })
477 .collect()
478 }
479}
480
481#[cfg(test)]
482mod tests {
483 use assert2::check;
484
485 use super::*;
486 use crate::processor::serde::{I64Serde, StringSerde};
487
488 fn store() -> KeyValueBytesStore<String, i64> {
489 KeyValueBytesStore::in_memory(
490 "s".into(),
491 Box::new(StringSerde),
492 Box::new(I64Serde),
493 "s-changelog".into(),
494 )
495 }
496
497 fn cached_store() -> KeyValueBytesStore<String, i64> {
498 let mut s = store();
499 s.enable_cache(Arc::new(Mutex::new(NamedCache::new("s".into()))));
500 s
501 }
502
503 fn ctx_at(ts: i64) -> RecordContext {
504 RecordContext {
505 topic: "t".into(),
506 partition: 0,
507 offset: 0,
508 timestamp: ts,
509 }
510 }
511
512 #[tokio::test]
513 async fn put_get_delete_and_changelog_buffer() {
514 let mut s = store();
515 s.put("a".into(), 1).await;
516 s.put("a".into(), 2).await;
517 check!(s.get(&"a".to_string()).await == Some(2));
518 check!(s.delete(&"a".to_string()).await == Some(2));
519 check!(s.get(&"a".to_string()).await == None);
520 let cl = s.take_changelog();
521 check!(cl.len() == 3);
522 check!(cl[2].1.is_none());
523 check!(s.take_changelog().is_empty());
524 }
525
526 #[tokio::test]
527 async fn range_returns_ordered_half_open() {
528 use bytes::Bytes;
529
530 use crate::processor::serde::BytesSerde;
531 let mut s = KeyValueBytesStore::<Bytes, Bytes>::in_memory(
532 "r".into(),
533 Box::new(BytesSerde),
534 Box::new(BytesSerde),
535 "r-cl".into(),
536 );
537 s.put(Bytes::from_static(&[1, 0]), Bytes::from_static(b"a"))
538 .await;
539 s.put(Bytes::from_static(&[1, 5]), Bytes::from_static(b"b"))
540 .await;
541 s.put(Bytes::from_static(&[2, 0]), Bytes::from_static(b"c"))
542 .await;
543 let r = s
544 .range(&Bytes::from_static(&[1, 0]), &Bytes::from_static(&[2, 0]))
545 .await; assert_eq!(
547 r,
548 vec![
549 (Bytes::from_static(&[1, 0]), Bytes::from_static(b"a")),
550 (Bytes::from_static(&[1, 5]), Bytes::from_static(b"b")),
551 ]
552 );
553 }
554
555 #[tokio::test]
556 async fn iq2_key_and_range() {
557 use crate::store::iq::{Iq2Failure, Iq2Query, IqQueryable};
558 let mut s = store();
559 s.put("a".into(), 1).await;
560 s.put("b".into(), 2).await;
561 s.put("c".into(), 3).await;
562 let q: &dyn IqQueryable = s.as_iq().unwrap();
563
564 let got = q
566 .iq2_execute(&Iq2Query::Key {
567 key: Box::new("b".to_string()),
568 })
569 .await
570 .unwrap();
571 assert_eq!(*got.downcast::<Option<i64>>().unwrap(), Some(2));
572 let miss = q
573 .iq2_execute(&Iq2Query::Key {
574 key: Box::new("z".to_string()),
575 })
576 .await
577 .unwrap();
578 assert_eq!(*miss.downcast::<Option<i64>>().unwrap(), None);
579
580 let r = q
582 .iq2_execute(&Iq2Query::Range {
583 lo: Some(Box::new("a".to_string())),
584 hi: Some(Box::new("b".to_string())),
585 descending: false,
586 })
587 .await
588 .unwrap();
589 assert_eq!(
590 *r.downcast::<Vec<(String, i64)>>().unwrap(),
591 vec![("a".to_string(), 1), ("b".to_string(), 2)]
592 );
593
594 let all_desc = q
596 .iq2_execute(&Iq2Query::Range {
597 lo: None,
598 hi: None,
599 descending: true,
600 })
601 .await
602 .unwrap();
603 assert_eq!(
604 *all_desc.downcast::<Vec<(String, i64)>>().unwrap(),
605 vec![
606 ("c".to_string(), 3),
607 ("b".to_string(), 2),
608 ("a".to_string(), 1)
609 ]
610 );
611
612 let bad = q
614 .iq2_execute(&Iq2Query::Key {
615 key: Box::new(7_i64),
616 })
617 .await;
618 assert_eq!(bad.err(), Some(Iq2Failure::KeyTypeMismatch));
619 }
620
621 #[tokio::test]
622 async fn apply_changelog_restores_without_re_logging() {
623 let mut s = store();
624 s.apply_changelog(
625 b"k".to_vec().into(),
626 Some(bytes::Bytes::from_static(&[0, 0, 0, 0, 0, 0, 0, 7])),
627 )
628 .await;
629 check!(s.get(&"k".to_string()).await == Some(7));
630 check!(s.take_changelog().is_empty());
631 s.apply_changelog(b"k".to_vec().into(), None).await;
632 check!(s.get(&"k".to_string()).await == None);
633 }
634
635 #[tokio::test]
636 async fn cached_store_reads_your_writes() {
637 let mut s = cached_store();
638 s.set_record_context(ctx_at(0));
639 s.put("a".into(), 1).await;
640 s.put("a".into(), 2).await;
641 check!(s.get(&"a".to_string()).await == Some(2));
643 let mut buffer = std::collections::VecDeque::new();
645 s.flush_cache_into(&mut buffer, &[0]).await;
646 check!(buffer.len() == 1);
647 }
648
649 #[tokio::test]
650 async fn cached_store_defers_changelog_until_flush() {
651 let mut s = cached_store();
652 s.set_record_context(ctx_at(0));
653 s.put("a".into(), 1).await;
654 s.put("a".into(), 2).await;
655 check!(s.take_changelog().is_empty());
657 let mut buffer = std::collections::VecDeque::new();
659 s.flush_cache_into(&mut buffer, &[0]).await;
660 let cl = s.take_changelog();
661 check!(cl.len() == 1);
662 check!(cl[0].0 == StringSerde.serialize("s-changelog", &"a".to_string()));
663 check!(cl[0].1 == Some(I64Serde.serialize("s-changelog", &2)));
664 }
665
666 #[tokio::test]
667 async fn flush_cache_into_emits_deduped_change() {
668 use crate::dsl::processors::change::Change;
669 let mut s = cached_store();
670 s.set_record_context(ctx_at(0));
672 s.put("a".into(), 1).await;
673 let mut seed = std::collections::VecDeque::new();
674 s.flush_cache_into(&mut seed, &[0]).await;
675 let _ = s.take_changelog();
676
677 s.set_record_context(ctx_at(7));
679 s.put("a".into(), 2).await;
680 s.put("a".into(), 3).await;
681 let mut buffer = std::collections::VecDeque::new();
683 s.flush_cache_into(&mut buffer, &[7]).await;
684 check!(buffer.len() == 1);
685 let (child, rec) = &buffer[0];
686 check!(*child == 7);
687 check!(rec.timestamp == 7);
689 let key = rec.key.as_ref().unwrap().downcast_ref::<String>().unwrap();
690 check!(key == "a");
691 let change = rec.value.downcast_ref::<Change<i64>>().unwrap();
692 check!(change.old == Some(1));
694 check!(change.new == Some(3));
695
696 check!(s.get(&"a".to_string()).await == Some(3));
698 }
699
700 #[tokio::test]
704 async fn cached_store_range_scan_and_count_overlay() {
705 use crate::store::iq::IqQueryable;
706 let mut s = cached_store();
707 s.set_record_context(ctx_at(0));
708 s.put("a".into(), 1).await;
709 s.put("b".into(), 2).await;
710 s.put("c".into(), 3).await;
711
712 let r = s.range(&"a".to_string(), &"c".to_string()).await; check!(r == vec![("a".to_string(), 1), ("b".to_string(), 2)]);
715
716 check!(s.iq_kv_all().await.len() == 3);
718 check!(s.iq_kv_approx_count().await == 3);
719 check!(s.iq_kv_get(b"b").await == Some(I64Serde.serialize("s-changelog", &2)));
721 }
722
723 #[tokio::test]
726 async fn cached_store_delete_returns_prev_and_stages_tombstone() {
727 let mut s = cached_store();
728 s.set_record_context(ctx_at(0));
729 s.put("a".into(), 5).await;
730
731 check!(s.delete(&"a".to_string()).await == Some(5));
734 check!(s.take_changelog().is_empty());
735 check!(s.get(&"a".to_string()).await == None); let mut buffer = std::collections::VecDeque::new();
739 s.flush_cache_into(&mut buffer, &[0]).await;
740 let cl = s.take_changelog();
741 check!(cl.len() == 1);
742 check!(cl[0].1.is_none());
743 }
744
745 #[tokio::test]
749 async fn cached_store_apply_changelog_goes_below_cache() {
750 let mut s = cached_store();
751 s.apply_changelog(
752 b"k".to_vec().into(),
753 Some(bytes::Bytes::from_static(&[0, 0, 0, 0, 0, 0, 0, 7])),
754 )
755 .await;
756 check!(s.get(&"k".to_string()).await == Some(7));
757 let mut buffer = std::collections::VecDeque::new();
759 s.flush_cache_into(&mut buffer, &[0]).await;
760 check!(buffer.is_empty());
761 check!(s.take_changelog().is_empty());
762
763 s.apply_changelog(b"k".to_vec().into(), None).await;
765 check!(s.get(&"k".to_string()).await == None);
766 }
767
768 #[tokio::test]
771 async fn cached_store_clear_empties_everything() {
772 use crate::store::iq::IqQueryable;
773 let mut s = cached_store();
774 s.set_record_context(ctx_at(0));
775 s.put("a".into(), 1).await;
776 StateStore::clear(&mut s).await;
777 check!(s.get(&"a".to_string()).await == None);
778 check!(s.iq_kv_all().await.is_empty());
779 let mut buffer = std::collections::VecDeque::new();
780 s.flush_cache_into(&mut buffer, &[0]).await;
781 check!(buffer.is_empty());
782 }
783
784 #[tokio::test]
787 async fn enable_cache_is_idempotent() {
788 let mut s = store();
789 check!(!s.is_cached());
790 s.enable_cache(Arc::new(Mutex::new(NamedCache::new("s".into()))));
791 check!(s.is_cached());
792 s.enable_cache(Arc::new(Mutex::new(NamedCache::new("s".into()))));
794 check!(s.is_cached());
795 check!(s.is_cached_erased());
796 }
797
798 #[tokio::test]
799 async fn plain_store_unchanged() {
800 let mut s = store();
803 s.put("a".into(), 1).await;
804 check!(s.get(&"a".to_string()).await == Some(1));
805 let cl = s.take_changelog();
806 check!(cl.len() == 1);
807 check!(cl[0].1 == Some(I64Serde.serialize("s-changelog", &1)));
808 let mut buffer = std::collections::VecDeque::new();
810 s.flush_cache_into(&mut buffer, &[0]).await;
811 check!(buffer.is_empty());
812 s.flush().await;
815 s.close();
816 check!(s.get(&"a".to_string()).await == Some(1));
817 }
818}