1use super::{Error, metrics};
4use crate::{
5 Context,
6 journal::contiguous::{Contiguous as _, variable},
7 rmap::RMap,
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 pub replay_buffer: NonZeroUsize,
44}
45
46pub struct Queue<E: Context, V: CodecShared> {
81 journal: variable::Journal<E, V>,
83
84 read_pos: u64,
89
90 ack_floor: u64,
94
95 acked_above: RMap,
100
101 metrics: metrics::Metrics,
103}
104
105impl<E: Context, V: CodecShared> Queue<E, V> {
106 #[boxed]
116 pub async fn init(context: E, cfg: Config<V::Cfg>) -> Result<Self, Error> {
117 let metrics = metrics::Metrics::init(&context);
119
120 let journal = variable::Journal::init(
121 context.child("journal"),
122 variable::Config {
123 partition: cfg.partition,
124 items_per_section: cfg.items_per_section,
125 compression: cfg.compression,
126 codec_config: cfg.codec_config,
127 page_cache: cfg.page_cache,
128 write_buffer: cfg.write_buffer,
129 replay_buffer: cfg.replay_buffer,
130 },
131 )
132 .await?;
133
134 let bounds = journal.bounds();
137 let acked_above = RMap::new();
138
139 debug!(floor = bounds.start, size = bounds.end, "queue initialized");
140
141 let _ = metrics.tip.try_set(bounds.end);
143 let _ = metrics.floor.try_set(bounds.start);
144 let _ = metrics.next.try_set(bounds.start);
145
146 Ok(Self {
147 journal,
148 read_pos: bounds.start,
149 ack_floor: bounds.start,
150 acked_above,
151 metrics,
152 })
153 }
154
155 pub fn is_acked(&self, position: u64) -> bool {
157 position < self.ack_floor || self.acked_above.get(&position).is_some()
158 }
159
160 pub async fn append(mut self, item: V) -> Result<(Self, u64), Error> {
168 let pos;
169 (self.journal, pos) = self.journal.append(&item).await?;
170 let _ = self.metrics.tip.try_set(pos + 1);
171 debug!(pos, "appended item");
172 Ok((self, pos))
173 }
174
175 pub async fn enqueue(self, item: V) -> Result<(Self, u64), Error> {
182 let (queue, pos) = self.append(item).await?;
183 let queue = queue.commit().await?;
184 Ok((queue, pos))
185 }
186
187 pub async fn dequeue(&mut self) -> Result<Option<(u64, V)>, Error> {
195 let size = self.journal.bounds().end;
196
197 if self.read_pos < self.ack_floor {
199 self.read_pos = self.ack_floor;
200 }
201
202 if let Some((_, end)) = self.acked_above.get(&self.read_pos) {
204 self.read_pos = end.saturating_add(1);
205 }
206
207 let _ = self.metrics.next.try_set(self.read_pos);
209 if self.read_pos >= size {
210 return Ok(None);
211 }
212
213 let item = self.journal.read(self.read_pos).await?;
214 let pos = self.read_pos;
215 self.read_pos += 1;
216 let _ = self.metrics.next.try_set(self.read_pos);
217 debug!(position = pos, "dequeued item");
218 Ok(Some((pos, item)))
219 }
220
221 pub fn ack(&mut self, position: u64) -> Result<(), Error> {
229 let size = self.journal.size();
230 if position >= size {
231 return Err(Error::PositionOutOfRange(position, size));
232 }
233
234 if position < self.ack_floor {
236 return Ok(());
237 }
238
239 if self.acked_above.get(&position).is_some() {
241 return Ok(());
242 }
243
244 if position == self.ack_floor {
246 let next = position + 1;
248 let final_floor = match self.acked_above.get(&next) {
249 Some((_, end)) => end + 1,
250 None => next,
251 };
252 self.acked_above.remove(next, final_floor - 1);
253 self.ack_floor = final_floor;
254 let _ = self.metrics.floor.try_set(self.ack_floor);
255 debug!(floor = self.ack_floor, "advanced ack floor");
256 } else {
257 self.acked_above.insert(position);
259 debug!(position, "acked item above floor");
260 }
261 Ok(())
262 }
263
264 pub fn ack_up_to(&mut self, up_to: u64) -> Result<(), Error> {
271 let size = self.journal.size();
272 if up_to > size {
273 return Err(Error::PositionOutOfRange(up_to, size));
274 }
275
276 if up_to <= self.ack_floor {
278 return Ok(());
279 }
280
281 let final_floor = match self.acked_above.get(&up_to) {
283 Some((_, end)) => end + 1,
284 None => up_to,
285 };
286
287 self.acked_above.remove(self.ack_floor, final_floor - 1);
289 self.ack_floor = final_floor;
290 let _ = self.metrics.floor.try_set(self.ack_floor);
291 debug!(floor = self.ack_floor, "batch acked up to");
292 Ok(())
293 }
294
295 pub const fn read_position(&self) -> u64 {
299 self.read_pos
300 }
301
302 pub const fn ack_floor(&self) -> u64 {
306 self.ack_floor
307 }
308
309 pub fn size(&self) -> u64 {
314 self.journal.size()
315 }
316
317 pub fn is_empty(&self) -> bool {
319 self.ack_floor >= self.journal.size()
322 }
323
324 pub fn reset(&mut self) {
327 let old_pos = self.read_pos;
328 self.read_pos = self.ack_floor;
329 let _ = self.metrics.next.try_set(self.read_pos);
330 debug!(
331 old_read_pos = old_pos,
332 new_read_pos = self.read_pos,
333 "reset read position"
334 );
335 }
336
337 #[cfg(test)]
339 fn pending(&self) -> u64 {
340 self.journal.size().saturating_sub(self.read_pos)
341 }
342
343 pub async fn commit(mut self) -> Result<Self, Error> {
348 self.journal = self.journal.commit().await?;
349 Ok(self)
350 }
351
352 pub async fn sync(mut self) -> Result<Self, Error> {
357 self.journal = self.journal.sync().await?;
358 (self.journal, _) = self.journal.prune(self.ack_floor).await?;
359 Ok(self)
360 }
361
362 #[boxed]
364 pub async fn destroy(self) -> Result<(), Error> {
365 self.journal.destroy().await?;
366 Ok(())
367 }
368}
369
370impl<E: Context, V: CodecShared> std::fmt::Debug for Queue<E, V> {
371 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
372 f.debug_struct("Queue")
373 .field("size", &self.size())
374 .field("ack_floor", &self.ack_floor())
375 .finish_non_exhaustive()
376 }
377}
378
379#[cfg(test)]
380mod tests {
381 use super::*;
382 use commonware_codec::RangeCfg;
383 use commonware_macros::test_traced;
384 use commonware_runtime::{
385 BufferPooler, Metrics as _, Runner, Supervisor as _, buffer::paged::CacheRef, deterministic,
386 };
387 use commonware_utils::{NZU16, NZU64, NZUsize};
388 use std::num::NonZeroU16;
389
390 const PAGE_SIZE: NonZeroU16 = NZU16!(1024);
391 const PAGE_CACHE_SIZE: NonZeroUsize = NZUsize!(10);
392
393 fn test_config(partition: &str, pooler: &impl BufferPooler) -> Config<(RangeCfg<usize>, ())> {
394 Config {
395 partition: partition.into(),
396 items_per_section: NZU64!(10),
397 compression: None,
398 codec_config: ((0..).into(), ()),
399 page_cache: CacheRef::from_pooler(pooler, PAGE_SIZE, PAGE_CACHE_SIZE),
400 write_buffer: NZUsize!(4096),
401 replay_buffer: NZUsize!(4096),
402 }
403 }
404
405 fn acked_above_count<E: Context, V: CodecShared>(queue: &Queue<E, V>) -> usize {
406 queue
407 .acked_above
408 .iter()
409 .map(|(&s, &e)| (e - s + 1) as usize)
410 .sum()
411 }
412
413 #[test_traced]
414 fn test_basic_enqueue_dequeue() {
415 let executor = deterministic::Runner::default();
416 executor.start(|context| async move {
417 let cfg = test_config("test_basic", &context);
418 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
419 .await
420 .unwrap();
421
422 assert!(queue.is_empty());
424 assert_eq!(queue.pending(), 0);
425 assert_eq!(queue.size(), 0);
426
427 let pos0;
429 (queue, pos0) = queue.enqueue(b"item0".to_vec()).await.unwrap();
430 let pos1;
431 (queue, pos1) = queue.enqueue(b"item1".to_vec()).await.unwrap();
432 let pos2;
433 (queue, pos2) = queue.enqueue(b"item2".to_vec()).await.unwrap();
434
435 assert_eq!(pos0, 0);
436 assert_eq!(pos1, 1);
437 assert_eq!(pos2, 2);
438 assert_eq!(queue.size(), 3);
439 assert_eq!(queue.pending(), 3);
440 assert!(!queue.is_empty());
441
442 let (p, item) = queue.dequeue().await.unwrap().unwrap();
444 assert_eq!(p, 0);
445 assert_eq!(item, b"item0");
446 assert_eq!(queue.pending(), 2);
447
448 let (p, item) = queue.dequeue().await.unwrap().unwrap();
449 assert_eq!(p, 1);
450 assert_eq!(item, b"item1");
451 assert_eq!(queue.pending(), 1);
452
453 let (p, item) = queue.dequeue().await.unwrap().unwrap();
454 assert_eq!(p, 2);
455 assert_eq!(item, b"item2");
456 assert_eq!(queue.pending(), 0);
457
458 assert!(!queue.is_empty());
460 assert!(queue.dequeue().await.unwrap().is_none());
461 });
462 }
463
464 #[test_traced]
465 fn test_append_commit_batch() {
466 let executor = deterministic::Runner::default();
467 executor.start(|context| async move {
468 let cfg = test_config("test_batch", &context);
469 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
470 .await
471 .unwrap();
472
473 for i in 0..5u8 {
475 (queue, _) = queue.append(vec![i]).await.unwrap();
476 }
477 let mut queue = queue.commit().await.unwrap();
478 assert_eq!(queue.size(), 5);
479
480 for i in 0..5 {
482 let (pos, item) = queue.dequeue().await.unwrap().unwrap();
483 assert_eq!(pos, i);
484 assert_eq!(item, vec![i as u8]);
485 }
486
487 for i in 5..8u8 {
489 (queue, _) = queue.append(vec![i]).await.unwrap();
490 }
491 let queue = queue.commit().await.unwrap();
492 let (mut queue, _) = queue.enqueue(vec![8]).await.unwrap();
493 assert_eq!(queue.size(), 9);
494
495 queue.ack_up_to(9).unwrap();
496 assert!(queue.is_empty());
497 });
498 }
499
500 #[test_traced]
501 fn test_append_commit_persistence() {
502 let executor = deterministic::Runner::default();
503 executor.start(|context| async move {
504 let cfg = test_config("test_batch_persist", &context);
505
506 {
507 let mut queue = Queue::<_, Vec<u8>>::init(context.child("first"), cfg.clone())
508 .await
509 .unwrap();
510 for i in 0..4u8 {
511 (queue, _) = queue.append(vec![i]).await.unwrap();
512 }
513 let queue = queue.commit().await.unwrap();
514 queue.sync().await.unwrap();
515 }
516
517 {
518 let mut queue = Queue::<_, Vec<u8>>::init(context.child("second"), cfg)
519 .await
520 .unwrap();
521 assert_eq!(queue.size(), 4);
522 for i in 0..4 {
523 let (pos, item) = queue.dequeue().await.unwrap().unwrap();
524 assert_eq!(pos, i);
525 assert_eq!(item, vec![i as u8]);
526 }
527 }
528 });
529 }
530
531 #[test_traced]
532 fn test_commit_after_sync_recovers_without_second_sync() {
533 let executor = deterministic::Runner::default();
534 executor.start(|context| async move {
535 let cfg = test_config("test_commit_after_sync_recovery", &context);
536
537 {
538 let mut queue = Queue::<_, Vec<u8>>::init(context.child("first"), cfg.clone())
539 .await
540 .unwrap();
541
542 (queue, _) = queue.append(b"synced".to_vec()).await.unwrap();
544 queue = queue.commit().await.unwrap();
545 queue = queue.sync().await.unwrap();
546
547 (queue, _) = queue.append(b"committed-a".to_vec()).await.unwrap();
549 (queue, _) = queue.append(b"committed-b".to_vec()).await.unwrap();
550 queue.commit().await.unwrap();
551 }
552
553 let mut queue = Queue::<_, Vec<u8>>::init(context.child("second"), cfg)
554 .await
555 .unwrap();
556 assert_eq!(queue.size(), 3);
557 for (expected_pos, expected_item) in [
558 (0, b"synced".to_vec()),
559 (1, b"committed-a".to_vec()),
560 (2, b"committed-b".to_vec()),
561 ] {
562 let (pos, item) = queue.dequeue().await.unwrap().unwrap();
563 assert_eq!(pos, expected_pos);
564 assert_eq!(item, expected_item);
565 }
566
567 queue.destroy().await.unwrap();
568 });
569 }
570
571 #[test_traced]
572 fn test_sequential_ack() {
573 let executor = deterministic::Runner::default();
574 executor.start(|context| async move {
575 let cfg = test_config("test_seq_ack", &context);
576 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
577 .await
578 .unwrap();
579
580 for i in 0..5u8 {
582 (queue, _) = queue.enqueue(vec![i]).await.unwrap();
583 }
584
585 for i in 0..5 {
587 let (pos, _) = queue.dequeue().await.unwrap().unwrap();
588 assert_eq!(pos, i);
589 queue.ack(pos).unwrap();
590 assert_eq!(queue.ack_floor(), i + 1);
591 }
592
593 assert!(queue.is_empty());
595 assert_eq!(queue.ack_floor(), 5);
596 });
597 }
598
599 #[test_traced]
600 fn test_out_of_order_ack() {
601 let executor = deterministic::Runner::default();
602 executor.start(|context| async move {
603 let cfg = test_config("test_ooo_ack", &context);
604 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
605 .await
606 .unwrap();
607
608 for i in 0..5u8 {
610 (queue, _) = queue.enqueue(vec![i]).await.unwrap();
611 }
612
613 for _ in 0..5 {
615 queue.dequeue().await.unwrap();
616 }
617
618 queue.ack(2).unwrap();
620 assert_eq!(queue.ack_floor(), 0); assert!(queue.is_acked(2));
622
623 queue.ack(4).unwrap();
624 assert_eq!(queue.ack_floor(), 0);
625 assert!(queue.is_acked(4));
626
627 queue.ack(1).unwrap();
628 assert_eq!(queue.ack_floor(), 0);
629
630 queue.ack(3).unwrap();
631 assert_eq!(queue.ack_floor(), 0);
632
633 queue.ack(0).unwrap();
635 assert_eq!(queue.ack_floor(), 5);
636 assert!(queue.is_empty());
637 });
638 }
639
640 #[test_traced]
641 fn test_ack_up_to() {
642 let executor = deterministic::Runner::default();
643 executor.start(|context| async move {
644 let cfg = test_config("test_ack_up_to", &context);
645 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
646 .await
647 .unwrap();
648
649 for i in 0..10u8 {
651 (queue, _) = queue.enqueue(vec![i]).await.unwrap();
652 }
653
654 queue.ack_up_to(5).unwrap();
656 assert_eq!(queue.ack_floor(), 5);
657
658 for i in 0..5 {
660 assert!(queue.is_acked(i));
661 }
662 for i in 5..10 {
664 assert!(!queue.is_acked(i));
665 }
666
667 let (p, _) = queue.dequeue().await.unwrap().unwrap();
669 assert_eq!(p, 5);
670 });
671 }
672
673 #[test_traced]
674 fn test_ack_up_to_with_existing_acks() {
675 let executor = deterministic::Runner::default();
676 executor.start(|context| async move {
677 let cfg = test_config("test_ack_up_to_existing", &context);
678 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
679 .await
680 .unwrap();
681
682 for i in 0..10u8 {
684 (queue, _) = queue.enqueue(vec![i]).await.unwrap();
685 }
686
687 queue.ack(7).unwrap();
689 queue.ack(8).unwrap();
690 assert_eq!(acked_above_count(&queue), 2);
691
692 queue.ack_up_to(5).unwrap();
694 assert_eq!(queue.ack_floor(), 5);
695 assert_eq!(acked_above_count(&queue), 2);
696
697 queue.ack_up_to(9).unwrap();
699 assert_eq!(queue.ack_floor(), 9);
700 assert_eq!(acked_above_count(&queue), 0);
701 });
702 }
703
704 #[test_traced]
705 fn test_ack_up_to_coalesces_with_acked_above() {
706 let executor = deterministic::Runner::default();
707 executor.start(|context| async move {
708 let cfg = test_config("test_ack_up_to_coalesce", &context);
709 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
710 .await
711 .unwrap();
712
713 for i in 0..10u8 {
715 (queue, _) = queue.enqueue(vec![i]).await.unwrap();
716 }
717
718 queue.ack(5).unwrap();
720 queue.ack(6).unwrap();
721 queue.ack(7).unwrap();
722 assert_eq!(queue.ack_floor(), 0);
723
724 queue.ack_up_to(5).unwrap();
726 assert_eq!(queue.ack_floor(), 8); });
728 }
729
730 #[test_traced]
731 fn test_ack_up_to_errors() {
732 let executor = deterministic::Runner::default();
733 executor.start(|context| async move {
734 let cfg = test_config("test_ack_up_to_errors", &context);
735 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
736 .await
737 .unwrap();
738
739 (queue, _) = queue.enqueue(b"item0".to_vec()).await.unwrap();
740 let (mut queue, _) = queue.enqueue(b"item1".to_vec()).await.unwrap();
741
742 let err = queue.ack_up_to(5).unwrap_err();
744 assert!(matches!(err, Error::PositionOutOfRange(5, 2)));
745
746 queue.ack_up_to(2).unwrap();
748 assert_eq!(queue.ack_floor(), 2);
749
750 queue.ack_up_to(1).unwrap();
752 assert_eq!(queue.ack_floor(), 2);
753 });
754 }
755
756 #[test_traced]
757 fn test_dequeue_skips_acked() {
758 let executor = deterministic::Runner::default();
759 executor.start(|context| async move {
760 let cfg = test_config("test_skip_acked", &context);
761 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
762 .await
763 .unwrap();
764
765 for i in 0..5u8 {
767 (queue, _) = queue.enqueue(vec![i]).await.unwrap();
768 }
769
770 queue.ack(1).unwrap();
772 queue.ack(3).unwrap();
773
774 let (p, item) = queue.dequeue().await.unwrap().unwrap();
776 assert_eq!(p, 0);
777 assert_eq!(item, vec![0]);
778
779 let (p, item) = queue.dequeue().await.unwrap().unwrap();
780 assert_eq!(p, 2); assert_eq!(item, vec![2]);
782
783 let (p, item) = queue.dequeue().await.unwrap().unwrap();
784 assert_eq!(p, 4); assert_eq!(item, vec![4]);
786
787 assert!(queue.dequeue().await.unwrap().is_none());
788 });
789 }
790
791 #[test_traced]
792 fn test_ack_errors() {
793 let executor = deterministic::Runner::default();
794 executor.start(|context| async move {
795 let cfg = test_config("test_ack_errors", &context);
796 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
797 .await
798 .unwrap();
799
800 (queue, _) = queue.enqueue(b"item0".to_vec()).await.unwrap();
801 let (mut queue, _) = queue.enqueue(b"item1".to_vec()).await.unwrap();
802
803 let err = queue.ack(5).unwrap_err();
805 assert!(matches!(err, Error::PositionOutOfRange(5, 2)));
806
807 queue.ack(1).unwrap();
809 assert!(queue.is_acked(1));
810
811 queue.ack(1).unwrap();
813 });
814 }
815
816 #[test_traced]
817 fn test_prune() {
818 let executor = deterministic::Runner::default();
819 executor.start(|context| async move {
820 let cfg = test_config("test_prune", &context);
821 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
822 .await
823 .unwrap();
824
825 for i in 0..25u8 {
827 (queue, _) = queue.enqueue(vec![i]).await.unwrap();
828 }
829 let mut queue = queue.sync().await.unwrap();
830
831 for i in 0..15 {
833 queue.dequeue().await.unwrap();
834 queue.ack(i).unwrap();
835 }
836 assert_eq!(queue.ack_floor(), 15);
837
838 let (p, item) = queue.dequeue().await.unwrap().unwrap();
840 assert_eq!(p, 15);
841 assert_eq!(item, vec![15]);
842 });
843 }
844
845 #[test_traced]
846 fn test_ack_across_sections() {
847 let executor = deterministic::Runner::default();
848 executor.start(|context| async move {
849 let cfg = test_config("test_multi_prune", &context);
850 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
851 .await
852 .unwrap();
853
854 for i in 0..50u8 {
856 (queue, _) = queue.enqueue(vec![i]).await.unwrap();
857 }
858 let mut queue = queue.sync().await.unwrap();
859
860 for i in 0..15 {
862 queue.dequeue().await.unwrap();
863 queue.ack(i).unwrap();
864 }
865 assert_eq!(queue.ack_floor(), 15);
866
867 let (p, item) = queue.dequeue().await.unwrap().unwrap();
869 assert_eq!(p, 15);
870 assert_eq!(item, vec![15]);
871
872 queue.ack(15).unwrap();
874 for i in 16..30 {
875 queue.dequeue().await.unwrap();
876 queue.ack(i).unwrap();
877 }
878 assert_eq!(queue.ack_floor(), 30);
879
880 let (p, item) = queue.dequeue().await.unwrap().unwrap();
882 assert_eq!(p, 30);
883 assert_eq!(item, vec![30]);
884
885 queue.ack(30).unwrap();
887 for i in 31..50 {
888 queue.dequeue().await.unwrap();
889 queue.ack(i).unwrap();
890 }
891 assert_eq!(queue.ack_floor(), 50);
892
893 assert!(queue.is_empty());
895 assert!(queue.dequeue().await.unwrap().is_none());
896 });
897 }
898
899 #[test_traced]
900 fn test_crash_recovery_replays_from_pruning_boundary() {
901 let executor = deterministic::Runner::default();
903 executor.start(|context| async move {
904 let cfg = test_config("test_recovery_replay", &context);
905
906 {
908 let mut queue = Queue::<_, Vec<u8>>::init(context.child("first"), cfg.clone())
909 .await
910 .unwrap();
911
912 for i in 0..5u8 {
913 (queue, _) = queue.enqueue(vec![i]).await.unwrap();
914 }
915
916 queue.ack(0).unwrap();
918 queue.ack(1).unwrap();
919 queue.ack(2).unwrap();
920 assert_eq!(queue.ack_floor(), 3);
921
922 queue.sync().await.unwrap();
923 }
924
925 {
927 let mut queue = Queue::<_, Vec<u8>>::init(context.child("second"), cfg.clone())
928 .await
929 .unwrap();
930
931 assert_eq!(queue.ack_floor(), 0);
933
934 for i in 0..5 {
936 let (p, _) = queue.dequeue().await.unwrap().unwrap();
937 assert_eq!(p, i);
938 }
939 }
940 });
941 }
942
943 #[test_traced]
944 fn test_crash_recovery_with_pruning() {
945 let executor = deterministic::Runner::default();
947 executor.start(|context| async move {
948 let cfg = test_config("test_recovery_pruned", &context);
949
950 let expected_pruning_boundary = {
952 let mut queue = Queue::<_, Vec<u8>>::init(context.child("first"), cfg.clone())
953 .await
954 .unwrap();
955
956 for i in 0..25u8 {
958 (queue, _) = queue.enqueue(vec![i]).await.unwrap();
959 }
960
961 for i in 0..15 {
963 queue.ack(i).unwrap();
964 }
965 assert_eq!(queue.ack_floor(), 15);
966
967 queue = queue.sync().await.unwrap();
969
970 let pruning_boundary = queue.journal.bounds().start;
972 assert!(pruning_boundary > 0, "expected some pruning to occur");
973
974 pruning_boundary
975 };
976
977 {
979 let mut queue = Queue::<_, Vec<u8>>::init(context.child("second"), cfg.clone())
980 .await
981 .unwrap();
982
983 let pruning_boundary = queue.journal.bounds().start;
985 assert_eq!(queue.ack_floor(), pruning_boundary);
986 assert_eq!(pruning_boundary, expected_pruning_boundary);
987
988 for i in pruning_boundary..25 {
990 let (p, item) = queue.dequeue().await.unwrap().unwrap();
991 assert_eq!(p, i);
992 assert_eq!(item, vec![i as u8]);
993 }
994
995 assert!(queue.dequeue().await.unwrap().is_none());
996 }
997 });
998 }
999
1000 #[test_traced]
1001 fn test_reset() {
1002 let executor = deterministic::Runner::default();
1003 executor.start(|context| async move {
1004 let cfg = test_config("test_reset", &context);
1005 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
1006 .await
1007 .unwrap();
1008
1009 for i in 0..5u8 {
1011 (queue, _) = queue.enqueue(vec![i]).await.unwrap();
1012 }
1013
1014 queue.dequeue().await.unwrap();
1016 queue.dequeue().await.unwrap();
1017 queue.dequeue().await.unwrap();
1018 assert_eq!(queue.read_position(), 3);
1019
1020 queue.reset();
1022 assert_eq!(queue.read_position(), 0);
1023
1024 let (p, item) = queue.dequeue().await.unwrap().unwrap();
1026 assert_eq!(p, 0);
1027 assert_eq!(item, vec![0]);
1028 });
1029 }
1030
1031 #[test_traced]
1032 fn test_reset_with_ack() {
1033 let executor = deterministic::Runner::default();
1034 executor.start(|context| async move {
1035 let cfg = test_config("test_reset_ack", &context);
1036 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
1037 .await
1038 .unwrap();
1039
1040 for i in 0..10u8 {
1042 (queue, _) = queue.enqueue(vec![i]).await.unwrap();
1043 }
1044
1045 for i in 0..5 {
1047 queue.dequeue().await.unwrap();
1048 queue.ack(i).unwrap();
1049 }
1050 assert_eq!(queue.ack_floor(), 5);
1051 assert_eq!(queue.read_position(), 5);
1052
1053 queue.dequeue().await.unwrap();
1055 queue.dequeue().await.unwrap();
1056 assert_eq!(queue.read_position(), 7);
1057
1058 queue.reset();
1060 assert_eq!(queue.read_position(), 5);
1061
1062 let (p, item) = queue.dequeue().await.unwrap().unwrap();
1064 assert_eq!(p, 5);
1065 assert_eq!(item, vec![5]);
1066 });
1067 }
1068
1069 #[test_traced]
1070 fn test_empty_queue_operations() {
1071 let executor = deterministic::Runner::default();
1072 executor.start(|context| async move {
1073 let cfg = test_config("test_empty", &context);
1074 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
1075 .await
1076 .unwrap();
1077
1078 assert!(queue.is_empty());
1080 assert!(queue.dequeue().await.unwrap().is_none());
1081 queue = queue.sync().await.unwrap();
1082 queue.reset();
1083 });
1084 }
1085
1086 #[test_traced]
1087 fn test_persistence() {
1088 let executor = deterministic::Runner::default();
1089 executor.start(|context| async move {
1090 let cfg = test_config("test_persist", &context);
1091
1092 {
1094 let mut queue = Queue::<_, Vec<u8>>::init(context.child("first"), cfg.clone())
1095 .await
1096 .unwrap();
1097
1098 (queue, _) = queue.enqueue(b"item0".to_vec()).await.unwrap();
1099 (queue, _) = queue.enqueue(b"item1".to_vec()).await.unwrap();
1100 queue.sync().await.unwrap();
1101 }
1102
1103 {
1105 let mut queue = Queue::<_, Vec<u8>>::init(context.child("second"), cfg.clone())
1106 .await
1107 .unwrap();
1108
1109 assert_eq!(queue.size(), 2);
1110
1111 let (_, item) = queue.dequeue().await.unwrap().unwrap();
1112 assert_eq!(item, b"item0");
1113
1114 let (_, item) = queue.dequeue().await.unwrap().unwrap();
1115 assert_eq!(item, b"item1");
1116 }
1117 });
1118 }
1119
1120 #[test_traced]
1121 fn test_large_queue_with_sparse_acks() {
1122 let executor = deterministic::Runner::default();
1123 executor.start(|context| async move {
1124 let cfg = test_config("test_sparse", &context);
1125 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
1126 .await
1127 .unwrap();
1128
1129 for i in 0..100u8 {
1131 (queue, _) = queue.enqueue(vec![i]).await.unwrap();
1132 }
1133
1134 for i in (0..100).step_by(3) {
1136 queue.ack(i).unwrap();
1137 }
1138
1139 let mut received = Vec::new();
1141 while let Some((pos, _)) = queue.dequeue().await.unwrap() {
1142 received.push(pos);
1143 }
1144
1145 let expected: Vec<u64> = (0..100).filter(|x| x % 3 != 0).collect();
1147 assert_eq!(received, expected);
1148 });
1149 }
1150
1151 #[test_traced]
1152 fn test_acked_above_coalescing() {
1153 let executor = deterministic::Runner::default();
1154 executor.start(|context| async move {
1155 let cfg = test_config("test_coalesce", &context);
1156 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
1157 .await
1158 .unwrap();
1159
1160 for i in 0..10u8 {
1162 (queue, _) = queue.enqueue(vec![i]).await.unwrap();
1163 }
1164
1165 for i in 1..9 {
1167 queue.ack(i).unwrap();
1168 }
1169
1170 assert_eq!(queue.ack_floor(), 0);
1172 assert!(acked_above_count(&queue) > 0);
1173
1174 queue.ack(0).unwrap();
1176 assert_eq!(queue.ack_floor(), 9);
1177 assert_eq!(acked_above_count(&queue), 0);
1178 });
1179 }
1180
1181 #[test_traced]
1182 fn test_ack_up_to_past_read_pos() {
1183 let executor = deterministic::Runner::default();
1184 executor.start(|context| async move {
1185 let cfg = test_config("test_ack_up_to_past_read_pos", &context);
1186 let mut queue = Queue::<_, Vec<u8>>::init(context.child("storage"), cfg)
1187 .await
1188 .unwrap();
1189
1190 for i in 0..10u8 {
1191 (queue, _) = queue.enqueue(vec![i]).await.unwrap();
1192 }
1193
1194 for _ in 0..3 {
1196 queue.dequeue().await.unwrap();
1197 }
1198 assert_eq!(queue.read_position(), 3);
1199
1200 queue.ack_up_to(7).unwrap();
1202 assert_eq!(queue.ack_floor(), 7);
1203
1204 let (pos, item) = queue.dequeue().await.unwrap().unwrap();
1206 assert_eq!(pos, 7);
1207 assert_eq!(item, vec![7]);
1208 });
1209 }
1210
1211 #[test_traced]
1212 fn test_metrics() {
1213 let executor = deterministic::Runner::default();
1214 executor.start(|context| async move {
1215 let cfg = test_config("test-metrics", &context);
1216 let ctx = context.child("test_metrics");
1217 let mut queue = Queue::<_, Vec<u8>>::init(ctx, cfg).await.unwrap();
1218
1219 let encoded = context.encode();
1220 assert!(
1221 encoded.contains("test_metrics_tip 0"),
1222 "expected tip 0: {encoded}"
1223 );
1224 assert!(
1225 encoded.contains("test_metrics_floor 0"),
1226 "expected floor 0: {encoded}"
1227 );
1228 assert!(
1229 encoded.contains("test_metrics_next 0"),
1230 "expected next 0: {encoded}"
1231 );
1232
1233 (queue, _) = queue.append(vec![0]).await.unwrap();
1235 let encoded = context.encode();
1236 assert!(
1237 encoded.contains("test_metrics_tip 1"),
1238 "expected tip 1: {encoded}"
1239 );
1240 let mut queue = queue.commit().await.unwrap();
1241
1242 for i in 1..10u8 {
1244 (queue, _) = queue.enqueue(vec![i]).await.unwrap();
1245 }
1246 let encoded = context.encode();
1247 assert!(
1248 encoded.contains("test_metrics_tip 10"),
1249 "expected tip 10: {encoded}"
1250 );
1251
1252 queue.dequeue().await.unwrap();
1254 queue.dequeue().await.unwrap();
1255 let encoded = context.encode();
1256 assert!(
1257 encoded.contains("test_metrics_next 2"),
1258 "expected next 2: {encoded}"
1259 );
1260
1261 queue.ack(0).unwrap();
1263 queue.ack(1).unwrap();
1264 let encoded = context.encode();
1265 assert!(
1266 encoded.contains("test_metrics_floor 2"),
1267 "expected floor 2: {encoded}"
1268 );
1269
1270 queue.ack(4).unwrap();
1272 queue.ack(6).unwrap();
1273 let encoded = context.encode();
1274 assert!(
1275 encoded.contains("test_metrics_floor 2"),
1276 "expected floor still 2: {encoded}"
1277 );
1278
1279 queue.ack(2).unwrap();
1281 queue.ack(3).unwrap();
1282 let encoded = context.encode();
1283 assert!(
1284 encoded.contains("test_metrics_floor 5"),
1285 "expected floor 5: {encoded}"
1286 );
1287
1288 queue.ack_up_to(8).unwrap();
1290 let encoded = context.encode();
1291 assert!(
1292 encoded.contains("test_metrics_floor 8"),
1293 "expected floor 8: {encoded}"
1294 );
1295
1296 queue.ack(8).unwrap();
1298 queue.ack(9).unwrap();
1299 let encoded = context.encode();
1300 assert!(
1301 encoded.contains("test_metrics_floor 10"),
1302 "expected floor 10: {encoded}"
1303 );
1304
1305 queue.reset();
1307 let encoded = context.encode();
1308 assert!(
1309 encoded.contains("test_metrics_next 10"),
1310 "expected next 10: {encoded}"
1311 );
1312 });
1313 }
1314
1315 #[test_traced]
1316 fn test_metrics_next_updates_on_fast_forward() {
1317 let executor = deterministic::Runner::default();
1318 executor.start(|context| async move {
1319 let cfg = test_config("test-ff", &context);
1320 let ctx = context.child("test_ff");
1321 let mut queue = Queue::<_, Vec<u8>>::init(ctx, cfg).await.unwrap();
1322
1323 for i in 0..3u8 {
1325 (queue, _) = queue.enqueue(vec![i]).await.unwrap();
1326 }
1327 let (pos, _) = queue.dequeue().await.unwrap().unwrap();
1328 queue.ack(pos).unwrap();
1329
1330 let encoded = context.encode();
1331 assert!(
1332 encoded.contains("test_ff_next 1"),
1333 "expected next 1: {encoded}"
1334 );
1335
1336 queue.ack(2).unwrap();
1338 queue.ack(1).unwrap();
1339 assert_eq!(queue.ack_floor(), 3);
1340
1341 let encoded = context.encode();
1343 assert!(
1344 encoded.contains("test_ff_next 1"),
1345 "expected next still 1: {encoded}"
1346 );
1347
1348 assert!(queue.dequeue().await.unwrap().is_none());
1350 let encoded = context.encode();
1351 assert!(
1352 encoded.contains("test_ff_next 3"),
1353 "expected next 3 after fast-forward: {encoded}"
1354 );
1355 });
1356 }
1357}