Skip to main content

Durable backend correctness for single-machine Python on one SQLite file: transactional outbox, exactly-once jobs, forward-only migrations, snapshots. Zero dependencies.

Project description

ballast

Durable backend correctness for single-machine Python — on one SQLite file, with zero dependencies.

The guarantees you'd normally stand up Redis + Postgres + a broker for, delivered against one WAL-mode SQLite database with no server and nothing to operate:

  • Transactional outbox — publish events inside your own transaction, so an event exists if and only if the work that produced it committed. No phantom events, ever.
  • Exactly-once jobs — a background worker runs a handler and marks the job done in the same transaction. A crash rolls back the side effects and the completion together; the job retries. No double-sends, no lost work.
  • Forward-only migrations — a tiny ladder that applies pending steps atomically and refuses to run against a newer database (the install-over-install downgrade guard).
  • Consistent snapshots & restoreVACUUM INTO backups with rotation, and a guarded restore to roll back.
  • OS-keyring secrets — read from the keyring when present, fall back to env vars always.

Built for the deployments that Temporal, Celery, and cloud queues structurally can't serve: desktop apps, on-prem appliances, air-gapped / regulated environments, and CLIs that must be crash-safe on a single box.

CI PyPI Python License


Why

Getting durable execution right on SQLite is a minefield most teams cross badly: writer serialization, BEGIN IMMEDIATE, the WAL pragma regime, the ambiguous-send problem after a crash, phantom events on rollback, idempotency, startup recovery of leased jobs. The usual escape hatch — run Redis/Postgres/Kafka — is impossible to ship to a customer's laptop or an air-gapped box, and absurd overkill for one process.

ballast packages the crash-tested version of all of that behind a small, boring API. It is deliberately dependency-free (standard library only), so there is nothing to audit but the code and it runs anywhere Python does.

Install

pip install ballast.py              # core: zero dependencies
pip install "ballast.py[keyring]"   # optional: OS-keyring-backed secrets

The PyPI distribution is named ballast.py (the plain ballast name belongs to an unrelated load-balancing library). The import name is unaffected: import ballast.

Requires Python 3.10+.

Quick start

from ballast import Database, EventBus, JobWorker, install

db = Database("app.db")
install(db)                                    # create ballast's tables (idempotent)

bus = EventBus()
bus.declare("order.placed")
bus.subscribe("fulfilment", "order.placed",
              lambda conn, e: conn.execute("INSERT INTO orders VALUES (?)", (e.entity_id,)))

with db.transaction() as conn:                 # publish atomically with your own writes
    bus.publish(conn, "order.placed", {"total": 4999}, entity_type="order", entity_id="ord-1")

JobWorker(db, bus=bus).drain()                 # process to quiescence (or .start() in the background)

See examples/quickstart.py and examples/durable_jobs.py.

What you get

Component What it does
Database Correctly-configured connections + a transaction() (BEGIN IMMEDIATE → commit/rollback).
MigrationRunner / Migration Forward-only ladder; atomic per-step; DowngradeError on a newer DB.
snapshot() / restore() VACUUM INTO backups with keep-N rotation; guarded restore.
EventBus Declare topics, publish() inside your transaction, register subscribers.
dispatch_pending / prune_events Advance cursors + enqueue dispatch jobs; prune consumed + aged events.
JobQueue / JobWorker Transactional enqueue/enqueue_unique; single-writer worker; exactly-once, retry+backoff, startup recovery.
SecretStore Keyring-first, env-var fallback secrets.

Design in one paragraph

Everything hangs off two ideas. First, BEGIN IMMEDIATE on every write (ballast runs SQLite in autocommit and issues transactions explicitly) so busy_timeout actually applies and writers queue instead of erroring. Second, the outbox and the job-completion live in the caller's / worker's transaction, so "publish the event" and "do the work" and "mark it done" are atomic with the state change. Ordering is by an AUTOINCREMENT seq (commit order under serialized writers), never by a random id. Full rationale in docs/ARCHITECTURE.md.

Documentation

Scope & non-goals

ballast is single-process, single-machine by design — that constraint is what makes the guarantees cheap and true. It is not a distributed queue, not a multi-writer cluster, and not a replacement for Temporal at scale. If you can run a broker and need multi-node fan-out, use one. If you must ship crash-safe durability inside one binary with no infrastructure, that is exactly what this is for.

License

Apache-2.0.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ballast_py-0.1.1.tar.gz (28.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ballast_py-0.1.1-py3-none-any.whl (27.3 kB view details)

Uploaded Python 3

File details

Details for the file ballast_py-0.1.1.tar.gz.

File metadata

  • Download URL: ballast_py-0.1.1.tar.gz
  • Upload date:
  • Size: 28.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ballast_py-0.1.1.tar.gz
Algorithm Hash digest
SHA256 51daac704977e4a55c6b41a8bc4c64b387757208aabd44b9d6a116b598a6b470
MD5 e4b774e717bd89b5f0138d46ab6cbab3
BLAKE2b-256 a63ed7b1bbeb3e54a7e7eaf310a6233279c334e2219c213e8e6724794a5adab5

See more details on using hashes here.

Provenance

The following attestation bundles were made for ballast_py-0.1.1.tar.gz:

Publisher: release.yml on Vibecoder012/ballast

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ballast_py-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: ballast_py-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 27.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ballast_py-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 188cf57f397c6c873935e01fc2c33b3e8bdd595e4fb2d72429da9c7105489b41
MD5 da652d3db3170105ef4895ab66e1c45f
BLAKE2b-256 f2c4afc9874d59cc4b3b6e3551df9fe8c92857e01c17034cd252b70bbe3477be

See more details on using hashes here.

Provenance

The following attestation bundles were made for ballast_py-0.1.1-py3-none-any.whl:

Publisher: release.yml on Vibecoder012/ballast

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page