Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

When to Use Graphile Worker RS

Graphile Worker RS is a PostgreSQL-backed job queue for Rust applications. Use it when your application already depends on PostgreSQL and you want background work to be stored, coordinated, retried, and scheduled through the same database instead of a separate queue service.

It is based on Graphile Worker and is designed for jobs such as sending emails, performing calculations, generating PDFs, scheduling future work, and running recurring tasks.

Good Fit

Graphile Worker RS is a good fit when you need one or more of these properties:

  • Background jobs that survive process restarts because they are persisted in PostgreSQL.
  • Low-latency job pickup using PostgreSQL LISTEN/NOTIFY.
  • Concurrent job processing coordinated through PostgreSQL SKIP LOCKED.
  • Automatic retries for failed jobs, including exponential backoff.
  • Scheduled jobs that should run later.
  • Cron-like recurring jobs.
  • Type-safe Rust task handlers with serde payloads.
  • A worker that can run inside an existing Rust async application.
  • Optional worker recovery for deployments where jobs may remain locked after a crash, network partition, forced abort, or orchestrator shutdown.

The common case is an application that already writes application data to PostgreSQL and wants to enqueue follow-up work close to that data.

use graphile_worker::{IntoTaskHandlerResult, TaskHandler, WorkerContext};
use serde::{Deserialize, Serialize};

#[derive(Deserialize, Serialize)]
struct SendEmail {
    to: String,
    subject: String,
}

impl TaskHandler for SendEmail {
    const IDENTIFIER: &'static str = "send_email";

    async fn run(self, _ctx: WorkerContext) -> impl IntoTaskHandlerResult {
        println!("send '{}' to {}", self.subject, self.to);
        Ok::<(), String>(())
    }
}

Typical Use Cases

User-Facing Background Work

Move slow or unreliable work out of HTTP request handling:

  • Send transactional email after signup or payment.
  • Generate a PDF, image, report, or export after the user requests it.
  • Perform expensive calculations after saving the primary database record.
  • Notify another system without blocking the response path.

Scheduled Work

Use scheduled jobs when the job should run at a known time in the future:

// Build the job with its payload and scheduling options, then enqueue it with
// WorkerUtils from your application code.
let job = SendEmail {
    to: "customer@example.com".to_owned(),
    subject: "Your report is ready".to_owned(),
};

Recurring Work

Use cron-like recurring tasks for periodic maintenance or synchronization:

  • Send daily reports.
  • Reconcile records on a schedule.
  • Poll an integration periodically.
  • Clean up old data.

Graphile Worker RS exposes crontab parsing and cron runner types from the main crate, so recurring jobs can live beside the rest of your worker configuration.

Database-Centered Workflows

The crate description explicitly calls out jobs generated by PostgreSQL triggers or functions. This is useful when the database is the source of truth for state transitions and the application needs a Rust worker to process the resulting work queue.

Prerequisites

Before choosing Graphile Worker RS, make sure these assumptions match your project:

  • You can provide a PostgreSQL database connection.
  • Your application can run a Rust async runtime. Tokio is enabled by default, and async-std is available through the runtime-async-std feature.
  • Your job payloads can be serialized and deserialized with serde.
  • You can register every task handler with a stable identifier string.
  • You are comfortable letting the worker manage its own PostgreSQL schema. The default schema name is graphile_worker.
  • You can choose the database driver and TLS features that match your deployment. The default features include Tokio, rustls TLS, and the sqlx driver.

A minimal Tokio setup looks like this:

graphile_worker::WorkerOptions::default()
    .concurrency(5)
    .schema("graphile_worker")
    .define_job::<SendEmail>()
    .pg_pool(pg_pool)
    .init()
    .await?
    .run()
    .await?;

When to Use Recovery

Worker recovery is useful for deployments where jobs can be interrupted by events outside the normal graceful shutdown path, such as a process crash, network partition, forced abort, or orchestrator shutdown.

When enabled, workers record heartbeats in PostgreSQL. A sweeper can then find workers that have stopped heartbeating, unlock their jobs, release queue locks, and delay the recovered jobs before retrying them.

use graphile_worker::{WorkerOptions, WorkerRecoveryConfig};
use std::time::Duration;

let recovery = WorkerRecoveryConfig::default()
    .enabled(true)
    .heartbeat_interval(Duration::from_secs(30))
    .sweep_interval(Duration::from_secs(60))
    .sweep_threshold(Duration::from_secs(300))
    .recovery_delay(Duration::from_secs(30));

let worker = WorkerOptions::default()
    .worker_recovery(recovery)
    .define_job::<SendEmail>()
    .pg_pool(pg_pool)
    .init()
    .await?;

Recovery is disabled by default, so enable it deliberately for deployments that need stale worker detection and job lock recovery.

Bad Fit

Graphile Worker RS may not be the right tool when:

  • Your application does not use PostgreSQL and you do not want PostgreSQL to be part of the job system.
  • You need an in-memory-only queue where jobs can be discarded on restart.
  • You need a general message broker that is independent of your database.
  • Your workers are not Rust applications.
  • Your workload cannot be represented as registered task identifiers with serializable payloads.
  • You need to avoid database-managed queue state entirely.

It is also not a replacement for normal request handling. If work must finish before the response can be sent, keep it in the request path. Use Graphile Worker RS when the work can run asynchronously and be retried independently.

Decision Checklist

Choose Graphile Worker RS when most of these are true:

  • PostgreSQL is already a required dependency.
  • The job must survive application restarts.
  • Failed jobs should be retried automatically.
  • Jobs can be described with a task identifier and a serialized payload.
  • Running the worker in a Rust async process fits your deployment.
  • Keeping queue state near application data is simpler than operating another queue service.