Jobs are structs; the queue is a setting.
A job is a serialisable struct with its name, queue and retry count as constants. Dispatch it, and a worker runs it on whichever driver the environment chose.
What you get
- Drivers: sync, memory, database, Redis, SQS and Kafka.
- The
kubernetesdriver runs a job as its ownbatch/v1Job pod whenJob::kubernetes()returns a spec. - Every failed attempt is a
JobAttemptFailedevent, withfinal_attemptset on the last. QueueManager::fake()records dispatches for tests:assert_pushed::<NotifyAuthor>().- Honest limit: the Redis queue can lose a job it has accepted.
In the docs
src/app/jobs/notify_author.rs
#[derive(Debug, Serialize, Deserialize)]
pub struct NotifyAuthor {
pub post_id: u64,
}
#[async_trait]
impl Job for NotifyAuthor {
const NAME: &'static str = "app.notify-author";
const QUEUE: &'static str = "mail";
const TRIES: u32 = 5;
async fn handle(&self, context: &JobContext) -> Result<()> {
let posts = context.resolve::<PostRepository>()?;
let users = context.resolve::<UserRepository>()?;
let Some(post) = posts.find(self.post_id.into()).await? else {
return Ok(()); // deleted since it was queued: nothing to say
};
let Some(author) = users.find(post.author_id.into()).await? else {
return Ok(());
};
context.resolve::<Notifier>()?.send(&author, &PostLive { post }).await?;
Ok(())
}
}
src/app/providers/app_provider.rs
events.listen(|event: Arc<PostPublished>| async move {
Queue::instance().dispatch(NotifyAuthor { post_id: event.post.id }).await?;
Ok(())
});
src/app/jobs/transcode_video.rs
use rainier_framework::queue::KubernetesJobSpec;
#[async_trait]
impl Job for TranscodeVideo {
const NAME: &'static str = "media.transcode";
const QUEUE: &'static str = "transcodes";
// Return a spec and the `kubernetes` queue driver runs this job as its
// own `batch/v1` Job pod, sized for it. `None` (the default) runs it
// on an ordinary worker like any other job.
fn kubernetes(&self) -> Option<KubernetesJobSpec> {
Some(KubernetesJobSpec::new("4Gi", "6Gi", "250m", "1").with_tmpfs_scratch())
}
async fn handle(&self, context: &JobContext) -> Result<()> {
/* … */
Ok(())
}
}
Up close
Select an image to see it full size.