Postgres-native background job queue — Python SDK with async/sync workers, transactional enqueue, progress tracking, and web UI
Project description
Awa
Postgres-native background job queue for Rust and Python.
Awa (Māori: river) provides durable, transactional job enqueueing with typed handlers in both Rust and Python. All queue state lives in Postgres — no Redis, no RabbitMQ. The Rust runtime handles polling, heartbeating, crash recovery, and dispatch. Python workers run on that same runtime via PyO3, getting Rust-grade reliability with Python-native ergonomics.
Features
- Postgres-only — one dependency you already have.
- Transactional enqueue — insert jobs inside your business transaction. Commit = visible. Rollback = gone.
- Cancel by unique key — cancel scheduled jobs by their insert-time components (kind + args) without storing job IDs.
- Rust and Python workers — same queues, identical semantics, mixed deployments.
- Crash recovery — heartbeat + hard deadline rescue. Stale jobs recovered automatically.
- Web UI — dashboard, job inspector, queue management, cron controls.
- Structured progress — handlers report percent, message, and checkpoint metadata; persisted across retries.
- Periodic/cron jobs — leader-elected scheduler with timezone support and atomic enqueue.
- Webhook callbacks — park jobs for external completion with optional CEL expression filtering.
- LISTEN/NOTIFY wakeup — sub-10ms pickup latency.
- OpenTelemetry — 20 built-in metrics (counters, histograms, gauges) for Prometheus/Grafana.
- Hot/cold storage — runnable work in a hot table, deferred work in a cold table.
- Rate limiting — per-queue token bucket. Weighted concurrency — global worker pool with per-queue guarantees.
Local benchmarks show ~8k jobs/sec sustained throughput (Rust workers), ~5k jobs/sec (Python workers), and sub-10ms p50 pickup latency. See benchmarking notes for methodology and caveats.
Core concurrency invariants (no duplicate processing after rescue, stale completions rejected, shutdown drain ordering) are checked with TLA+ models covering single and multi-instance deployments.
Getting Started
# 1. Install
pip install awa-pg awa-cli # Python
# or: cargo add awa # Rust
# 2. Start Postgres and run migrations
awa --database-url $DATABASE_URL migrate
# 3. Write a worker and start processing (see examples below)
# 4. Monitor
awa --database-url $DATABASE_URL serve # → http://127.0.0.1:3000
Language-specific guides:
Python Example
import awa
import asyncio
from dataclasses import dataclass
@dataclass
class SendEmail:
to: str
subject: str
async def main():
client = awa.Client("postgres://localhost/mydb")
await client.migrate()
@client.worker(SendEmail, queue="email")
async def handle_email(job):
print(f"Sending to {job.args.to}: {job.args.subject}")
await client.insert(SendEmail(to="alice@example.com", subject="Welcome"))
client.start([("email", 2)])
await asyncio.sleep(1)
await client.shutdown()
asyncio.run(main())
Progress tracking — checkpoint and resume on retry:
@client.worker(BatchImport, queue="etl")
async def handle_import(job):
last_id = (job.progress or {}).get("metadata", {}).get("last_id", 0)
for item in fetch_items(after=last_id):
process(item)
job.set_progress(50, "halfway")
job.update_metadata({"last_id": item.id})
await job.flush_progress()
Transactional enqueue — atomic with your business logic:
async with await client.transaction() as tx:
await tx.execute("INSERT INTO orders (id) VALUES ($1)", order_id)
await tx.insert(SendEmail(to="alice@example.com", subject="Order confirmed"))
Sync API for Django/Flask — every async method has a _sync variant:
client = awa.Client("postgres://localhost/mydb")
client.migrate_sync()
job = client.insert_sync(SendEmail(to="bob@example.com", subject="Hello"))
See awa-python/examples/ for complete runnable scripts tested in CI.
Rust Example
use awa::{Client, QueueConfig, JobArgs, JobResult, JobError, JobContext, Worker};
use serde::{Serialize, Deserialize};
#[derive(Debug, Serialize, Deserialize)]
struct SendEmail {
to: String,
subject: String,
}
impl JobArgs for SendEmail {
fn kind() -> &'static str { "send_email" }
}
struct SendEmailWorker;
#[async_trait::async_trait]
impl Worker for SendEmailWorker {
fn kind(&self) -> &'static str { "send_email" }
async fn perform(&self, ctx: &JobContext) -> Result<JobResult, JobError> {
let args: SendEmail = serde_json::from_value(ctx.job.args.clone())
.map_err(|e| JobError::terminal(e.to_string()))?;
send_email(&args.to, &args.subject).await
.map_err(JobError::retryable)?;
Ok(JobResult::Completed)
}
}
// Insert a job (with uniqueness)
awa::insert_with(&pool, &SendEmail { to: "alice@example.com".into(), subject: "Welcome".into() },
awa::InsertOpts { unique: Some(awa::UniqueOpts { by_args: true, ..Default::default() }), ..Default::default() },
).await?;
// Cancel by unique key (e.g., when the triggering condition is resolved)
awa::admin::cancel_by_unique_key(&pool, "send_email", None, Some(&serde_json::json!({"to": "alice@example.com", "subject": "Welcome"})), None).await?;
// Start workers with a typed lifecycle hook
let client = Client::builder(pool)
.queue("default", QueueConfig::default())
.register_worker(SendEmailWorker)
.on_event::<SendEmail, _, _>(|event| async move {
if let awa::JobEvent::Exhausted { args, error, .. } = event {
tracing::error!(to = %args.to, error = %error, "email job exhausted retries");
}
})
.build()?;
client.start().await?;
Installation
Python
pip install awa-pg # SDK: insert, worker, admin, progress
pip install awa-cli # CLI: migrations, queue admin, web UI
Rust
[dependencies]
awa = "0.3"
CLI
Available via pip (no Rust toolchain needed) or cargo:
pip install awa-cli
# or: cargo install awa-cli
awa --database-url $DATABASE_URL migrate
awa --database-url $DATABASE_URL serve
awa --database-url $DATABASE_URL queue stats
awa --database-url $DATABASE_URL job list --state failed
Architecture
┌──────────────┐ ┌──────────────┐
│ Rust producer │ │ Python (pip) │
└──────┬───────┘ └──────┬───────┘
└────────┬────────┘
▼
┌────────────────┐
│ PostgreSQL │
│ jobs_hot │
│ scheduled_jobs │
└───────┬────────┘
│
┌────────┼────────┐
▼ ▼ ▼
┌──────┐ ┌──────┐ ┌──────┐
│Worker│ │Worker│ │Worker│
│(Rust)│ │(PyO3)│ │(PyO3)│
└──────┘ └──────┘ └──────┘
All coordination through Postgres. The Rust runtime owns polling, heartbeats, shutdown, and crash recovery for both languages. Mixed Rust and Python workers coexist on the same queues. See architecture overview for full details.
Workspace
| Crate | Purpose |
|---|---|
awa |
Main crate — re-exports awa-model + awa-worker |
awa-model |
Types, queries, migrations, admin ops |
awa-macros |
#[derive(JobArgs)] proc macro |
awa-worker |
Runtime: dispatch, heartbeat, maintenance |
awa-ui |
Web UI (axum API + embedded React frontend) |
awa-cli |
CLI binary (migrations, admin, serve) |
awa-python |
PyO3 extension module (pip install awa-pg) |
awa-testing |
Test helpers (TestClient) |
Documentation
| Doc | Description |
|---|---|
| Rust getting started | From cargo add to a job reaching completed |
| Python getting started | From pip install to a job reaching completed |
| Deployment guide | Docker, Kubernetes, pool sizing, graceful shutdown |
| Migration guide | Fresh installs, upgrades, extracted SQL, rollback strategy |
| Configuration reference | QueueConfig, ClientBuilder, Python start(), env vars |
| Troubleshooting | Stuck running jobs, leader delays, heartbeat timeouts |
| Architecture overview | System design, data flow, state machine, crash recovery |
| Web UI design | API endpoints, pages, component library |
| Benchmarking notes | Methodology, headline numbers, how to run |
| Validation test plan | Full test matrix with 100+ test cases |
| TLA+ correctness models | Formal verification of core invariants |
Architecture Decision Records (ADRs)
- 001: Postgres-only
- 002: BLAKE3 uniqueness
- 003: Heartbeat + deadline hybrid
- 004: PyO3 async bridge
- 005: Priority aging
- 006: AwaTransaction as narrow SQL surface
- 007: Periodic cron jobs
- 008: COPY batch ingestion
- 009: Python sync support
- 010: Per-queue rate limiting
- 011: Weighted concurrency
- 012: Split hot and deferred job storage
- 013: Durable run leases and guarded finalization
- 014: Structured progress and metadata
License
MIT OR Apache-2.0
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file awa_pg-0.4.0a1-cp312-cp312-macosx_11_0_arm64.whl.
File metadata
- Download URL: awa_pg-0.4.0a1-cp312-cp312-macosx_11_0_arm64.whl
- Upload date:
- Size: 5.4 MB
- Tags: CPython 3.12, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3ec980c76fc5a522fca8642b93a74594b39e7c0d11594931263d2d93dafbbed0
|
|
| MD5 |
8a85f086ce52c0b7a1c452f38cf3e4d9
|
|
| BLAKE2b-256 |
f3f2ea5481062ce98e0d671a075cd10c020c36bdfd5354db43f261cbb46f8b79
|
Provenance
The following attestation bundles were made for awa_pg-0.4.0a1-cp312-cp312-macosx_11_0_arm64.whl:
Publisher:
release.yml on hardbyte/awa
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
awa_pg-0.4.0a1-cp312-cp312-macosx_11_0_arm64.whl -
Subject digest:
3ec980c76fc5a522fca8642b93a74594b39e7c0d11594931263d2d93dafbbed0 - Sigstore transparency entry: 1166294957
- Sigstore integration time:
-
Permalink:
hardbyte/awa@7755eed2b433b7805ae78303f6b93e5b11f68967 -
Branch / Tag:
refs/tags/v0.4.0-alpha.1 - Owner: https://github.com/hardbyte
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7755eed2b433b7805ae78303f6b93e5b11f68967 -
Trigger Event:
push
-
Statement type:
File details
Details for the file awa_pg-0.4.0a1-cp312-cp312-macosx_10_12_x86_64.whl.
File metadata
- Download URL: awa_pg-0.4.0a1-cp312-cp312-macosx_10_12_x86_64.whl
- Upload date:
- Size: 5.6 MB
- Tags: CPython 3.12, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
41a18c1cfa6e4748ea130a5d3f604a9c332027738351584cc0299c1082c33082
|
|
| MD5 |
a4aace2c824d0139b508f577294006aa
|
|
| BLAKE2b-256 |
610a5c25d2695b4026a850d3087a0e4478e0939abd13f87ca753f07c5833a42f
|
Provenance
The following attestation bundles were made for awa_pg-0.4.0a1-cp312-cp312-macosx_10_12_x86_64.whl:
Publisher:
release.yml on hardbyte/awa
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
awa_pg-0.4.0a1-cp312-cp312-macosx_10_12_x86_64.whl -
Subject digest:
41a18c1cfa6e4748ea130a5d3f604a9c332027738351584cc0299c1082c33082 - Sigstore transparency entry: 1166294591
- Sigstore integration time:
-
Permalink:
hardbyte/awa@7755eed2b433b7805ae78303f6b93e5b11f68967 -
Branch / Tag:
refs/tags/v0.4.0-alpha.1 - Owner: https://github.com/hardbyte
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7755eed2b433b7805ae78303f6b93e5b11f68967 -
Trigger Event:
push
-
Statement type:
File details
Details for the file awa_pg-0.4.0a1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: awa_pg-0.4.0a1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 6.0 MB
- Tags: CPython 3.8, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e169e96d936448aa66372d9586a470fa9d600f41afc68f359d60a861e4e6e20a
|
|
| MD5 |
428710c40dd84de8f9deee854d92d8de
|
|
| BLAKE2b-256 |
e9e3e10a9aed145ff5977ed458034c9be6b245409acf87dd2ae9f6e6b35c8066
|
Provenance
The following attestation bundles were made for awa_pg-0.4.0a1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
release.yml on hardbyte/awa
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
awa_pg-0.4.0a1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
e169e96d936448aa66372d9586a470fa9d600f41afc68f359d60a861e4e6e20a - Sigstore transparency entry: 1166294147
- Sigstore integration time:
-
Permalink:
hardbyte/awa@7755eed2b433b7805ae78303f6b93e5b11f68967 -
Branch / Tag:
refs/tags/v0.4.0-alpha.1 - Owner: https://github.com/hardbyte
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7755eed2b433b7805ae78303f6b93e5b11f68967 -
Trigger Event:
push
-
Statement type:
File details
Details for the file awa_pg-0.4.0a1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: awa_pg-0.4.0a1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 6.1 MB
- Tags: CPython 3.8, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4e5f503e3ef214d6d51c98ada3c463bad14649a5bb097a002fca2371c7c205fe
|
|
| MD5 |
0a5dd8fb27d63863efec18c8d41f19ee
|
|
| BLAKE2b-256 |
47519fb5f146ad9e2639f3f4874da67d85845c2b519b89fd1dd629ae0ef508f3
|
Provenance
The following attestation bundles were made for awa_pg-0.4.0a1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:
Publisher:
release.yml on hardbyte/awa
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
awa_pg-0.4.0a1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
4e5f503e3ef214d6d51c98ada3c463bad14649a5bb097a002fca2371c7c205fe - Sigstore transparency entry: 1166293812
- Sigstore integration time:
-
Permalink:
hardbyte/awa@7755eed2b433b7805ae78303f6b93e5b11f68967 -
Branch / Tag:
refs/tags/v0.4.0-alpha.1 - Owner: https://github.com/hardbyte
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7755eed2b433b7805ae78303f6b93e5b11f68967 -
Trigger Event:
push
-
Statement type: