| Name | Modified | Size | Downloads / Week |
|---|---|---|---|
| Parent folder | |||
| 12.26.0 source code.tar.gz | 2026-07-10 | 644.8 kB | |
| 12.26.0 source code.zip | 2026-07-10 | 782.1 kB | |
| README.md | 2026-07-10 | 5.5 kB | |
| Totals: 3 Items | 1.4 MB | 0 | |
Schema version: 37 (migrates automatically on start() / migrate).
This release adds schema drift detection, self-healing for background index builds, and a set of correctness fixes. A few of those fixes surface errors that earlier versions silently swallowed — see Behavior changes before upgrading.
Highlights
Schema drift detection
- New
boss.detectSchemaDrift()— compares the live database against the shape pg-boss expects (tables, indexes, functions, columns, enum, constraints) and returns a structuredSchemaDriftReport. It only reports; it never modifies the schema. - New CLI command:
pg-boss doctor— runs the same scan and prints missing / invalid / mismatched objects with copy-paste remediation DDL. Exit code1on drift,0when clean. - Drift comparison is backend-aware: type/default/constraint checks are Postgres-canonical and are skipped on CockroachDB (which renders catalog text differently), while presence checks run everywhere. Catalog queries a backend doesn't support (e.g.
pg_get_functiondef) are skipped rather than reported as false drift. - A canonical schema manifest (
src/schema.json) is generated from a real install and validated in CI (npm run gen:manifest:check, wired intopretest), so the expected shape can never silently drift from the DDL.
Background index build (BAM) health & healing
- Interrupted
CREATE INDEX CONCURRENTLYbuilds are detected and healed: a re-attempted build that finds an invalid leftover index drops and rebuilds it, instead of the command'sIF NOT EXISTSskipping over a broken index forever. A valid index is never dropped. - Stalled builds are reclaimed using a cluster-wide
pg_locksliveness check (theShareUpdateExclusiveLockaCREATE INDEX CONCURRENTLYholds on its table for the whole build): a genuinely running build is never reclaimed (no matter how long it runs), a dead one recovers within minutes, and the check stays correct when many pg-boss instances share one database. Backends that roll interrupted builds back (CockroachDB, YugabyteDB) fall back to a timeout-only reclaim.
Behavior changes
These are bug fixes for cases where pg-boss previously resolved successfully while hiding a failure. Under strict semver they are behavior changes; they ship in this minor release. Ensure an error listener is attached to the boss instance, and handle rejections from direct fetch() / deleteQueue() / insert() calls.
fetch()/work()now surface database errors. Previously every fetch error was swallowed and returned as an empty batch — a DB outage was indistinguishable from an empty queue. Now only a unique-constraint violation (SQLSTATE23505, the benign concurrent-slot race) is treated as an empty fetch; any other error propagates. In awork()loop it is routed to the worker's error handler and emitted as anerrorevent; a directfetch()call now rejects.insert()rejects on duplicate job ids within a batch. Passing the same explicitidtwice in oneinsert([...])now throwsduplicate job id in insert batch: <id>instead of silently deduplicating viaON CONFLICT DO NOTHING.deleteQueue()now propagates DELETE / cache-eviction errors. A transient failure during the delete previously resolved as success while the queue survived; it now rejects. Deleting a queue that does not exist remains a no-op.
Other fixes
- knex adapter: queries mixing jsonb
?key-exists operators with$Nplaceholders (e.g.updateJob) no longer throwUndefined binding(s) detected— literal?operators are escaped before binding. retryBackoff: truewith noretryDelaynow backs off (#839). The defaultretryDelayof0made the exponential-backoff formula (retryDelay * 2^n) evaluate to0, so retries fired instantly with no backoff. The delay is now floored to 1 second in the backoff path whenretryDelayis unset, matching the documented default. The fixed-delay path (retryBackoffoff) still honors an explicitretryDelay: 0as immediate retry.update()honorsstartAfter: 0(and negative values) to pull a deferred job forward to now, instead of silently dropping the edit.- LISTEN/NOTIFY reconnect: the listener only self-heals after it has connected at least once, so a failed initial connect falls back to polling cleanly without leaking an untracked reconnect.
- Migration floor guard: migrating from a DB version below the oldest supported starting version now fails with a clear message instead of applying migrations over missing steps.
job_table_formatmigration (v37): the table-name rewrite is anchored so it matches only the base table reference and barejob_iNtokens.- Internal hardening: start/stop refactor with ret/re-entrancy safety, clock-skew handling in the timekeeper, a group-counter leak fix, and dead-letter insert parameter alignment.
Upgrade notes
- No manual migration required —
start()(orpg-boss migrate) moves the schema to v37. - After upgrading, attach an
errorlistener if you don't already have one; the fetch change means genuine DB errors now reach it. - Optionally run
pg-boss doctorto verify the live schema matches the expected shape.
Dependencies
- Dev-dependency bumps:
@electric-sql/pglite,@types/node,kysely,tsx.
Full Changelog: https://github.com/timgit/pg-boss/compare/12.25.1...12.26.0