Skip to main content

commonware_storage/queue/
mod.rs

1//! A durable, at-least-once delivery queue backed by a [`variable::Journal`](crate::journal::contiguous::variable).
2//!
3//! [Queue] provides a persistent message queue with at-least-once delivery semantics.
4//! Items are durably stored in a journal and will survive crashes. [Queue::init] returns the
5//! queue, which writes items, and its [Reader], which delivers committed items. The reader must
6//! explicitly acknowledge each item after processing. On restart, all non-pruned items are
7//! re-delivered (acknowledged or not).
8//!
9//! # Ownership
10//!
11//! Methods that write to storage (`append`, `enqueue`, `commit`, `sync`) take the queue by value
12//! and return it on success. If one returns an error, or its future is dropped before it finishes,
13//! the queue is gone: state that was not yet durable is discarded, but everything already on disk
14//! stays recoverable. The reader then delivers every published item and returns `None`. Reader
15//! methods borrow the reader. A failed read does not invalidate it.
16//!
17//! # Concurrency
18//!
19//! The queue and its reader can run in separate tasks. The reader never waits on the queue, and
20//! [Reader::recv] integrates with `select!` for multiplexing with other futures.
21//!
22//! ```rust,ignore
23//! use commonware_storage::queue::Queue;
24//! use commonware_macros::select;
25//!
26//! let (queue, mut reader) = Queue::init(context, config).await?;
27//!
28//! // Writer task
29//! let (queue, position) = queue.enqueue(&item).await?;
30//!
31//! // Reader task
32//! loop {
33//!     select! {
34//!         result = reader.recv() => {
35//!             let Some((pos, item)) = result? else { break };
36//!             // Process item...
37//!             reader.ack(pos)?;
38//!         }
39//!         _ = shutdown => break,
40//!     }
41//! }
42//! ```
43//!
44//! # Example
45//!
46//! ```rust
47//! use commonware_codec::RangeCfg;
48//! use commonware_runtime::{Spawner, Runner, deterministic, buffer::paged::CacheRef};
49//! use commonware_storage::{queue::{Queue, Config}};
50//! use std::num::{NonZeroU16, NonZeroU64, NonZeroUsize};
51//!
52//! let executor = deterministic::Runner::default();
53//! executor.start(|context| async move {
54//!     // Create a page cache
55//!     let page_cache = CacheRef::new(
56//!         NonZeroU16::new(1024).unwrap(),
57//!         NonZeroUsize::new(10).unwrap(),
58//!     );
59//!
60//!     // Create a queue and its reader
61//!     let (mut queue, mut reader) = Queue::<_, Vec<u8>>::init(context, Config {
62//!         partition: "my-queue".into(),
63//!         items_per_section: NonZeroU64::new(1000).unwrap(),
64//!         compression: None,
65//!         codec_config: ((0..).into(), ()), // RangeCfg for Vec length, () for u8
66//!         page_cache,
67//!         write_buffer: NonZeroUsize::new(4096).unwrap(),
68//!         replay_buffer: NonZeroUsize::new(4096).unwrap(),
69//!     }).await.unwrap();
70//!
71//!     // Enqueue items
72//!     (queue, _) = queue.enqueue(&b"task1".to_vec()).await.unwrap();
73//!     (queue, _) = queue.enqueue(&b"task2".to_vec()).await.unwrap();
74//!
75//!     // Receive and process items (can be acknowledged out of order)
76//!     while let Some((position, item)) = reader.try_recv().await.unwrap() {
77//!         // Process the item...
78//!         println!("Processing item at position {}", position);
79//!
80//!         // Acknowledge after successful processing
81//!         reader.ack(position).unwrap();
82//!     }
83//!
84//!     // Persist the queue and prune acknowledged items
85//!     queue.sync().await.unwrap();
86//! });
87//! ```
88
89#[cfg(all(test, feature = "arbitrary"))]
90mod conformance;
91mod cursor;
92mod metrics;
93mod storage;
94
95pub use storage::{Config, Queue, Reader};
96use thiserror::Error;
97
98/// Errors that can occur when interacting with [Queue].
99#[derive(Debug, Error)]
100pub enum Error {
101    #[error("journal error: {0}")]
102    Journal(#[from] crate::journal::Error),
103    #[error("position out of range: {0} (queue size is {1})")]
104    PositionOutOfRange(u64, u64),
105}