Changelog¶
Changelog¶
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased¶
0.1.1 - 2026-05-08¶
Patch release. No runtime code changes — docs + site cleanup only.
Fixed¶
Read the Docs build —
docs/conf.pyenables MyST’slinkifyextension, which needslinkify-it-py. That package was missing from the[docs]extra so RTD’s first build crashed withModuleNotFoundError: Linkify enabled but not installed.The extra now declaresmyst-parser[linkify]>=4.0.1.README license badge — was pointing at
pypi/l/roost.svg(renders as “package or version not found” since the dist ispgroost). Now points atpypi/l/pgroost.svg.
Changed¶
Docs URL — moved from
roost.readthedocs.iotopgroost.readthedocs.ioto match the PyPI distribution. Updated README badge, README links,pyproject.tomlDocumentationURL,docs/conf.pypkg_version("pgroost"), and TASKS.md targets.Marketing site canonical URL —
roost-sitewas hardcodinghttps://roost.devinastro.config.mjs, which leaked into Open Graph + canonical tags even though the site deploys atroost.ashhadahsan.com. The README’srecipesandcomparisonlinks also pointed at the unowned domain. All now point atroost.ashhadahsan.com.
0.1.0 - 2026-05-08¶
First public release.
Distribution¶
Published on PyPI as
pgroost(the bareroostname was reserved). Install withpip install pgroost. The import path staysimport roostand the CLI command staysroost.
Added — pre-publish polish¶
roost doctor— health-check command +Checkrecords covering migration state, NOTIFY trigger presence, recent worker heartbeats, and a job-state summary. Returns non-zero exit on any failure.roost run --once+Worker.run_once()— drain currently-available jobs and exit. Useful for serverless / one-shot runners.roost run --workers N— multi-process supervisor viamultiprocessingspawn context. Each child re-imports user modules into a fresh interpreter (uvicorn-style). Pair with systemd / docker / k8s for restart semantics.Stable error codes (
code: ClassVar[str]) on everyRoostErrorsubclass plusJobTimeoutError/JobFailed. Lets users branch on errors programmatically instead of string-matching.roost.tasksmodule —specs(),get(name),names()over the registered handler set.Two-step claim (
SELECT FOR UPDATE SKIP LOCKED→UPDATEby ids) replacing the modifying-CTE pattern. Smaller contention surface, easier to reason about.Server-side
scheduled_atdefault —COALESCE($N::timestamptz, now())in INSERT. Fixes a subtle clock-skew bug where Pythondatetime.now()on the client could produce timestamps slightly ahead of Postgresnow()(e.g. Docker testcontainers, k8s pods with unsynced clocks), making newly enqueued rows briefly invisible toWHERE scheduled_at <= now().Test coverage at 86%, 163 tests on real Postgres via
testcontainers(no DB mocks).
Added — v0.2 polish (post-initial-release)¶
Per-task defaults on
@job—queue,priority,max_attempts,tags,timeout_seconds, plus throttling:rate_per_minute,max_concurrency. Explicit enqueue kwargs always win.Cron timezone support —
@cron("0 9 * * 1-5", timezone="America/Los_Angeles"). IANA names, DST-aware. Defaults to UTC.roost.contribintegrations: FastAPI (RoostDep,tx_roost_dep), Django (enqueue_in_atomic), Flask (RoostExtension).AsyncRoost.wait_for(job_id)— block until terminal, returns aJobOutcomewith the stored result. Backed byLISTEN roost_updated.roost.testing—run_inlineanddrain_pendingso app tests don’t need testcontainers.Migration framework — numbered up/down migrations,
roost migrate up/down/statusCLI,roost.migrationsbookkeeping table.Job dependencies / chaining —
enqueue(child, depends_on=[parent_id]). Child waits for every parent to reachcompleted. A peer-worker reaper cancels children whose parent ended indiscardedorcancelled.Per-task rate limiting + max concurrency — fetch SQL gates with a
ROW_NUMBER() PARTITION BY taskwindow so a single batch never over-picks. Best-effort under multi-worker contention.metadataJSONB column — out-of-band field for trace/request/tenant ids that aren’t handler input.Capped
errors[]— default 20, configurable per worker. Trim runs in SQL so the row never balloons under retry storms.Auto-archive — optional periodic move of terminal jobs older than N seconds into a
roost.jobs_archivetable.Worker startup retries — exponential backoff if Postgres isn’t ready when the worker boots.
roost enqueueCLI — adhoc operator enqueue:roost enqueue task_name --args '{...}' --in 5m.roost requeue --discarded --queue X— bulk dead-letter revive scoped to one queue.Event hooks —
Hooks(before_job, after_job)plug into every dispatch with a shared per-executionctxdict; throwing hooks never poison the handler.roost tasks export— emit a JSON manifest of registered tasks plus their Pydantic-derived JSON Schemas, useful for typed clients.roost run --reload— dev mode that watches imported handler modules and exits cleanly so a supervisor can restart. Requirespip install pgroost[reload].
Initial release¶
Initial project scaffold (uv, src layout, ruff, mypy, pre-commit, MIT license).
roost.jobsschema withLISTEN/NOTIFYtriggers (roost_inserted,roost_updated,roost_cancel_requested).Transactional enqueue (async + sync) honoring the caller’s connection.
Bulk enqueue (
enqueue_many+JobInsert) usingexecutemanyfor one-round-trip inserts.Per-job priorities, tags, timeouts, and result storage exposed on the public facade.
Pydantic-typed args via
@job(name, args_model=…)— validation at handler-call time.Worker loop using
FOR UPDATE SKIP LOCKEDwith retries, snoozing, and graceful shutdown.Cancel propagation —
roost cancelaborts in-flight handlers viaLISTEN roost_cancel_requested.Per-job timeout enforcement via
asyncio.wait_foraround the handler.Result storage — handler return values are persisted to
roost.jobs.result.Backoff strategies:
exponential,linear,fixed, plus custom callable hook.Cluster-singleton cron scheduler via Postgres advisory lock (now uses a dedicated lock connection).
@joband@crondecorators backed by an in-process registry.Public facades
AsyncRoost(asyncpg) andRoost(psycopg).Operational primitives: queue pause/resume, worker heartbeat table, orphan reaper, mass-requeue of discarded jobs.
Typer CLI:
init,run,status,retry,cancel,workers,requeue --discarded,queue pause|resume|list,version.Observability: structlog defaults (JSON in prod, pretty in dev), optional OpenTelemetry trace propagation (
pip install pgroost[otel]), optional Prometheus metrics (pip install pgroost[metrics]).Bench harness (
bench/throughput.py) measuring throughput + p50/p99 enqueue→start latency.Chaos test verifying SIGKILL’d worker jobs are recovered by the orphan reaper.
testcontainers-based test suite covering enqueue atomicity, retries, cron, listen/notify, unique jobs, hardening, and feature surfaces (50+ tests).
CI matrix on Python 3.10–3.13 × Postgres 13–16 and Read the Docs configuration.
Sphinx + Furo documentation site with quickstart, concepts, and recipes.
Community files: SECURITY.md, CODE_OF_CONDUCT.md, GitHub issue + PR templates, Dependabot config.