Skip to main content

apalis_core/backend/
mod.rs

1//! Core traits for interacting with backends
2//!
3//! The core traits and types for backends, responsible for providing sources of tasks, handling their lifecycle, and exposing middleware for internal processing.
4//! The traits here abstract over different backend implementations, allowing for extensibility and interoperability.
5//!
6//! # Overview
7//! - [`Backend`]: The primary trait representing a task source, defining methods for polling tasks, heartbeats, and middleware.
8//! - [`TaskSink`]: An extension trait for backends that support pushing tasks.
9//! - [`FetchById`], [`Update`], [`Reschedule`]: Additional traits for managing tasks.
10//! - [`Vacuum`], [`ResumeById`], [`ResumeAbandoned`]: Traits for backend maintenance and task recovery.
11//! - [`RegisterWorker`], [`ListWorkers`], [`ListTasks`]: Traits for worker management and task listing.
12//! - [`WaitForCompletion`]: A trait for waiting on task completion and checking their status.
13//!
14//!
15//! ## Provided Implementations
16//!
17//! The module includes several default backend implementations, such as:
18//! - [`MemoryStorage`](memory::MemoryStorage): An in-memory backend for testing and lightweight use cases
19//! - [`CustomBackend`](custom::CustomBackend): A flexible backend allowing custom functions for task management
20//! - [`VecDequeBackend`](dequeue::VecDequeBackend): A simple in-memory backend that uses a `VecDeque` to store tasks.
21//! - [`Persisted`](persistence::Persisted): A backend that drives a [`Persistence`](persistence::Persistence) instance to emit tasks
22use std::{
23    fmt::Debug,
24    future::Future,
25    task::{Context, Poll},
26    time::Duration,
27};
28
29use crate::{backend::codec::Codec, task::task_id::TaskId, worker::context::WorkerContext};
30
31pub mod codec;
32pub mod custom;
33pub mod ext;
34pub mod factory;
35/// Finalizes an intermediate backend configuration into a concrete backend.
36///
37/// Used internally by workers
38pub mod finalize;
39pub mod queue;
40
41mod expose;
42/// Provides utilities for working with futures
43pub mod future;
44mod impls;
45/// Persistence layer for managing workers, tasks and events
46///
47/// Provides the `Persistence` trait which eases making backends with predictable persistence
48#[cfg(feature = "sleep")]
49pub mod persistence;
50mod results;
51mod sink;
52mod worker;
53
54pub use expose::*;
55pub use results::*;
56pub use sink::*;
57pub use worker::*;
58/// In-memory backend based on channels
59pub mod memory {
60    pub use crate::backend::impls::memory::*;
61}
62
63/// In-memory dequeue backend
64pub mod dequeue {
65    pub use crate::backend::impls::dequeue::*;
66}
67
68/// A task backend that drives task acquisition and lifecycle management for a worker.
69///
70/// A backend is responsible for:
71///
72/// - determining when it is ready to perform work;
73/// - yielding available tasks;
74/// - registering the worker's waker when no work is currently available; and
75/// - completing any pending cleanup or acknowledgement work during shutdown.
76///
77/// In other systems, this abstraction may be called a queue, broker, or
78/// consumer. The Backend abstraction is intentionally broader than a queue:
79/// it may also manage acknowledgements, leases, heartbeats, subscriptions, and
80/// other state associated with processing tasks.
81///
82/// The worker drives a backend by repeatedly polling [poll_ready], [poll_next],
83/// and [poll_close] as appropriate. Implementations must not block inside these
84/// methods.
85///
86/// # Polling
87///
88/// Backend methods follow the same conventions as [Future] and [Stream]:
89///
90/// - Poll::Ready means the operation can make progress immediately.
91/// - Poll::Pending means the operation cannot make progress yet and the
92///   implementation has registered the supplied waker.
93/// - The implementation must arrange for the waker to be woken when polling
94///   again may make progress.
95///
96/// A backend should avoid busy-polling. In particular, [poll_next] should
97/// return Poll::Pending once all currently available tasks have been drained
98/// and wait for its underlying source to signal that more work is available.
99///
100/// # Shutdown
101///
102/// [poll_close] is called when the worker is shutting down. It should continue
103/// making progress until all backend-owned resources and pending operations
104/// have been released. A backend must not report completion from poll_close
105/// while work that it is responsible for flushing or releasing remains.
106///
107/// [poll_next]: Backend::poll_next
108/// [poll_ready]: Backend::poll_ready
109/// [poll_close]: Backend::poll_close
110/// [Future]: std::future::Future
111/// [Stream]: futures_core::Stream
112#[must_use = "a Backend does nothing unless it is polled"]
113pub trait Backend: Sized {
114    /// The type of task the backend emits.
115    type Task;
116
117    /// The error type returned by backend operations
118    type Error: std::error::Error + Send + Sync + 'static;
119
120    /// Polls whether the backend is ready for the worker to request more work.
121    ///
122    /// This method may be used to flush pending backend operations or perform
123    /// other work required before [`poll_next`] can be called.
124    /// Returning `Poll::Ready(Ok(()))` indicates that the worker may proceed.
125    /// Returning `Poll::Pending` indicates that the backend cannot make progress
126    /// yet. The implementation must register `cx.waker()` and wake it when the
127    /// backend becomes ready.
128    ///
129    /// [`poll_next`]: Backend::poll_next
130    fn poll_ready(
131        &mut self,
132        cx: &mut Context<'_>,
133        worker: &WorkerContext,
134    ) -> Poll<Result<(), Self::Error>>;
135
136    /// Polls the backend for the next available task for this worker.
137    ///
138    /// Like `poll_ready`, this is lazy: a backend should only produce a
139    /// task when one is actually available, not busy-poll its source.
140    /// Once the buffer of ready tasks is drained and this returns
141    /// `Poll::Pending`, the backend is expected to register the waker and
142    /// go to sleep until new work arrives rather than being polled again
143    /// right away. Returns `None` when the backend is exhausted and will
144    /// never produce another task.
145    ///
146    /// # Example
147    ///
148    /// A minimal in-memory backend might look like this:
149    ///
150    /// ```ignore
151    /// fn poll_next(
152    ///     &mut self,
153    ///     cx: &mut Context<'_>,
154    ///     _worker: &WorkerContext,
155    /// ) -> Poll<Option<Result<Task, Self::Error>>> {
156    ///     match self.queue.pop_front() {
157    ///         // A task was ready, return it immediately.
158    ///         Some(task) => Poll::Ready(Some(Ok(task))),
159    ///         // Nothing to do right now: register the waker and sleep
160    ///         // until `wake()` is called (e.g. when a task is pushed).
161    ///         None => {
162    ///             self.waker = Some(cx.waker().clone());
163    ///             Poll::Pending
164    ///         }
165    ///     }
166    /// }
167    /// ```
168    fn poll_next(
169        &mut self,
170        cx: &mut Context<'_>,
171        worker: &WorkerContext,
172    ) -> Poll<Option<Result<Self::Task, Self::Error>>>;
173
174    /// Flushes/releases any resources the backend holds (pending acks,
175    /// open subscriptions, connections) before the worker fully shuts down.
176    fn poll_close(
177        &mut self,
178        cx: &mut Context<'_>,
179        worker: &WorkerContext,
180    ) -> Poll<Result<(), Self::Error>>;
181}
182
183/// Decorates a backend with middleware, configuration and associated types
184pub trait BackendConfig {
185    /// The type of argument this backend emits
186    type Args;
187
188    /// The internal type of this Backend's [`TaskId`]
189    type Id;
190
191    /// This defines the kind of Backend
192    type Kind;
193
194    /// The config for the backend
195    type Config;
196
197    /// The type representing backend middleware layer.
198    type Layer;
199
200    /// Returns the config associated with the backend.
201    fn config(&self) -> &Self::Config;
202
203    /// Returns the backend's middleware layer.
204    fn middleware(&mut self, worker: &mut WorkerContext) -> Self::Layer;
205}
206
207/// Provides wire-format support for a backend.
208///
209/// A `WireFormatBackend` associates a backend with a codec used to encode and
210/// decode tasks and with the compact representation used when tasks are
211/// transmitted or persisted in their encoded form.
212pub trait WireFormatBackend {
213    /// The codec used to encode and decode tasks.
214    type Codec: Send + 'static;
215
216    /// The compact representation of task arguments.
217    type Compact;
218
219    /// Returns a reference to the codec used by the backend.
220    fn codec(&self) -> &Self::Codec;
221}
222
223/// Allows fetching a task by its ID
224pub trait FetchById: Backend {
225    /// Fetch a task by its unique identifier
226    #[allow(clippy::type_complexity)]
227    fn fetch_by_id(
228        &mut self,
229        task_id: &TaskId,
230    ) -> impl Future<Output = Result<Option<Self::Task>, Self::Error>> + Send;
231}
232
233/// Allows updating an existing task
234pub trait Update: Backend {
235    /// Update the given task
236    fn update(&mut self, task: Self::Task) -> impl Future<Output = Result<(), Self::Error>> + Send;
237}
238
239/// Allows rescheduling a task for later execution
240pub trait Reschedule: Backend {
241    /// Reschedule the task after a specified duration
242    fn reschedule(
243        &mut self,
244        task: Self::Task,
245        wait: Duration,
246    ) -> impl Future<Output = Result<(), Self::Error>> + Send;
247}
248
249/// Allows cleaning up resources in the backend
250pub trait Vacuum: Backend {
251    /// Cleans up all resources and returns the number of tasks vacuumed
252    fn vacuum(&mut self) -> impl Future<Output = Result<usize, Self::Error>> + Send;
253
254    /// Cleans up all resources committed before a specific duration
255    /// and returns the number of tasks vacuumed
256    fn vacuum_before(
257        &mut self,
258        duration: Duration,
259    ) -> impl Future<Output = Result<usize, Self::Error>> + Send;
260}
261
262/// Allows resuming a task by its ID
263pub trait ResumeById: Backend {
264    /// Resume a task by its ID
265    fn resume_by_id(
266        &mut self,
267        id: TaskId,
268    ) -> impl Future<Output = Result<bool, Self::Error>> + Send;
269}
270
271/// A [`Backend`] that can be fallibly constructed from its own configuration.
272pub trait TryNewBackend: BackendConfig + Backend + Sized {
273    /// The final backend after decoration
274    ///
275    /// This value may easily be Self;
276    /// But in some cases we want to add some `BackendExt` compositions
277    type Backend;
278    /// Attempt to construct a new backend instance from its configuration.
279    fn try_new(config: Self::Config) -> Result<Self::Backend, Self::Error>;
280}