background_jobs_core/
lib.rs

1#![deny(missing_docs)]
2
3//! # Background Jobs Core
4//! _basic types and traits for implementing a background jobs processor_
5//!
6//! This crate shouldn't be depended on directly, except in the case of implementing a custom jobs
7//! processor. For a default solution based on Actix and Sled, look at the `background-jobs` crate.
8
9mod box_error;
10mod catch_unwind;
11mod job;
12mod job_info;
13mod processor_map;
14mod storage;
15mod unsend_job;
16
17pub use crate::{
18    box_error::BoxError,
19    job::{new_job, new_scheduled_job, process, Job},
20    job_info::{JobInfo, NewJobInfo, ReturnJobInfo},
21    processor_map::{CachedProcessorMap, ProcessorMap},
22    storage::{memory_storage, Storage},
23};
24
25pub use unsend_job::{JoinError, UnsendJob, UnsendSpawner};
26
27#[derive(Debug, thiserror::Error)]
28/// The error type returned by the `process` method
29pub enum JobError {
30    /// Some error occurred while processing the job
31    #[error("{0}")]
32    Processing(#[from] BoxError),
33
34    /// Creating a `Job` type from the provided `serde_json::Value` failed
35    #[error("Could not make JSON value from arguments")]
36    Json,
37
38    /// This job type was not registered for this client
39    #[error("This job type was not registered for the client")]
40    Unregistered,
41}
42
43#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
44/// Indicate the state of a job after an attempted run
45pub enum JobResult {
46    /// The job succeeded
47    Success,
48
49    /// The job failed
50    Failure,
51
52    /// The worker had no concept of this job
53    Unregistered,
54
55    /// The worker requesting this job closed
56    Unexecuted,
57}
58
59impl JobResult {
60    /// Indicate a successful job
61    pub const fn success() -> Self {
62        JobResult::Success
63    }
64
65    /// Indicate a failed job
66    pub const fn failure() -> Self {
67        JobResult::Failure
68    }
69
70    /// Indicate that the job was not registered for this worker
71    pub const fn unregistered() -> Self {
72        JobResult::Unregistered
73    }
74
75    /// Check if the job failed
76    pub const fn is_failure(self) -> bool {
77        matches!(self, JobResult::Failure)
78    }
79
80    /// Check if the job succeeded
81    pub const fn is_success(self) -> bool {
82        matches!(self, JobResult::Success)
83    }
84
85    /// Check if the job is missing it's processor
86    pub const fn is_unregistered(self) -> bool {
87        matches!(self, JobResult::Unregistered)
88    }
89
90    /// Check if the job was returned without an execution attempt
91    pub const fn is_unexecuted(self) -> bool {
92        matches!(self, JobResult::Unexecuted)
93    }
94}
95
96#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
97/// Different styles for retrying jobs
98pub enum Backoff {
99    /// Seconds between execution
100    ///
101    /// For example, `Backoff::Linear(5)` will retry a failed job 5 seconds after the previous
102    /// attempt
103    Linear(usize),
104
105    /// Base for seconds between execution
106    ///
107    /// For example, `Backoff::Exponential(2)` will retry a failed job 2 seconds after the first
108    /// failure, 4 seconds after the second failure, 8 seconds after the third failure, and 16
109    /// seconds after the fourth failure
110    Exponential(usize),
111}
112
113#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
114/// How many times a job should be retried before giving up
115pub enum MaxRetries {
116    /// Keep retrying forever
117    Infinite,
118
119    /// Put a limit on the number of retries
120    Count(usize),
121}
122
123impl MaxRetries {
124    fn compare(self, retry_count: u32) -> ShouldStop {
125        match self {
126            MaxRetries::Infinite => ShouldStop::Requeue,
127            MaxRetries::Count(count) => {
128                if (retry_count as usize) <= count {
129                    ShouldStop::Requeue
130                } else {
131                    ShouldStop::LimitReached
132                }
133            }
134        }
135    }
136}
137
138#[derive(Clone, Copy, Debug, Eq, PartialEq)]
139/// A type that represents whether a job should be requeued
140pub enum ShouldStop {
141    /// The job has hit the maximum allowed number of retries, and should be failed permanently
142    LimitReached,
143
144    /// The job is allowed to be put back into the job queue
145    Requeue,
146}
147
148impl ShouldStop {
149    /// A boolean representation of this state
150    pub const fn should_requeue(&self) -> bool {
151        matches!(self, ShouldStop::Requeue)
152    }
153}
154
155impl From<serde_json::error::Error> for JobError {
156    fn from(_: serde_json::error::Error) -> Self {
157        JobError::Json
158    }
159}