Skip to main content

Job

Trait Job 

Source
pub trait Job:
    Serialize
    + DeserializeOwned
    + Send
    + Sync
    + 'static {
    const NAME: &'static str;
    const TRIES: u32 = 3;
    const QUEUE: &'static str = "default";
    const UNIQUE_FOR: Duration = _;

    // Required method
    fn handle<'life0, 'life1, 'async_trait>(
        &'life0 self,
        context: &'life1 JobContext,
    ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait;

    // Provided methods
    fn backoff(attempt: u32) -> Duration { ... }
    fn failed<'life0, 'life1, 'life2, 'async_trait>(
        &'life0 self,
        context: &'life1 JobContext,
        error: &'life2 Error,
    ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait,
             'life2: 'async_trait { ... }
    fn unique_id(&self) -> Option<String> { ... }
}
Expand description

Work to be done later.

use rainier_queue::{Job, JobContext};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct SendWelcomeEmail {
    user_id: u64,
}

#[async_trait::async_trait]
impl Job for SendWelcomeEmail {
    const NAME: &'static str = "mail.welcome";

    async fn handle(&self, _context: &JobContext) -> rainier_support::Result<()> {
        println!("emailing user {}", self.user_id);
        Ok(())
    }
}

Required Associated Constants§

Source

const NAME: &'static str

The job’s stable name on the wire.

Must not change once jobs of this type exist in a queue, and must be unique across the application.

Provided Associated Constants§

Source

const TRIES: u32 = 3

How many times to attempt the job before giving up.

Source

const QUEUE: &'static str = "default"

The queue this job goes on by default.

Source

const UNIQUE_FOR: Duration = _

How long the uniqueness lock is held if the job never finishes.

The safety net for a worker that dies mid-job: without it, one crash would block that job’s id forever. Set it longer than the job takes.

Required Methods§

Source

fn handle<'life0, 'life1, 'async_trait>( &'life0 self, context: &'life1 JobContext, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Do the work.

Returning Err releases the job for another attempt, until TRIES is exhausted and it is marked failed.

Provided Methods§

Source

fn backoff(attempt: u32) -> Duration

How long to wait before retrying after attempt failures.

Exponential by default — 1s, 2s, 4s, … — because the usual reason a job fails is a dependency that is briefly unavailable, and retrying at full speed makes that worse.

Source

fn failed<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, context: &'life1 JobContext, error: &'life2 Error, ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Called after the final attempt fails. For recording the failure somewhere the application will notice.

Source

fn unique_id(&self) -> Option<String>

What makes two of these job “the same one”, or None for jobs that are never duplicates.

Laravel’s ShouldBeUnique. Returning Some makes dispatch take an atomic lock on this id, so a second dispatch while the first is still pending is dropped rather than queued.

ⓘ
impl Job for RebuildSearchIndex {
    const NAME: &'static str = "search.rebuild";

    // One rebuild at a time, whatever asks for it.
    fn unique_id(&self) -> Option<String> {
        Some(String::new())
    }
}

impl Job for SendInvoice {
    const NAME: &'static str = "billing.invoice";

    // One per invoice, but two different invoices are two jobs.
    fn unique_id(&self) -> Option<String> {
        Some(self.invoice_id.to_string())
    }
}

The lock key is NAME and this id, so two job types returning the same id do not collide.

§This deduplicates dispatch, not execution

The lock is released when the job finishes, so it stops a queue filling with a hundred copies of the same work. It does not stop two workers running one job twice — that is the queue’s reservation, a different mechanism — and it does not survive the lock expiring under a job that overran UNIQUE_FOR.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§