pub struct PersistentJobQueue { /* private fields */ }jobs and (crate features model-mysql or model-postgres or model-sqlite) only.Expand description
A job queue backed by the model layer (model-sqlite / model-postgres /
model-mysql): jobs are written to a rws_jobs table before being
acknowledged, so pending and retrying jobs survive a process crash or
restart, and several replicas can share one queue.
Unlike JobQueue, a job here can’t be an arbitrary
closure — a closure can’t be serialized to disk. Instead the queue stores
(job_type, payload) as plain text and dispatches to a handler
registered for that job_type at execution time. Register every handler
you enqueue before calling start, including after a
restart — unprocessed rows from a previous run (and any row left
running when the process crashed) are picked up automatically.
Since 18.35.0 (#93) a job can wait until a time, go to a named queue
with a priority, be unique (EnqueueOptions); a job that exhausts its
retries stays in the table as failed — the dead-letter list
(failed_jobs, retry,
on_dead); and schedule runs a job
on a cron schedule once per due time across every replica.
CREATE TABLE IF NOT EXISTS rws_jobs (
id VARCHAR(64) PRIMARY KEY,
job_type TEXT NOT NULL,
payload TEXT NOT NULL DEFAULT '',
status TEXT NOT NULL DEFAULT 'pending', -- pending | running | failed
attempts INTEGER NOT NULL DEFAULT 0,
max_retries INTEGER NOT NULL DEFAULT 3,
next_run_at BIGINT NOT NULL,
created_at BIGINT NOT NULL,
last_error TEXT,
queue VARCHAR(64) NOT NULL DEFAULT 'default',
priority BIGINT NOT NULL DEFAULT 0,
unique_key VARCHAR(191)
)A table from before 18.35.0 gets the last three columns added. Completed jobs are deleted.
Implementations§
Source§impl PersistentJobQueue
impl PersistentJobQueue
Sourcepub async fn new(pool: DbPool) -> Result<Self, DbError>
pub async fn new(pool: DbPool) -> Result<Self, DbError>
Creates the queue and its backing rws_jobs table if absent (adding
the 18.35.0 columns to an older one), then resets any row left
running (interrupted by a previous crash) back to pending so it
gets picked up again.
Sourcepub fn max_retries(self, n: u32) -> Self
pub fn max_retries(self, n: u32) -> Self
Override the default retry count applied to jobs enqueued via
enqueue (default: 3). Use
EnqueueOptions::max_retries for a per-job override.
Sourcepub fn poll_interval(self, interval: Duration) -> Self
pub fn poll_interval(self, interval: Duration) -> Self
Override how often idle workers poll for due jobs (default: 500 ms).
Sourcepub fn backoff(self, initial: Duration, multiplier: u32) -> Self
pub fn backoff(self, initial: Duration, multiplier: u32) -> Self
Override the backoff policy applied between retries: initial delay
before the first retry, doubled (or scaled by multiplier) on each
subsequent attempt. Default: 500 ms initial, 2x multiplier.
Sourcepub fn queues(self, names: &[&str]) -> Self
pub fn queues(self, names: &[&str]) -> Self
Only run jobs from these named queues (default: every queue) — one
process for mailers, another for the rest.
Sourcepub fn schedule(
self,
name: &str,
cron: &str,
job_type: &str,
payload: &str,
) -> Result<Self, String>
pub fn schedule( self, name: &str, cron: &str, job_type: &str, payload: &str, ) -> Result<Self, String>
Enqueue job_type with payload on the cron schedule cron (six
fields, seconds first, UTC; see Scheduler).
Every replica can declare the same schedule: each due time is
claimed by exactly one of them (a compare-and-set on the
rws_job_schedules row named name), which enqueues the job.
Sourcepub fn schedule_with(
self,
name: &str,
cron: &str,
job_type: &str,
payload: &str,
options: EnqueueOptions,
) -> Result<Self, String>
pub fn schedule_with( self, name: &str, cron: &str, job_type: &str, payload: &str, options: EnqueueOptions, ) -> Result<Self, String>
schedule with options for the enqueued jobs.
Sourcepub fn on_dead(&self, hook: impl Fn(&JobInfo) + Send + Sync + 'static)
pub fn on_dead(&self, hook: impl Fn(&JobInfo) + Send + Sync + 'static)
Call hook with each job that exhausts its retries, as it moves to
the dead-letter list (status = 'failed').
Sourcepub fn register(
&self,
job_type: &str,
handler: impl Fn(&str) -> Result<(), String> + Send + Sync + 'static,
)
pub fn register( &self, job_type: &str, handler: impl Fn(&str) -> Result<(), String> + Send + Sync + 'static, )
Register the function that executes jobs of job_type. Call before
start — jobs enqueued for an unregistered job_type
fail immediately (and retry, same as any other handler error).
Sourcepub async fn enqueue(
&self,
job_type: &str,
payload: &str,
) -> Result<String, DbError>
pub async fn enqueue( &self, job_type: &str, payload: &str, ) -> Result<String, DbError>
Persist a new job using the queue’s defaults. Returns the job id.
Sourcepub async fn enqueue_with_retries(
&self,
job_type: &str,
payload: &str,
max_retries: u32,
) -> Result<String, DbError>
pub async fn enqueue_with_retries( &self, job_type: &str, payload: &str, max_retries: u32, ) -> Result<String, DbError>
Persist a new job with a per-job retry override.
Sourcepub async fn enqueue_with(
&self,
job_type: &str,
payload: &str,
options: EnqueueOptions,
) -> Result<String, DbError>
pub async fn enqueue_with( &self, job_type: &str, payload: &str, options: EnqueueOptions, ) -> Result<String, DbError>
Persist a new job with options (18.35.0). Returns its id — for a
unique job already pending or running, that
job’s id, and nothing is inserted.
Sourcepub async fn tick(&self) -> Result<bool, DbError>
pub async fn tick(&self) -> Result<bool, DbError>
Run one poll-claim-execute cycle. Returns Ok(true) if a job was
claimed (whether it succeeded, failed, or is retrying), Ok(false) if
none was due. Exposed for tests and for embedding in a caller-owned
loop; most applications should use start instead.
Sourcepub async fn tick_schedules(&self) -> Result<usize, DbError>
pub async fn tick_schedules(&self) -> Result<usize, DbError>
Enqueue the scheduled jobs that are due, each due time once across
every process sharing the database. Returns how many this call
enqueued. start’s first worker calls it about once a
second; exposed for tests and caller-owned loops.
Sourcepub async fn jobs(
&self,
status: Option<&str>,
limit: usize,
) -> Result<Vec<JobInfo>, DbError>
pub async fn jobs( &self, status: Option<&str>, limit: usize, ) -> Result<Vec<JobInfo>, DbError>
Jobs with status (pending, running, failed; None: all),
highest priority and earliest due first, at most limit.
Sourcepub async fn failed_jobs(&self, limit: usize) -> Result<Vec<JobInfo>, DbError>
pub async fn failed_jobs(&self, limit: usize) -> Result<Vec<JobInfo>, DbError>
The dead-letter list: jobs that exhausted their retries.
Sourcepub async fn retry(&self, id: &str) -> Result<bool, DbError>
pub async fn retry(&self, id: &str) -> Result<bool, DbError>
Put a failed job back in the queue, due now, with its attempts reset.
false when there’s no failed job id.
Sourcepub async fn delete(&self, id: &str) -> Result<bool, DbError>
pub async fn delete(&self, id: &str) -> Result<bool, DbError>
Delete a job that isn’t running. false when there’s no such job.
Sourcepub fn start(self: Arc<Self>, workers: usize) -> Vec<JoinHandle<()>>
pub fn start(self: Arc<Self>, workers: usize) -> Vec<JoinHandle<()>>
Spawn workers background OS threads, each running its own
single-threaded Tokio runtime, polling for due jobs at
poll_interval (default: 500 ms) whenever the
queue is empty; the first also enqueues scheduled
jobs. Returns the thread handles; call stop then join
them for a graceful shutdown.
Auto Trait Implementations§
impl !Freeze for PersistentJobQueue
impl !RefUnwindSafe for PersistentJobQueue
impl !UnwindSafe for PersistentJobQueue
impl Forget for PersistentJobQueue
impl Send for PersistentJobQueue
impl Sync for PersistentJobQueue
impl Unpin for PersistentJobQueue
impl UnsafeUnpin for PersistentJobQueue
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Pointable for T
impl<T> Pointable for T
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<T> ServiceExt for T
impl<T> ServiceExt for T
Source§fn map_response_body<F>(self, f: F) -> MapResponseBody<Self, F>where
Self: Sized,
fn map_response_body<F>(self, f: F) -> MapResponseBody<Self, F>where
Self: Sized,
map-response-body only.Source§fn trace_for_http(self) -> Trace<Self, SharedClassifier<ServerErrorsAsFailures>>where
Self: Sized,
fn trace_for_http(self) -> Trace<Self, SharedClassifier<ServerErrorsAsFailures>>where
Self: Sized,
trace only.Source§fn trace_for_grpc(self) -> Trace<Self, SharedClassifier<GrpcErrorsAsFailures>>where
Self: Sized,
fn trace_for_grpc(self) -> Trace<Self, SharedClassifier<GrpcErrorsAsFailures>>where
Self: Sized,
trace only.