Job

An async / distributed job runner for Rust applications with Postgres backend.
Uses sqlx for interfacing with the DB.
Features
- Async job execution with PostgreSQL backend
- Job scheduling and rescheduling
- Configurable retry logic with exponential backoff
- Built-in job tracking and monitoring
Usage
Add this to your Cargo.toml:
[dependencies]
job = "0.1"
Basic Example
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use job::*;
#[derive(Debug, Serialize, Deserialize)]
struct MyJobConfig {
delay_ms: u64,
}
impl JobConfig for MyJobConfig {
type Initializer = MyJobInitializer;
}
struct MyJobInitializer;
impl JobInitializer for MyJobInitializer {
fn job_type() -> JobType {
JobType::new("my_job")
}
fn init(&self, job: &Job) -> Result<Box<dyn JobRunner>, Box<dyn std::error::Error>> {
let config: MyJobConfig = job.config()?;
Ok(Box::new(MyJobRunner { config }))
}
}
struct MyJobRunner {
config: MyJobConfig,
}
#[async_trait]
impl JobRunner for MyJobRunner {
async fn run(
&self,
_current_job: CurrentJob,
) -> Result<JobCompletion, Box<dyn std::error::Error>> {
tokio::time::sleep(tokio::time::Duration::from_millis(self.config.delay_ms)).await;
println!("Job completed!");
Ok(JobCompletion::Complete)
}
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let pool = sqlx::PgPool::connect("postgresql://user:pass@localhost/db").await?;
let config = JobsSvcConfig::builder()
.pg_con("postgresql://user:pass@localhost/db")
.build()
.expect("Could not build JobSvcConfig");
let mut jobs = Jobs::init(config).await?;
jobs.add_initializer(MyJobInitializer);
jobs.start_poll().await?;
let job_config = MyJobConfig { delay_ms: 1000 };
let job_id = JobId::new();
let job = jobs.create_and_spawn(job_id, job_config).await?;
tokio::time::sleep(tokio::time::Duration::from_millis(5000)).await;
let job = jobs.find(job_id).await?;
assert!(job.completed());
Ok(())
}
Setup
In order to use the jobs crate migrations need to run on Postgres to initialize the tables.
You can either let the library run them, copy them into your project or add them to your migrations via code.
Option 1.
Let the library run the migrations - this is useful when you are not using sqlx in the rest of your project.
To avoid compilation errors set export SQLX_OFFLINE=true in your dev shell.
let config = JobsSvcConfig::builder()
.pool(sqlx::PgPool::connect("postgresql://user:pass@localhost/db")
.exec_migration(true)
.build()
.expect("Could not build JobSvcConfig");
Option 2.
If you are using sqlx you can copy the migration file into your project:
cp ./migrations/20250904065521_job_setup.sql <path>/<to>/<your>/<project>/migrations/
Option 3.
You can also add the job migrations in code when you run your own migrations without copying the file:
use job::IncludeMigrations;
sqlx::migrate!().include_job_migrations().run(&pool).await?;
License
Licensed under the Apache License, Version 2.0.