56 releases (5 breaking)
| 0.6.5 | Jul 29, 2026 |
|---|---|
| 0.6.0-rc.4 | Jun 29, 2026 |
| 0.5.1-alpha.3 | Mar 31, 2026 |
#1168 in Database interfaces
686 downloads per month
Used in awa-seaorm
2MB
46K
SLoC
awa
Postgres-native background job queue. Transactional enqueue, heartbeat crash recovery, priority aging, retries with backoff, cron, callbacks, unique jobs, dead-letter queue, and a vacuum-aware storage engine designed to keep dead-tuple pressure bounded under sustained load.
This crate is the user-facing facade. It re-exports the worker (awa-worker) and model (awa-model) crates and is what most Rust applications depend on directly.
[dependencies]
awa = "0.6"
Quick start
use awa::{insert_with, Client, InsertOpts, JobArgs, JobResult, QueueConfig};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, JobArgs)]
struct SendEmail {
to: String,
subject: String,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let pool = sqlx::PgPool::connect(&std::env::var("DATABASE_URL")?).await?;
let client = Client::builder(pool.clone())
.queue("email", QueueConfig::default())
.register::<SendEmail, _, _>(|args, _ctx| async move {
println!("sending to {}: {}", args.to, args.subject);
Ok(JobResult::Completed)
})
.build()?;
client.start().await?;
insert_with(
&pool,
&SendEmail {
to: "ada@example.com".into(),
subject: "hello".into(),
},
InsertOpts {
queue: "email".into(),
..Default::default()
},
)
.await?;
Ok(())
}
What you get
- Transactional enqueue — enqueueing a job is a normal
INSERTyou can commit alongside your application's writes. - Vacuum-aware storage — append-only ready/terminal partitions plus rotating lease and receipt rings keep the hot queue tables' dead-tuple footprint bounded under sustained load. See ADR-019 and ADR-023.
- Crash-safe execution — heartbeat-based lease tracking; jobs whose workers vanish are rescued automatically.
- Per-queue policy — priorities, priority aging, weighted concurrency, rate limits, deadlines, retry/backoff, cron, dead-letter queue.
- Unique jobs — content-keyed deduplication windowed across pending / running / completed.
- Callbacks and external waits — wait for an external event without burning a worker slot.
- First-class Python bindings — same engine, same SQL, same defaults; see awa-pg on PyPI.
Partitioned FIFO and ordering keys
Queues default to strict FIFO per (queue, priority). Operators can raise awa.queue_meta.enqueue_shards on a contended queue to trade strict FIFO for throughput; the contract then becomes partitioned FIFO — strict order within each shard, no ordering promised across shards. Producers can pin related jobs to the same shard with InsertOpts::ordering_key:
let opts = InsertOpts {
queue: "customer-updates".into(),
ordering_key: Some(format!("customer-{customer_id}").into_bytes()),
..Default::default()
};
awa::insert_with(&pool, &UpdateCustomer { ... }, opts).await?;
Jobs sharing an ordering_key always pick the same shard, so the shard's strict FIFO carries over to per-key FIFO. At enqueue_shards = 1 the key is ignored. See ADR-025 for the full contract.
Partitioned Queues
For very hot workloads, PartitionedQueue gives Rust producers and workers the same deterministic physical queue set for one logical queue:
use awa::{InsertOpts, QueueConfig, PartitionedQueue};
let updates = PartitionedQueue::new("customer-updates", 4)?;
let client = awa::Client::builder(pool.clone())
.partitioned_queue(&updates, QueueConfig {
max_workers: 32, // per physical queue
..Default::default()
})
.register::<UpdateCustomer, _, _>(handle_update)
.build()?;
let opts = updates.route_opts_by_key(
InsertOpts::default(),
format!("customer-{customer_id}").into_bytes(),
);
awa::insert_with(&pool, &UpdateCustomer { ... }, opts).await?;
This is ordinary Awa queueing under the hood: each physical queue keeps its durable claim, lease, retry, DLQ, and completion behavior. The helper only standardizes naming and routing so applications do not hand-roll partitioning.
Documentation
- Getting started (Rust)
- Configuration
- Architecture
- Migrations
- Upgrading 0.5.x → 0.6
- Dead Letter Queue
- Deployment
- Cross-system benchmark comparison
License
Dual-licensed under MIT or Apache-2.0, at your option.
Dependencies
~52–72MB
~1M SLoC