use super::{StorageError, StorageResult};
use crate::request::{Request, RequestId};
use std::{fmt, time::Duration};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LeaseId(u64);
impl LeaseId {
pub fn new(raw: u64) -> Self {
Self(raw)
}
pub fn as_u64(&self) -> u64 {
self.0
}
}
impl fmt::Display for LeaseId {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(formatter)
}
}
#[derive(Debug)]
pub struct Lease {
pub request: Request,
pub lease_id: LeaseId,
pub expires_at: std::time::Instant,
}
#[derive(Debug, Clone, Default)]
#[non_exhaustive]
#[must_use = "add options do nothing unless passed to RequestQueue::add"]
pub struct AddOptions {
pub forefront: bool,
}
#[derive(Debug, Clone)]
#[non_exhaustive]
#[must_use = "reclaim options do nothing unless passed to RequestQueue::reclaim"]
pub struct ReclaimOptions {
pub forefront: bool,
pub increment_retry: bool,
}
impl Default for ReclaimOptions {
fn default() -> Self {
Self {
forefront: false,
increment_retry: true,
}
}
}
#[derive(Debug, Clone)]
#[must_use = "queue insertion results report deduplication state"]
pub struct ProcessedRequest {
pub request_id: RequestId,
pub unique_key: String,
pub was_already_present: bool,
pub was_already_handled: bool,
}
pub type QueueOpInfo = ProcessedRequest;
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum RequestSource {
Request(Request),
}
impl From<Request> for RequestSource {
fn from(request: Request) -> Self {
Self::Request(request)
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
#[must_use = "batched insertion results report processed requests"]
pub struct AddRequestsBatchedResult {
pub processed: Vec<ProcessedRequest>,
}
#[must_use = "batch handles must be awaited to observe completion"]
pub struct BatchAddHandle {
pub added: Vec<ProcessedRequest>,
completion: Completion,
}
enum Completion {
Ready(AddRequestsBatchedResult),
Task(tokio::task::JoinHandle<StorageResult<AddRequestsBatchedResult>>),
}
impl fmt::Debug for BatchAddHandle {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("BatchAddHandle")
.field("added", &self.added)
.field("completion", &"<completion>")
.finish()
}
}
impl BatchAddHandle {
pub fn ready(added: Vec<ProcessedRequest>) -> Self {
Self {
completion: Completion::Ready(AddRequestsBatchedResult {
processed: added.clone(),
}),
added,
}
}
pub fn deferred(
added: Vec<ProcessedRequest>,
task: tokio::task::JoinHandle<StorageResult<AddRequestsBatchedResult>>,
) -> Self {
Self {
added,
completion: Completion::Task(task),
}
}
pub(crate) fn notify_on_completion<F>(self, notify: F) -> Self
where
F: FnOnce() + Send + 'static,
{
let Self { added, completion } = self;
match completion {
Completion::Ready(result) => {
notify();
Self {
added,
completion: Completion::Ready(result),
}
}
Completion::Task(task) => Self {
added,
completion: Completion::Task(tokio::spawn(async move {
let result = task.await.map_err(|error| {
StorageError::Backend(anyhow::anyhow!("batch add task failed: {error}"))
});
notify();
result?
})),
},
}
}
pub async fn wait(self) -> StorageResult<AddRequestsBatchedResult> {
match self.completion {
Completion::Ready(result) => Ok(result),
Completion::Task(task) => task.await.map_err(|error| {
StorageError::Backend(anyhow::anyhow!("batch add task failed: {error}"))
})?,
}
}
}
#[async_trait::async_trait]
pub trait RequestQueue: Send + Sync {
async fn add(&self, req: Request, opts: AddOptions) -> StorageResult<QueueOpInfo>;
async fn add_batch(
&self,
reqs: Vec<RequestSource>,
opts: AddOptions,
) -> StorageResult<BatchAddHandle>;
async fn fetch_next(&self) -> StorageResult<Option<Lease>>;
async fn mark_handled(&self, lease: Lease) -> StorageResult<()>;
async fn reclaim(&self, lease: Lease, opts: ReclaimOptions) -> StorageResult<()>;
async fn renew(&self, lease_id: &LeaseId, extend_by: Duration) -> StorageResult<()>;
async fn abandon(&self, lease: Lease) -> StorageResult<()>;
async fn is_empty(&self) -> StorageResult<bool>;
async fn is_finished(&self) -> StorageResult<bool>;
async fn handled_count(&self) -> StorageResult<u64>;
async fn pending_count(&self) -> StorageResult<u64>;
}