apalis_core/lib.rs
1#![crate_name = "apalis_core"]
2#![warn(
3 missing_debug_implementations,
4 missing_docs,
5 rust_2018_idioms,
6 unreachable_pub,
7 bad_style,
8 dead_code,
9 improper_ctypes,
10 non_shorthand_field_patterns,
11 overflowing_literals,
12 path_statements,
13 patterns_in_fns_without_body,
14 unconditional_recursion,
15 unused,
16 unused_allocation,
17 unused_comparisons,
18 unused_parens,
19 while_true
20)]
21#![cfg_attr(docsrs, feature(doc_cfg))]
22//! A high-performance, type-safe task processing framework for rust.
23//!
24//! `apalis-core` provides the fundamental abstractions and runtime components for building
25//! scalable background task systems with middleware support, graceful shutdown, and monitoring capabilities.
26//!
27//! This is advanced documentation, for guide level documentation is found on the [website](https://apalis.dev).
28//!
29//! # Core Concepts
30//!
31//! `apalis-core` is built around four primary abstractions that provide a flexible and extensible task processing system:
32//!
33//! - **[`Tasks`](#tasks)**: Type-safe task data structures with processing metadata
34//! - **[`Backends`](#backends)**: Pluggable task storage and streaming implementations
35//! - **[`Workers`](#workers)**: Task processing engines with lifecycle management
36//! - **[`Monitor`](#monitor)**: Multi-worker coordination and observability
37//!
38//! The framework leverages the `tower` service abstraction to provide a rich middleware
39//! ecosystem like error handling, timeouts, rate limiting,
40//! and observability.
41//!
42//!
43//! ## Tasks
44//!
45//! The task struct provides type-safe components for task data and metadata:
46//! - [`Args`](crate::task::task_fn) - The primary structure for the task
47//! - [`ExecutionContext`](crate::task::ExecutionContext) - Backend persisted info on task execution.
48//! - [`TaskContext`](crate::task::context::TaskContext) - Worker provided context for a currently task in execution.
49//! - [`Status`](crate::task::status::Status) - Represents the current state of a task
50//! - [`TaskId`](crate::task::task_id::TaskId) - Unique identifier for task tracking
51//! - [`Attempt`](crate::task::attempt::Attempt) - Retry tracking and attempt information
52//! - [`Extensions`](crate::task::data) - Type-safe storage for additional task data
53//! - [`Metadata`](crate::task::metadata) - metadata associated with the task
54//!
55//! ### Example: Using `TaskBuilder`
56//!
57//! ```ignore
58//! let task: Task<String> = TaskBuilder::new("my-task".to_string())
59//! .id("task-123".into())
60//! .max_attempts(3)
61//! .timeout(Duration::from_secs(30))
62//! .run_in_minutes(10)
63//! .build();
64//! ```
65//! Specific documentation for tasks can be found in the [`task`] and [`task::builder`] modules.
66//!
67//! #### Relevant Guides:
68//! - [**Defining Task arguments**](crate::task::task_fn) - Creating effective task arguments that are scalable and type-safe
69//!
70//! ## Backends
71//!
72//! The [`Backend`](crate::backend::Backend) trait serves as the core abstraction for all task sources.
73//! It defines task polling mechanisms, streaming interfaces, and middleware integration points.
74//!
75//! ### Inbuilt Implementations
76//! - [`MemoryStorage`](crate::backend::memory::MemoryStorage) : In-memory storage based on channels
77//! - [`CustomBackend`](crate::backend::custom) : Flexible backend composition allowing custom functions for task management
78//!
79//! Backends handle task persistence, distribution, and reliability concerns while providing
80//! a uniform interface for worker consumption.
81//!
82//! ## Workers
83//!
84//! The [`Worker`](crate::worker) is the core runtime component responsible for task polling, execution, and lifecycle management:
85//!
86//! ### Worker Lifecycle
87//!
88//! - Workers are responsible for task polling, processing, and lifecycle management.
89//! - Workers can be run as a future or as a stream of events.
90//! - Workers readiness is conditioned on the backend and service (and middleware) being ready.
91//! - This means any blocking middleware eg (concurrency) will block the worker from polling tasks.
92//!
93//! ### Worker Components
94//!
95//! The following are the main components the worker module:
96//!
97//! - [`WorkerBuilder`] - Fluent builder for configuring and constructing workers
98//! - [`Worker`] - Actual worker implementation that processes tasks
99//! - [`WorkerContext`] - Runtime state including task counts and execution status
100//! - [`Event`] - Worker event enumeration (`Start`, `Engage`, `Idle`, `Error`, `Stop`)
101//! - [`Ext`](crate::worker::ext) - Extension traits and middleware for adding functionality to workers
102//!
103//! ### Example: Building and Running a Worker
104//! ```rust
105//! # use apalis_core::worker::{builder::WorkerBuilder, context::WorkerContext};
106//! # use apalis_core::backend::memory::MemoryStorage;
107//! # use apalis_core::error::BoxDynError;
108//! # use std::time::Duration;
109//! # use crate::apalis_core::worker::ext::event_listener::EventListenerExt;
110//! # use crate::apalis_core::backend::TaskSink;
111//! #[tokio::main]
112//! async fn main() {
113//! let mut in_memory = MemoryStorage::new();
114//! in_memory.push(1u32).await.unwrap();
115//!
116//! async fn task(
117//! task: u32,
118//! worker: WorkerContext,
119//! ) -> Result<(), BoxDynError> {
120//! /// Do some work
121//! tokio::time::sleep(Duration::from_secs(1)).await;
122//! worker.stop().unwrap();
123//! Ok(())
124//! }
125//!
126//! let worker = WorkerBuilder::new("rango-tango")
127//! .backend(in_memory)
128//! .on_event(|worker, ev| {
129//! println!("On Event = {:?}, {:?}", ev, worker.name());
130//! })
131//! .build(task);
132//! worker.run().await.unwrap();
133//! }
134//! ```
135//!
136//! Learn more about workers in the [`worker`](crate::worker) and [`worker::builder`](crate::worker::builder) modules.
137//!
138//! #### Relevant Tutorials:
139//! - [**Creating task handlers**](crate::task::task_fn) - Defining task processing functions using the [`TaskFn`] trait
140//!
141//! ## Monitor
142//!
143//! The [`Monitor`](crate::monitor::Monitor) helps manage and coordinate multiple workers:
144//!
145//! **Main Features:**
146//! - **Worker Registry** - Keeps track of active workers
147//! - **Event Handling** - Handles and processes worker events
148//! - **Graceful Shutdown** - Stops all workers together safely
149//! - **Health Monitoring** - Restarts and manages worker health
150//! ### Example: Using `Monitor` with a Worker
151//!
152//! ```rust
153//! # use apalis_core::monitor::Monitor;
154//! # use apalis_core::worker::builder::WorkerBuilder;
155//! # use apalis_core::task::Task;
156//! # use apalis_core::backend::{TaskSink, dequeue::VecDequeBackend};
157//! # use tower::service_fn;
158//! # use std::time::Duration;
159//! # use apalis_core::worker::context::WorkerContext;
160//! #[tokio::main]
161//! async fn main() {
162//! let mut storage = VecDequeBackend::new();
163//! storage.push(1u32).await.unwrap();
164//!
165//! let monitor = Monitor::new()
166//! .on_event(|worker, event| println!("{}: {:?}", worker.name(), event))
167//! .register(move |_| {
168//! WorkerBuilder::new("demo-worker")
169//! .backend(storage.clone())
170//! .build(|req: u32, worker: WorkerContext| async move {
171//! println!("Processing task: {:?}", req);
172//! # worker.stop().unwrap();
173//! Ok::<_, std::io::Error>(req)
174//! })
175//! });
176//!
177//! // Start monitor and run all registered workers
178//! monitor.run().await.unwrap();
179//! }
180//! ```
181//!
182//! Learn more about the monitor in the [`monitor` module](crate::monitor).
183//!
184//! ## Middleware
185//!
186//! Built on the `tower` ecosystem, `apalis-core` provides extensive middleware support like error handling, timeouts, rate limiting, and observability.
187//!
188//! ### Core Middleware
189//!
190//! The following middleware layers are included with their worker extensions:
191//! - [`AcknowledgmentLayer`] - Task acknowledgment after processing
192//! - [`EventListenerLayer`] - Worker event emission and handling
193//! - [`CircuitBreakerLayer`] - Circuit breaker pattern for failure handling
194//! - [`LongRunningLayer`] - Support for tracking long-running tasks
195//!
196//! ### Extending with middleware
197//!
198//! You can write your own middleware to run code before or after a task is processed.
199//!
200//! <details>
201//! <summary>Creating Custom Middleware</summary>
202//!
203//! Here's a simple example of a logging middleware layer:
204//!
205//! ```rust
206//! use apalis_core::task::Task;
207//! use tower::{Layer, Service};
208//! use std::task::{Context, Poll};
209//!
210//! // Define a logging service that wraps another service
211//! pub struct LoggingService<S> {
212//! inner: S,
213//! }
214//!
215//! impl<S, Args, Res, Err> Service<Task<Args>> for LoggingService<S>
216//! where
217//! S: Service<Task<Args>, Response = Res, Error = Err>,
218//! Args: std::fmt::Debug,
219//! {
220//! type Response = Res;
221//! type Error = Err;
222//! type Future = S::Future;
223//!
224//! fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
225//! self.inner.poll_ready(cx)
226//! }
227//!
228//! fn call(&mut self, req: Task<Args>) -> Self::Future {
229//! println!("Processing task: {:?}", req.args);
230//! self.inner.call(req)
231//! }
232//! }
233//!
234//! // Define a layer that wraps services with LoggingService
235//! pub struct LoggingLayer;
236//!
237//! impl<S> Layer<S> for LoggingLayer {
238//! type Service = LoggingService<S>;
239//!
240//! fn layer(&self, service: S) -> Self::Service {
241//! LoggingService { inner: service }
242//! }
243//! }
244//! ```
245//! </details>
246//!
247//! If you want your middleware to do more than just intercept requests and responses, you can use extension traits. See the [`worker::ext`](crate::worker::ext) module for examples.
248//!
249//! ## Error Handling
250//!
251//! `apalis-core` defines a comprehensive error taxonomy for robust error handling:
252//!
253//! - [`AbortError`] - Non-retryable fatal errors requiring immediate termination
254//! - [`RetryAfterError`] - Retryable execution errors triggering retry mechanisms after a delay
255//! - [`DeferredError`] - Retryable execution errors triggering immediate retry
256//!
257//! This error classification enables precise error handling strategies and
258//! appropriate retry behavior for different failure scenarios.
259//!
260//! ## Graceful Shutdown
261//!
262//! `apalis-core` has a reliable graceful shutdown system that makes sure
263//! workers stop safely and all tasks finish before shutting down:
264//!
265//! **Key Features:**
266//! - Task tracking: Workers keep track of how many tasks are running.
267//! - Shutdown control: The system waits until all tasks are finished before shutting down.
268//! - Monitor coordination: A shared [`Shutdown`] token helps all workers stop together.
269//! - Timeout: You can set a time limit for shutdown using [`with_terminator`](crate::monitor::Monitor::with_terminator).
270//!
271//! Learn more about the graceful shutdown process in the [`monitor`](crate::monitor#graceful-shutdown-with-timeout) module.
272//!
273//! # Feature flags
274#![cfg_attr(
275 feature = "docsrs",
276 cfg_attr(doc, doc = ::document_features::document_features!())
277)]
278//!
279//! # Development
280//! `apalis-core` provides comprehensive extensibility mechanisms such as middleware and ext traits.
281//! Beyond there one may want to dive deeper into the following topics:
282//!
283//! - [**Using CustomBackend**](crate::backend::custom) - using custom backend to integrate with already existing systems
284//! - [**Extending Workers using extension traits**](crate::worker::ext#creating-a-custom-worker-extension-trait) - implementing custom worker functionality via extension traits
285//!
286//! # Observability
287//! You can track tasks using [apalis-board](https://github.com/apalis-dev/apalis-board).
288//! 
289//!
290//! [`Backend`]: crate::backend::Backend
291//! [`TaskFn`]: crate::task::task_fn::TaskFn
292//! [`Service`]: tower_service::Service
293//! [`Task`]: crate::task
294//! [`WorkerBuilder`]: crate::worker::builder
295//! [`Worker`]: crate::worker
296//! [`Monitor`]: crate::monitor
297//! [`AcknowledgmentLayer`]: crate::worker::ext::ack
298//! [`EventListenerLayer`]: crate::worker::ext::event_listener
299//! [`CircuitBreakerLayer`]: crate::worker::ext::circuit_breaker
300//! [`LongRunningLayer`]: crate::worker::ext::long_running
301//! [`AbortError`]: crate::error::AbortError
302//! [`RetryAfterError`]: crate::error::RetryAfterError
303//! [`DeferredError`]: crate::error::DeferredError
304//! [`WorkerContext`]: crate::worker::context::WorkerContext
305//! [`Event`]: crate::worker::event::Event
306//! [`ExecutionContext`]: crate::task::ExecutionContext
307//! [`Status`]: crate::task::status::Status
308//! [`TaskId`]: crate::task::task_id::TaskId
309//! [`Attempt`]: crate::task::attempt::Attempt
310//! [`FromRequest`]: crate::task::from_request::FromRequest
311//! [`Shutdown`]: crate::monitor::shutdown::Shutdown
312
313#[macro_use]
314pub(crate) mod macros;
315
316pub mod backend;
317/// Includes internal error types.
318pub mod error;
319
320/// Actively manage and observe workers
321pub mod monitor;
322pub mod task;
323pub mod worker;
324
325/// Layers for building middleware stacks
326pub mod layers {
327 pub use tower_layer::*;
328 pub use tower_service::Service;
329}
330/// Timing and delaying utilities
331#[cfg(feature = "sleep")]
332pub mod timer {
333 pub use futures_timer::Delay;
334 /// Runtime agnostic sleep function based on [Delay]
335 pub async fn sleep(duration: std::time::Duration) {
336 futures_timer::Delay::new(duration).await;
337 }
338}