Releases: AlexeyMatskevich/apalis-diesel-postgres
v0.4.1
Fixed
list_queues()(theListQueuesadmin trait) silently returned an emptystatslist for any queue with no completed jobs: the per-queueAVG_JOB_DURATION_MINSaggregate is SQLNULLin that state, which serialized to a JSONnullthat cannot decode intoapalis_core::Statistic, failing the wholeVec<Statistic>decode and dropping every stat for the queue. Thequeue_statsCTE nowCOALESCEs null stat values to"0", so a queue with jobs always reports its full stat set.
Changed
- The unscoped
lock_taskno longer lists "or in another queue" in itsTaskNotFoundhint: that entry point does not filter byjob_type, so a task in another queue is locked rather than reported missing.lock_task_in_queuekeeps the queue-aware hint.
Documentation
- Every public
Result-returning function now carries an# Errorssection. MIGRATIONSand theschemamodule are documented; the crate enables#![warn(missing_docs)]and#![warn(rustdoc::broken_intra_doc_links)].- README links to
examples/*andCONTRIBUTING.mdare now absolute GitHub URLs (relative links 404 on docs.rs); added an MSRV note (Rust 1.88). Cargo.tomlgained[package.metadata.docs.rs]so docs.rs documents thentexpath alongsidetokio.CONTRIBUTING.mdno longer lists--no-default-featurescheck/test commands (building without a runtime feature is an intentionalcompile_error!).
Full changelog: v0.4.0...v0.4.1
v0.4.0
Breaking change (minor): PgFetcher's phantom _marker field is no longer public — construct the marker fetcher via Default instead.
Fixed
- Decode-stranding: a claimed row whose payload failed to decode was stranded in
Runningfor as long as the claiming worker kept heartbeating (ack needs a decoded task; orphan recovery only reclaims rows of stale workers). The decode stage now releases such rows through the normal retry budget —Failedwith the decode error inlast_result, terminalKilledonce attempts are exhausted — guarded by the exact claim epoch (lock_by,lock_at,attempts) so a delayed release never touches a row that was acked, swept, or re-claimed in the meantime. - Concurrent
reenqueue_orphanedsweeps could double-apply to the same stale row under READ COMMITTED: burning an extra attempt, prematurely killing a job, or flipping an already-acked row back toPending. The sweep now repeats the status predicate on the outer UPDATE and claims candidates withFOR UPDATE OF jobs SKIP LOCKED. - The shared notify listener returned its pooled connection to r2d2 without
UNLISTEN, so the next pool user inherited the subscription and notifications accumulated unread in libpq's receive buffer. Every listener exit now removes the subscription first. Debugoutput ofPgAck(and the publicPgMiddleware) printed the per-processlease_tokenverbatim; it is now redacted.with_codecrebuilt the sink from scratch, silently dropping buffered tasks and any in-flight flush; both now carry over.- With both
tokioandntexfeatures enabled, calling the backend from the ntex executor panicked insidetokio::task::spawn_blocking; the backend now falls back to ntex's blocking pool when no Tokio runtime is present. - The checked-in Diesel schema was missing
workers.lease_token; new specs pinsrc/schema.rsagainstinformation_schemaso the next migration cannot leave the typed schema stale silently. - CI's postgres job ran only 3 of the 11 integration test binaries; it now runs
--tests, gating every current and future test binary.
Changed
- Pool-path enqueue batches without an
idempotency_keyskip the conflict-recovery machinery on the sink's hot flush path; the outbox path (push_with_conn/push_task_with_conn) keeps full SAVEPOINT semantics — a failing INSERT rolls back only the batch and never aborts the caller's outer transaction. - apalis RC dependencies are pinned exactly (
=…-rc.9) socargo updatecannot silently pull a breakingrc.10. - The
Sinkimpl onPostgresStorageno longer requiresArgs: Send + Sync + 'static.
Documentation
- The handler examples now run business transactions on a separate backend pool injected via
Data<PgPool>, matching the "Connection pool isolation" guidance they previously contradicted.
Full details: CHANGELOG.md
v0.3.0
Breaking change. Idempotency-key conflicts on enqueue now return a dedicated, typed error variant instead of the stringly-typed Error::InvalidArgument("idempotency_key conflict: …"):
Error::IdempotencyConflict { job_type: String, conflicting_keys: Vec<String>, total: usize }Match the variant (not the message text) to tell a benign duplicate apart from a real failure:
match storage.push_task_with_conn(conn, task) {
Ok(id) => { /* enqueued */ }
Err(Error::IdempotencyConflict { .. }) => { /* duplicate — swallow it */ }
Err(other) => return Err(other),
}For batch enqueues, conflicting_keys lists exactly which idempotency_keys collided, so you can drop them and re-enqueue the rest.
Behavior is unchanged: a single duplicate still rolls back the whole batch via SAVEPOINT (one duplicate undoes every row in the batch, not just the colliding one) while a surrounding transaction stays alive. Every other Error::InvalidArgument case (queue-name / metadata / idempotency-key length caps, unreachable run_at) is unchanged.
Error is #[non_exhaustive], so future variants won't be a breaking change for downstreams that already match with a wildcard arm.
v0.2.0
What's new in 0.2.0
⚠️ Breaking
- Removed the public
SharedPostgresError::NamespaceExistsvariant (dead after the broadcast redesign). Code that matched on it must be updated. Under 0.x semver this warrants the minor bump.
Features
wait_for_completionis now resilient to transient database errors. A failed completion poll is retried with backoff instead of abandoning the whole batch; the stream surfaces an error and ends only after several consecutive failures with no successful poll in between. Any successful poll resets the streak, and completed results stay durable inapalis.jobs, so a surfaced error is always safe to recover from by re-issuingwait_for.
Fixes
- Concurrent
setup()migrations are serialized with a session-level advisory lock, so several replicas booting against a fresh database no longer race on migration-0 DDL / the__diesel_schema_migrationsinsert.
Tests & internals
- Large methodology-driven test pass (exhaustive specification through nested contexts): closed coverage gaps (
queue_by_id,list_tasksdefaults, thelock_taskre-lock and queue-scoping arms, metadata-cap and claimability boundary values), tightened assertions, and de-flaked the suite (shared pool, temp-DB isolation, deterministic worker-integration terminal status). 521 tests, clippy clean.
Docs
- Installation now points at crates.io.
Full diff: v0.1.1...v0.2.0
v0.1.1
Fix
PostgresStorageis nowSend + Syncunder bothtokioandntexruntimes, so it can be used withapalis::WorkerBuilder::build(). The published0.1.0failed to compile inside aWorkerBuilderbecause ntex'sBlockingResultis!Sync;PgSink::flush_futureis now wrapped inMutex<Option<…>>withMutex::get_mutkeeping the hot path lock-free. Compile-timeassert_send_syncguards the invariant.
Notes
0.1.0has been yanked from crates.io — it does not work with apalis.- Added an integration test covering the full worker round-trip (handler-ok and handler-err branches).
- Added
examples/worker.rs(tokio) andexamples/worker-ntex.rs(ntex / neon-polling) demonstrating end-to-end use withWorkerBuilderand in-handlerpush_with_conn. - README documents in-handler transactional fan-out.