1use super::{metrics, Error};
4use crate::{
5 journal::contiguous::{variable, Contiguous as _},
6 rmap::RMap,
7 Context,
8};
9use commonware_codec::CodecShared;
10use commonware_macros::boxed;
11use commonware_runtime::{buffer::paged::CacheRef, telemetry::metrics::GaugeExt};
12use std::num::{NonZeroU64, NonZeroUsize};
13use tracing::debug;
14
15#[derive(Clone)]
17pub struct Config<C> {
18 pub partition: String,
20
21 pub items_per_section: NonZeroU64,
26
27 pub compression: Option<u8>,
32
33 pub codec_config: C,
35
36 pub page_cache: CacheRef,
38
39 pub write_buffer: NonZeroUsize,
41}
42
43pub struct Queue<E: Context, V: CodecShared> {
75 journal: variable::Journal<E, V>,
77
78 read_pos: u64,
83
84 ack_floor: u64,
88
89 acked_above: RMap,
94
95 metrics: metrics::Metrics,
97}
98
99impl<E: Context, V: CodecShared> Queue<E, V> {
100 #[boxed]
110 pub async fn init(context: E, cfg: Config<V::Cfg>) -> Result<Self, Error> {
111 let metrics = metrics::Metrics::init(&context);
113
114 let journal = variable::Journal::init(
115 context.child("journal"),
116 variable::Config {
117 partition: cfg.partition,
118 items_per_section: cfg.items_per_section,
119 compression: cfg.compression,
120 codec_config: cfg.codec_config,
121 page_cache: cfg.page_cache,
122 write_buffer: cfg.write_buffer,
123 },
124 )
125 .await?;
126
127 let bounds = journal.bounds();
130 let acked_above = RMap::new();
131
132 debug!(floor = bounds.start, size = bounds.end, "queue initialized");
133
134 let _ = metrics.tip.try_set(bounds.end);
136 let _ = metrics.floor.try_set(bounds.start);
137 let _ = metrics.next.try_set(bounds.start);
138
139 Ok(Self {
140 journal,
141 read_pos: bounds.start,
142 ack_floor: bounds.start,
143 acked_above,
144 metrics,
145 })
146 }
147
148 pub fn is_acked(&self, position: u64) -> bool {
150 position < self.ack_floor || self.acked_above.get(&position).is_some()
151 }
152
153 pub async fn append(&mut self, item: V) -> Result<u64, Error> {
161 let pos = self.journal.append(&item).await?;
162 let _ = self.metrics.tip.try_set(pos + 1);
163 debug!(pos, "appended item");
164 Ok(pos)
165 }
166
167 pub async fn enqueue(&mut self, item: V) -> Result<u64, Error> {
174 let pos = self.append(item).await?;
175 self.commit().await?;
176 Ok(pos)
177 }
178
179 pub async fn dequeue(&mut self) -> Result<Option<(u64, V)>, Error> {
187 let size = self.journal.bounds().end;
188
189 if self.read_pos < self.ack_floor {
191 self.read_pos = self.ack_floor;
192 }
193
194 if let Some((_, end)) = self.acked_above.get(&self.read_pos) {
196 self.read_pos = end.saturating_add(1);
197 }
198
199 let _ = self.metrics.next.try_set(self.read_pos);
201 if self.read_pos >= size {
202 return Ok(None);
203 }
204
205 let item = self.journal.read(self.read_pos).await?;
206 let pos = self.read_pos;
207 self.read_pos += 1;
208 let _ = self.metrics.next.try_set(self.read_pos);
209 debug!(position = pos, "dequeued item");
210 Ok(Some((pos, item)))
211 }
212
213 pub fn ack(&mut self, position: u64) -> Result<(), Error> {
221 let size = self.journal.size();
222 if position >= size {
223 return Err(Error::PositionOutOfRange(position, size));
224 }
225
226 if position < self.ack_floor {
228 return Ok(());
229 }
230
231 if self.acked_above.get(&position).is_some() {
233 return Ok(());
234 }
235
236 if position == self.ack_floor {
238 let next = position + 1;
240 let final_floor = match self.acked_above.get(&next) {
241 Some((_, end)) => end + 1,
242 None => next,
243 };
244 self.acked_above.remove(next, final_floor - 1);
245 self.ack_floor = final_floor;
246 let _ = self.metrics.floor.try_set(self.ack_floor);
247 debug!(floor = self.ack_floor, "advanced ack floor");
248 } else {
249 self.acked_above.insert(position);
251 debug!(position, "acked item above floor");
252 }
253 Ok(())
254 }
255
256 pub fn ack_up_to(&mut self, up_to: u64) -> Result<(), Error> {
263 let size = self.journal.size();
264 if up_to > size {
265 return Err(Error::PositionOutOfRange(up_to, size));
266 }
267
268 if up_to <= self.ack_floor {
270 return Ok(());
271 }
272
273 let final_floor = match self.acked_above.get(&up_to) {
275 Some((_, end)) => end + 1,
276 None => up_to,
277 };
278
279 self.acked_above.remove(self.ack_floor, final_floor - 1);
281 self.ack_floor = final_floor;
282 let _ = self.metrics.floor.try_set(self.ack_floor);
283 debug!(floor = self.ack_floor, "batch acked up to");
284 Ok(())
285 }
286
287 pub const fn read_position(&self) -> u64 {
291 self.read_pos
292 }
293
294 pub const fn ack_floor(&self) -> u64 {
298 self.ack_floor
299 }
300
301 pub const fn size(&self) -> u64 {
306 self.journal.size()
307 }
308
309 pub const fn is_empty(&self) -> bool {
311 self.ack_floor >= self.journal.size()
314 }
315
316 pub fn reset(&mut self) {
319 let old_pos = self.read_pos;
320 self.read_pos = self.ack_floor;
321 let _ = self.metrics.next.try_set(self.read_pos);
322 debug!(
323 old_read_pos = old_pos,
324 new_read_pos = self.read_pos,
325 "reset read position"
326 );
327 }
328
329 #[cfg(test)]
331 pub(crate) const fn pending(&self) -> u64 {
332 self.journal.size().saturating_sub(self.read_pos)
333 }
334
335 pub async fn commit(&mut self) -> Result<(), Error> {
340 self.journal.commit().await?;
341 Ok(())
342 }
343
344 pub async fn sync(&mut self) -> Result<(), Error> {
349 self.journal.sync().await?;
350 self.journal.prune(self.ack_floor).await?;
351 Ok(())
352 }
353
354 #[boxed]
356 pub async fn destroy(self) -> Result<(), Error> {
357 self.journal.destroy().await?;
358 Ok(())
359 }
360}
361
362#[cfg(test)]
363mod tests {
364 use super::*;
365 use commonware_codec::RangeCfg;
366 use commonware_macros::test_traced;
367 use commonware_runtime::{
368 buffer::paged::CacheRef, deterministic, BufferPooler, Metrics as _, Runner, Supervisor as _,
369 };
370 use commonware_utils::{NZUsize, NZU16, NZU64};
371 use std::num::NonZeroU16;
372
373 const PAGE_SIZE: NonZeroU16 = NZU16!(1024);
374 const PAGE_CACHE_SIZE: NonZeroUsize = NZUsize!(10);
375
376 fn test_config(partition: &str, pooler: &impl BufferPooler) -> Config<(RangeCfg<usize>, ())> {
377 Config {
378 partition: partition.into(),
379 items_per_section: NZU64!(10),
380 compression: None,
381 codec_config: ((0..).into(), ()),
382 page_cache: CacheRef::from_pooler(pooler, PAGE_SIZE, PAGE_CACHE_SIZE),
383 write_buffer: NZUsize!(4096),
384 }
385 }
386
387 fn acked_above_count<E: Context, V: CodecShared>(queue: &Queue<E, V>) -> usize {
388 queue
389 .acked_above
390 .iter()
391 .map(|(&s, &e)| (e - s + 1) as usize)
392 .sum()
393 }
394
395 #[test_traced]
396 fn test_basic_enqueue_dequeue() {
397 let executor = deterministic::Runner::default();
398 executor.start(|context| async move {
399 let cfg = test_config("test_basic", &context);
400 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
401 .await
402 .unwrap();
403
404 assert!(queue.is_empty());
406 assert_eq!(queue.pending(), 0);
407 assert_eq!(queue.size(), 0);
408
409 let pos0 = queue.enqueue(b"item0".to_vec()).await.unwrap();
411 let pos1 = queue.enqueue(b"item1".to_vec()).await.unwrap();
412 let pos2 = queue.enqueue(b"item2".to_vec()).await.unwrap();
413
414 assert_eq!(pos0, 0);
415 assert_eq!(pos1, 1);
416 assert_eq!(pos2, 2);
417 assert_eq!(queue.size(), 3);
418 assert_eq!(queue.pending(), 3);
419 assert!(!queue.is_empty());
420
421 let (p, item) = queue.dequeue().await.unwrap().unwrap();
423 assert_eq!(p, 0);
424 assert_eq!(item, b"item0");
425 assert_eq!(queue.pending(), 2);
426
427 let (p, item) = queue.dequeue().await.unwrap().unwrap();
428 assert_eq!(p, 1);
429 assert_eq!(item, b"item1");
430 assert_eq!(queue.pending(), 1);
431
432 let (p, item) = queue.dequeue().await.unwrap().unwrap();
433 assert_eq!(p, 2);
434 assert_eq!(item, b"item2");
435 assert_eq!(queue.pending(), 0);
436
437 assert!(!queue.is_empty());
439 assert!(queue.dequeue().await.unwrap().is_none());
440 });
441 }
442
443 #[test_traced]
444 fn test_append_commit_batch() {
445 let executor = deterministic::Runner::default();
446 executor.start(|context| async move {
447 let cfg = test_config("test_batch", &context);
448 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
449 .await
450 .unwrap();
451
452 for i in 0..5u8 {
454 queue.append(vec![i]).await.unwrap();
455 }
456 queue.commit().await.unwrap();
457 assert_eq!(queue.size(), 5);
458
459 for i in 0..5 {
461 let (pos, item) = queue.dequeue().await.unwrap().unwrap();
462 assert_eq!(pos, i);
463 assert_eq!(item, vec![i as u8]);
464 }
465
466 for i in 5..8u8 {
468 queue.append(vec![i]).await.unwrap();
469 }
470 queue.commit().await.unwrap();
471 queue.enqueue(vec![8]).await.unwrap();
472 assert_eq!(queue.size(), 9);
473
474 queue.ack_up_to(9).unwrap();
475 assert!(queue.is_empty());
476 });
477 }
478
479 #[test_traced]
480 fn test_append_commit_persistence() {
481 let executor = deterministic::Runner::default();
482 executor.start(|context| async move {
483 let cfg = test_config("test_batch_persist", &context);
484
485 {
486 let mut queue = Queue::<_, Vec<u8>>::init(context.child("first"), cfg.clone())
487 .await
488 .unwrap();
489 for i in 0..4u8 {
490 queue.append(vec![i]).await.unwrap();
491 }
492 queue.commit().await.unwrap();
493 queue.sync().await.unwrap();
494 }
495
496 {
497 let mut queue = Queue::<_, Vec<u8>>::init(context.child("second"), cfg)
498 .await
499 .unwrap();
500 assert_eq!(queue.size(), 4);
501 for i in 0..4 {
502 let (pos, item) = queue.dequeue().await.unwrap().unwrap();
503 assert_eq!(pos, i);
504 assert_eq!(item, vec![i as u8]);
505 }
506 }
507 });
508 }
509
510 #[test_traced]
511 fn test_commit_after_sync_recovers_without_second_sync() {
512 let executor = deterministic::Runner::default();
513 executor.start(|context| async move {
514 let cfg = test_config("test_commit_after_sync_recovery", &context);
515
516 {
517 let mut queue = Queue::<_, Vec<u8>>::init(context.child("first"), cfg.clone())
518 .await
519 .unwrap();
520
521 queue.append(b"synced".to_vec()).await.unwrap();
523 queue.commit().await.unwrap();
524 queue.sync().await.unwrap();
525
526 queue.append(b"committed-a".to_vec()).await.unwrap();
528 queue.append(b"committed-b".to_vec()).await.unwrap();
529 queue.commit().await.unwrap();
530 }
531
532 let mut queue = Queue::<_, Vec<u8>>::init(context.child("second"), cfg)
533 .await
534 .unwrap();
535 assert_eq!(queue.size(), 3);
536 for (expected_pos, expected_item) in [
537 (0, b"synced".to_vec()),
538 (1, b"committed-a".to_vec()),
539 (2, b"committed-b".to_vec()),
540 ] {
541 let (pos, item) = queue.dequeue().await.unwrap().unwrap();
542 assert_eq!(pos, expected_pos);
543 assert_eq!(item, expected_item);
544 }
545
546 queue.destroy().await.unwrap();
547 });
548 }
549
550 #[test_traced]
551 fn test_sequential_ack() {
552 let executor = deterministic::Runner::default();
553 executor.start(|context| async move {
554 let cfg = test_config("test_seq_ack", &context);
555 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
556 .await
557 .unwrap();
558
559 for i in 0..5u8 {
561 queue.enqueue(vec![i]).await.unwrap();
562 }
563
564 for i in 0..5 {
566 let (pos, _) = queue.dequeue().await.unwrap().unwrap();
567 assert_eq!(pos, i);
568 queue.ack(pos).unwrap();
569 assert_eq!(queue.ack_floor(), i + 1);
570 }
571
572 assert!(queue.is_empty());
574 assert_eq!(queue.ack_floor(), 5);
575 });
576 }
577
578 #[test_traced]
579 fn test_out_of_order_ack() {
580 let executor = deterministic::Runner::default();
581 executor.start(|context| async move {
582 let cfg = test_config("test_ooo_ack", &context);
583 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
584 .await
585 .unwrap();
586
587 for i in 0..5u8 {
589 queue.enqueue(vec![i]).await.unwrap();
590 }
591
592 for _ in 0..5 {
594 queue.dequeue().await.unwrap();
595 }
596
597 queue.ack(2).unwrap();
599 assert_eq!(queue.ack_floor(), 0); assert!(queue.is_acked(2));
601
602 queue.ack(4).unwrap();
603 assert_eq!(queue.ack_floor(), 0);
604 assert!(queue.is_acked(4));
605
606 queue.ack(1).unwrap();
607 assert_eq!(queue.ack_floor(), 0);
608
609 queue.ack(3).unwrap();
610 assert_eq!(queue.ack_floor(), 0);
611
612 queue.ack(0).unwrap();
614 assert_eq!(queue.ack_floor(), 5);
615 assert!(queue.is_empty());
616 });
617 }
618
619 #[test_traced]
620 fn test_ack_up_to() {
621 let executor = deterministic::Runner::default();
622 executor.start(|context| async move {
623 let cfg = test_config("test_ack_up_to", &context);
624 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
625 .await
626 .unwrap();
627
628 for i in 0..10u8 {
630 queue.enqueue(vec![i]).await.unwrap();
631 }
632
633 queue.ack_up_to(5).unwrap();
635 assert_eq!(queue.ack_floor(), 5);
636
637 for i in 0..5 {
639 assert!(queue.is_acked(i));
640 }
641 for i in 5..10 {
643 assert!(!queue.is_acked(i));
644 }
645
646 let (p, _) = queue.dequeue().await.unwrap().unwrap();
648 assert_eq!(p, 5);
649 });
650 }
651
652 #[test_traced]
653 fn test_ack_up_to_with_existing_acks() {
654 let executor = deterministic::Runner::default();
655 executor.start(|context| async move {
656 let cfg = test_config("test_ack_up_to_existing", &context);
657 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
658 .await
659 .unwrap();
660
661 for i in 0..10u8 {
663 queue.enqueue(vec![i]).await.unwrap();
664 }
665
666 queue.ack(7).unwrap();
668 queue.ack(8).unwrap();
669 assert_eq!(acked_above_count(&queue), 2);
670
671 queue.ack_up_to(5).unwrap();
673 assert_eq!(queue.ack_floor(), 5);
674 assert_eq!(acked_above_count(&queue), 2);
675
676 queue.ack_up_to(9).unwrap();
678 assert_eq!(queue.ack_floor(), 9);
679 assert_eq!(acked_above_count(&queue), 0);
680 });
681 }
682
683 #[test_traced]
684 fn test_ack_up_to_coalesces_with_acked_above() {
685 let executor = deterministic::Runner::default();
686 executor.start(|context| async move {
687 let cfg = test_config("test_ack_up_to_coalesce", &context);
688 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
689 .await
690 .unwrap();
691
692 for i in 0..10u8 {
694 queue.enqueue(vec![i]).await.unwrap();
695 }
696
697 queue.ack(5).unwrap();
699 queue.ack(6).unwrap();
700 queue.ack(7).unwrap();
701 assert_eq!(queue.ack_floor(), 0);
702
703 queue.ack_up_to(5).unwrap();
705 assert_eq!(queue.ack_floor(), 8); });
707 }
708
709 #[test_traced]
710 fn test_ack_up_to_errors() {
711 let executor = deterministic::Runner::default();
712 executor.start(|context| async move {
713 let cfg = test_config("test_ack_up_to_errors", &context);
714 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
715 .await
716 .unwrap();
717
718 queue.enqueue(b"item0".to_vec()).await.unwrap();
719 queue.enqueue(b"item1".to_vec()).await.unwrap();
720
721 let err = queue.ack_up_to(5).unwrap_err();
723 assert!(matches!(err, Error::PositionOutOfRange(5, 2)));
724
725 queue.ack_up_to(2).unwrap();
727 assert_eq!(queue.ack_floor(), 2);
728
729 queue.ack_up_to(1).unwrap();
731 assert_eq!(queue.ack_floor(), 2);
732 });
733 }
734
735 #[test_traced]
736 fn test_dequeue_skips_acked() {
737 let executor = deterministic::Runner::default();
738 executor.start(|context| async move {
739 let cfg = test_config("test_skip_acked", &context);
740 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
741 .await
742 .unwrap();
743
744 for i in 0..5u8 {
746 queue.enqueue(vec![i]).await.unwrap();
747 }
748
749 queue.ack(1).unwrap();
751 queue.ack(3).unwrap();
752
753 let (p, item) = queue.dequeue().await.unwrap().unwrap();
755 assert_eq!(p, 0);
756 assert_eq!(item, vec![0]);
757
758 let (p, item) = queue.dequeue().await.unwrap().unwrap();
759 assert_eq!(p, 2); assert_eq!(item, vec![2]);
761
762 let (p, item) = queue.dequeue().await.unwrap().unwrap();
763 assert_eq!(p, 4); assert_eq!(item, vec![4]);
765
766 assert!(queue.dequeue().await.unwrap().is_none());
767 });
768 }
769
770 #[test_traced]
771 fn test_ack_errors() {
772 let executor = deterministic::Runner::default();
773 executor.start(|context| async move {
774 let cfg = test_config("test_ack_errors", &context);
775 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
776 .await
777 .unwrap();
778
779 queue.enqueue(b"item0".to_vec()).await.unwrap();
780 queue.enqueue(b"item1".to_vec()).await.unwrap();
781
782 let err = queue.ack(5).unwrap_err();
784 assert!(matches!(err, Error::PositionOutOfRange(5, 2)));
785
786 queue.ack(1).unwrap();
788 assert!(queue.is_acked(1));
789
790 queue.ack(1).unwrap();
792 });
793 }
794
795 #[test_traced]
796 fn test_prune() {
797 let executor = deterministic::Runner::default();
798 executor.start(|context| async move {
799 let cfg = test_config("test_prune", &context);
800 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
801 .await
802 .unwrap();
803
804 for i in 0..25u8 {
806 queue.enqueue(vec![i]).await.unwrap();
807 }
808 queue.sync().await.unwrap();
809
810 for i in 0..15 {
812 queue.dequeue().await.unwrap();
813 queue.ack(i).unwrap();
814 }
815 assert_eq!(queue.ack_floor(), 15);
816
817 let (p, item) = queue.dequeue().await.unwrap().unwrap();
819 assert_eq!(p, 15);
820 assert_eq!(item, vec![15]);
821 });
822 }
823
824 #[test_traced]
825 fn test_ack_across_sections() {
826 let executor = deterministic::Runner::default();
827 executor.start(|context| async move {
828 let cfg = test_config("test_multi_prune", &context);
829 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
830 .await
831 .unwrap();
832
833 for i in 0..50u8 {
835 queue.enqueue(vec![i]).await.unwrap();
836 }
837 queue.sync().await.unwrap();
838
839 for i in 0..15 {
841 queue.dequeue().await.unwrap();
842 queue.ack(i).unwrap();
843 }
844 assert_eq!(queue.ack_floor(), 15);
845
846 let (p, item) = queue.dequeue().await.unwrap().unwrap();
848 assert_eq!(p, 15);
849 assert_eq!(item, vec![15]);
850
851 queue.ack(15).unwrap();
853 for i in 16..30 {
854 queue.dequeue().await.unwrap();
855 queue.ack(i).unwrap();
856 }
857 assert_eq!(queue.ack_floor(), 30);
858
859 let (p, item) = queue.dequeue().await.unwrap().unwrap();
861 assert_eq!(p, 30);
862 assert_eq!(item, vec![30]);
863
864 queue.ack(30).unwrap();
866 for i in 31..50 {
867 queue.dequeue().await.unwrap();
868 queue.ack(i).unwrap();
869 }
870 assert_eq!(queue.ack_floor(), 50);
871
872 assert!(queue.is_empty());
874 assert!(queue.dequeue().await.unwrap().is_none());
875 });
876 }
877
878 #[test_traced]
879 fn test_crash_recovery_replays_from_pruning_boundary() {
880 let executor = deterministic::Runner::default();
882 executor.start(|context| async move {
883 let cfg = test_config("test_recovery_replay", &context);
884
885 {
887 let mut queue = Queue::<_, Vec<u8>>::init(context.child("first"), cfg.clone())
888 .await
889 .unwrap();
890
891 for i in 0..5u8 {
892 queue.enqueue(vec![i]).await.unwrap();
893 }
894
895 queue.ack(0).unwrap();
897 queue.ack(1).unwrap();
898 queue.ack(2).unwrap();
899 assert_eq!(queue.ack_floor(), 3);
900
901 queue.sync().await.unwrap();
902 }
903
904 {
906 let mut queue = Queue::<_, Vec<u8>>::init(context.child("second"), cfg.clone())
907 .await
908 .unwrap();
909
910 assert_eq!(queue.ack_floor(), 0);
912
913 for i in 0..5 {
915 let (p, _) = queue.dequeue().await.unwrap().unwrap();
916 assert_eq!(p, i);
917 }
918 }
919 });
920 }
921
922 #[test_traced]
923 fn test_crash_recovery_with_pruning() {
924 let executor = deterministic::Runner::default();
926 executor.start(|context| async move {
927 let cfg = test_config("test_recovery_pruned", &context);
928
929 let expected_pruning_boundary = {
931 let mut queue = Queue::<_, Vec<u8>>::init(context.child("first"), cfg.clone())
932 .await
933 .unwrap();
934
935 for i in 0..25u8 {
937 queue.enqueue(vec![i]).await.unwrap();
938 }
939
940 for i in 0..15 {
942 queue.ack(i).unwrap();
943 }
944 assert_eq!(queue.ack_floor(), 15);
945
946 queue.sync().await.unwrap();
948
949 let pruning_boundary = queue.journal.bounds().start;
951 assert!(pruning_boundary > 0, "expected some pruning to occur");
952
953 pruning_boundary
954 };
955
956 {
958 let mut queue = Queue::<_, Vec<u8>>::init(context.child("second"), cfg.clone())
959 .await
960 .unwrap();
961
962 let pruning_boundary = queue.journal.bounds().start;
964 assert_eq!(queue.ack_floor(), pruning_boundary);
965 assert_eq!(pruning_boundary, expected_pruning_boundary);
966
967 for i in pruning_boundary..25 {
969 let (p, item) = queue.dequeue().await.unwrap().unwrap();
970 assert_eq!(p, i);
971 assert_eq!(item, vec![i as u8]);
972 }
973
974 assert!(queue.dequeue().await.unwrap().is_none());
975 }
976 });
977 }
978
979 #[test_traced]
980 fn test_reset() {
981 let executor = deterministic::Runner::default();
982 executor.start(|context| async move {
983 let cfg = test_config("test_reset", &context);
984 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
985 .await
986 .unwrap();
987
988 for i in 0..5u8 {
990 queue.enqueue(vec![i]).await.unwrap();
991 }
992
993 queue.dequeue().await.unwrap();
995 queue.dequeue().await.unwrap();
996 queue.dequeue().await.unwrap();
997 assert_eq!(queue.read_position(), 3);
998
999 queue.reset();
1001 assert_eq!(queue.read_position(), 0);
1002
1003 let (p, item) = queue.dequeue().await.unwrap().unwrap();
1005 assert_eq!(p, 0);
1006 assert_eq!(item, vec![0]);
1007 });
1008 }
1009
1010 #[test_traced]
1011 fn test_reset_with_ack() {
1012 let executor = deterministic::Runner::default();
1013 executor.start(|context| async move {
1014 let cfg = test_config("test_reset_ack", &context);
1015 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
1016 .await
1017 .unwrap();
1018
1019 for i in 0..10u8 {
1021 queue.enqueue(vec![i]).await.unwrap();
1022 }
1023
1024 for i in 0..5 {
1026 queue.dequeue().await.unwrap();
1027 queue.ack(i).unwrap();
1028 }
1029 assert_eq!(queue.ack_floor(), 5);
1030 assert_eq!(queue.read_position(), 5);
1031
1032 queue.dequeue().await.unwrap();
1034 queue.dequeue().await.unwrap();
1035 assert_eq!(queue.read_position(), 7);
1036
1037 queue.reset();
1039 assert_eq!(queue.read_position(), 5);
1040
1041 let (p, item) = queue.dequeue().await.unwrap().unwrap();
1043 assert_eq!(p, 5);
1044 assert_eq!(item, vec![5]);
1045 });
1046 }
1047
1048 #[test_traced]
1049 fn test_empty_queue_operations() {
1050 let executor = deterministic::Runner::default();
1051 executor.start(|context| async move {
1052 let cfg = test_config("test_empty", &context);
1053 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
1054 .await
1055 .unwrap();
1056
1057 assert!(queue.is_empty());
1059 assert!(queue.dequeue().await.unwrap().is_none());
1060 queue.sync().await.unwrap();
1061 queue.reset();
1062 });
1063 }
1064
1065 #[test_traced]
1066 fn test_persistence() {
1067 let executor = deterministic::Runner::default();
1068 executor.start(|context| async move {
1069 let cfg = test_config("test_persist", &context);
1070
1071 {
1073 let mut queue = Queue::<_, Vec<u8>>::init(context.child("first"), cfg.clone())
1074 .await
1075 .unwrap();
1076
1077 queue.enqueue(b"item0".to_vec()).await.unwrap();
1078 queue.enqueue(b"item1".to_vec()).await.unwrap();
1079 queue.sync().await.unwrap();
1080 }
1081
1082 {
1084 let mut queue = Queue::<_, Vec<u8>>::init(context.child("second"), cfg.clone())
1085 .await
1086 .unwrap();
1087
1088 assert_eq!(queue.size(), 2);
1089
1090 let (_, item) = queue.dequeue().await.unwrap().unwrap();
1091 assert_eq!(item, b"item0");
1092
1093 let (_, item) = queue.dequeue().await.unwrap().unwrap();
1094 assert_eq!(item, b"item1");
1095 }
1096 });
1097 }
1098
1099 #[test_traced]
1100 fn test_large_queue_with_sparse_acks() {
1101 let executor = deterministic::Runner::default();
1102 executor.start(|context| async move {
1103 let cfg = test_config("test_sparse", &context);
1104 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
1105 .await
1106 .unwrap();
1107
1108 for i in 0..100u8 {
1110 queue.enqueue(vec![i]).await.unwrap();
1111 }
1112
1113 for i in (0..100).step_by(3) {
1115 queue.ack(i).unwrap();
1116 }
1117
1118 let mut received = Vec::new();
1120 while let Some((pos, _)) = queue.dequeue().await.unwrap() {
1121 received.push(pos);
1122 }
1123
1124 let expected: Vec<u64> = (0..100).filter(|x| x % 3 != 0).collect();
1126 assert_eq!(received, expected);
1127 });
1128 }
1129
1130 #[test_traced]
1131 fn test_acked_above_coalescing() {
1132 let executor = deterministic::Runner::default();
1133 executor.start(|context| async move {
1134 let cfg = test_config("test_coalesce", &context);
1135 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
1136 .await
1137 .unwrap();
1138
1139 for i in 0..10u8 {
1141 queue.enqueue(vec![i]).await.unwrap();
1142 }
1143
1144 for i in 1..9 {
1146 queue.ack(i).unwrap();
1147 }
1148
1149 assert_eq!(queue.ack_floor(), 0);
1151 assert!(acked_above_count(&queue) > 0);
1152
1153 queue.ack(0).unwrap();
1155 assert_eq!(queue.ack_floor(), 9);
1156 assert_eq!(acked_above_count(&queue), 0);
1157 });
1158 }
1159
1160 #[test_traced]
1161 fn test_ack_up_to_past_read_pos() {
1162 let executor = deterministic::Runner::default();
1163 executor.start(|context| async move {
1164 let cfg = test_config("test_ack_up_to_past_read_pos", &context);
1165 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
1166 .await
1167 .unwrap();
1168
1169 for i in 0..10u8 {
1170 queue.enqueue(vec![i]).await.unwrap();
1171 }
1172
1173 for _ in 0..3 {
1175 queue.dequeue().await.unwrap();
1176 }
1177 assert_eq!(queue.read_position(), 3);
1178
1179 queue.ack_up_to(7).unwrap();
1181 assert_eq!(queue.ack_floor(), 7);
1182
1183 let (pos, item) = queue.dequeue().await.unwrap().unwrap();
1185 assert_eq!(pos, 7);
1186 assert_eq!(item, vec![7]);
1187 });
1188 }
1189
1190 #[test_traced]
1191 fn test_metrics() {
1192 let executor = deterministic::Runner::default();
1193 executor.start(|context| async move {
1194 let cfg = test_config("test-metrics", &context);
1195 let ctx = context.child("test_metrics");
1196 let mut queue = Queue::<_, Vec<u8>>::init(ctx, cfg).await.unwrap();
1197
1198 let encoded = context.encode();
1199 assert!(
1200 encoded.contains("test_metrics_tip 0"),
1201 "expected tip 0: {encoded}"
1202 );
1203 assert!(
1204 encoded.contains("test_metrics_floor 0"),
1205 "expected floor 0: {encoded}"
1206 );
1207 assert!(
1208 encoded.contains("test_metrics_next 0"),
1209 "expected next 0: {encoded}"
1210 );
1211
1212 queue.append(vec![0]).await.unwrap();
1214 let encoded = context.encode();
1215 assert!(
1216 encoded.contains("test_metrics_tip 1"),
1217 "expected tip 1: {encoded}"
1218 );
1219 queue.commit().await.unwrap();
1220
1221 for i in 1..10u8 {
1223 queue.enqueue(vec![i]).await.unwrap();
1224 }
1225 let encoded = context.encode();
1226 assert!(
1227 encoded.contains("test_metrics_tip 10"),
1228 "expected tip 10: {encoded}"
1229 );
1230
1231 queue.dequeue().await.unwrap();
1233 queue.dequeue().await.unwrap();
1234 let encoded = context.encode();
1235 assert!(
1236 encoded.contains("test_metrics_next 2"),
1237 "expected next 2: {encoded}"
1238 );
1239
1240 queue.ack(0).unwrap();
1242 queue.ack(1).unwrap();
1243 let encoded = context.encode();
1244 assert!(
1245 encoded.contains("test_metrics_floor 2"),
1246 "expected floor 2: {encoded}"
1247 );
1248
1249 queue.ack(4).unwrap();
1251 queue.ack(6).unwrap();
1252 let encoded = context.encode();
1253 assert!(
1254 encoded.contains("test_metrics_floor 2"),
1255 "expected floor still 2: {encoded}"
1256 );
1257
1258 queue.ack(2).unwrap();
1260 queue.ack(3).unwrap();
1261 let encoded = context.encode();
1262 assert!(
1263 encoded.contains("test_metrics_floor 5"),
1264 "expected floor 5: {encoded}"
1265 );
1266
1267 queue.ack_up_to(8).unwrap();
1269 let encoded = context.encode();
1270 assert!(
1271 encoded.contains("test_metrics_floor 8"),
1272 "expected floor 8: {encoded}"
1273 );
1274
1275 queue.ack(8).unwrap();
1277 queue.ack(9).unwrap();
1278 let encoded = context.encode();
1279 assert!(
1280 encoded.contains("test_metrics_floor 10"),
1281 "expected floor 10: {encoded}"
1282 );
1283
1284 queue.reset();
1286 let encoded = context.encode();
1287 assert!(
1288 encoded.contains("test_metrics_next 10"),
1289 "expected next 10: {encoded}"
1290 );
1291 });
1292 }
1293
1294 #[test_traced]
1295 fn test_metrics_next_updates_on_fast_forward() {
1296 let executor = deterministic::Runner::default();
1297 executor.start(|context| async move {
1298 let cfg = test_config("test-ff", &context);
1299 let ctx = context.child("test_ff");
1300 let mut queue = Queue::<_, Vec<u8>>::init(ctx, cfg).await.unwrap();
1301
1302 for i in 0..3u8 {
1304 queue.enqueue(vec![i]).await.unwrap();
1305 }
1306 let (pos, _) = queue.dequeue().await.unwrap().unwrap();
1307 queue.ack(pos).unwrap();
1308
1309 let encoded = context.encode();
1310 assert!(
1311 encoded.contains("test_ff_next 1"),
1312 "expected next 1: {encoded}"
1313 );
1314
1315 queue.ack(2).unwrap();
1317 queue.ack(1).unwrap();
1318 assert_eq!(queue.ack_floor(), 3);
1319
1320 let encoded = context.encode();
1322 assert!(
1323 encoded.contains("test_ff_next 1"),
1324 "expected next still 1: {encoded}"
1325 );
1326
1327 assert!(queue.dequeue().await.unwrap().is_none());
1329 let encoded = context.encode();
1330 assert!(
1331 encoded.contains("test_ff_next 3"),
1332 "expected next 3 after fast-forward: {encoded}"
1333 );
1334 });
1335 }
1336}