# 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.
```rust,ignore
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:
```rust,ignore
// 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:
```rust,ignore
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.
```rust,ignore
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.