Skip to content

feat: pluggable storage backends (pg/redis/mongodb) — v0.3.0#1

Open
robertziel wants to merge 1 commit into
mainfrom
feat/pluggable-storage-backends
Open

feat: pluggable storage backends (pg/redis/mongodb) — v0.3.0#1
robertziel wants to merge 1 commit into
mainfrom
feat/pluggable-storage-backends

Conversation

@robertziel
Copy link
Copy Markdown
Owner

What

Adds a host-agnostic StorageBackend SPI so the durable-queue store is selectable:

queue_workflows.configure(db_backend="pg" | "redis" | "mongodb")

One provider per file (queue_workflows/backends/), following the codebase's "injected hook + safe default" philosophy. Additive and opt-in: pg (default) is byte-compatible and the legacy Postgres engine is untouched.

The contract (identical across backends)

enqueue · claim exactly-once · lease/renew/reclaim · idempotent terminals · atomic outbox (complete_with_event/fail_with_event — both-or-neither) · wake · heartbeat · operator ON/OFF · per-namespace tenant isolation.

Guarantee pg redis mongodb
Claim once FOR UPDATE SKIP LOCKED ZPOPMIN in Lua find_one_and_update
Atomic outbox one txn one Lua script multi-doc txn
Wake LISTEN/pg_notify pub/sub change stream
Isolation namespace column key prefix DB per namespace

Tests (TDD)

tests/test_backend_contract.py — one parametrized suite green against all three live servers (a backend whose server is unreachable is skipped), incl. an 8-thread claim-once contention stress test.

  • 481 passed locally (415 existing engine tests + 66 contract = 22 × 3 backends).
  • No regressions; redis/pymongo import lazily, so a pg-only deploy needs neither.
  • Dockerized PG 16 + Redis 7 + Mongo 7 (single-node replica set) for the matrix.

Audit (design / regressions / safety / internal data leakage)

  • Fixed — Redis counts() could drift; the Lua now decrements the job's prior status (pg/mongo derive counts from live status, so they're immune).
  • Added — the multi-threaded claim-once test (was only proven sequentially).
  • No driver object (cursor/pipeline/session) appears in the port → PG internals can't leak into redis/mongo call sites. Cross-namespace claim/read/count/wake are isolated + tested.

Integration boundary (honest scope)

Selecting redis/mongodb does not re-home the orchestrator / claim-worker / dispatcher — those still run on Postgres. v0.3.0 ships the SPI + three contract-complete backends; wiring the DAG orchestrator end-to-end onto a non-PG backend is a later milestone. Caveats (Redis = single instance not Cluster; Mongo = replica set required) are in docs/storage_backends.md.

Release

Version bumped to 0.3.0 + CHANGELOG cut (additive ⇒ SemVer minor). Recommend cutting the v0.3.0 git tag after merge (a tag on this branch would be orphaned by a squash-merge).

🤖 Generated with Claude Code

Make the durable-queue store selectable via
configure(db_backend="pg"|"redis"|"mongodb"). Additive + opt-in: pg (default)
is byte-compatible and the legacy Postgres engine is untouched; selecting
redis/mongo does NOT re-home the orchestrator/worker (a later milestone).

- backends/base.py: StorageBackend port — enqueue / claim-exactly-once /
  lease+reclaim / idempotent terminals / atomic outbox
  (complete_with_event/fail_with_event) / wake / heartbeat / ON-OFF control.
  No driver object in any signature (anti-leakage); each backend is
  namespace-bound (multi-tenant isolation).
- backends/postgres.py: reference adapter — FOR UPDATE SKIP LOCKED, RETURNING
  CAS, one-transaction outbox, LISTEN/NOTIFY, on its own qw_* tables.
- backends/redis.py: Lua-atomic claim/terminal/outbox, sorted-set
  priority+FIFO, pub/sub wake (single instance, not Cluster).
- backends/mongodb.py: find_one_and_update claim, multi-doc-transaction
  outbox, change-stream wake (replica set required).
- tests/test_backend_contract.py: one parametrized contract suite, green
  against all three live servers (incl. an 8-thread claim-once contention
  stress test); a backend whose server is unreachable is skipped.
- config: db_backend + db_namespace + redis/mongo URL envs; [redis]/[mongodb]
  optional extras (drivers import lazily — pg-only deploys need neither).
- docs/storage_backends.md: contract, capability matrix + caveats, audit, and
  the integration boundary. CHANGELOG 0.3.0; CLAUDE.md/AGENTS.md/README updated.

Audit (design/regressions/safety/leakage): fixed Redis counts() drift
(decrement prior status, not always running); added the concurrency test; no
engine-suite regressions (415 green).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant