Repository navigation
BackWave 1.7.0
Added
- Oracle storage adapter.
BackWave.Oracletargets Oracle 19c and later, passes the same Conformance Suite as the other adapters, and carries the same OpenTelemetrydb.*spans. Its hot paths are batched: a claim of 32 jobs costs 9 statements, an outcome report of 32 costs 5, and a lease sweep costs 4. The adapter measures 4.1x the throughput of a per-row design at a 1 ms round trip. SetOracleStoreOptions.EnableWakeUpHintsto wake an idle pump on enqueue throughDBMS_ALERT. The hint is off by default. With it on, pickup latency drops from the 5 s poll interval to about 138 ms. A missingEXECUTEgrant onSYS.DBMS_ALERTlogs one warning (EventId 1501) and the pump keeps polling. - Clean-stop lease hand-back. A Worker Group that stops cleanly now returns the Leases it holds, so a healthy node picks the work up at once instead of after a full Lease Duration.
IJobStoregainsRelinquishLeasesAsync, a default interface method that returns zero, so a custom store compiles and behaves as before. The hand-back and the final outcome report share oneShutdownBudget(default 5 s). A fail-stopped group writes nothing more and lets its Leases lapse. - Fail-stop trigger set.
InvariantTriggernames every impossible state BackWave detects.InvariantViolationExceptioncarries the tripped trigger. The pump and every adapter now halt on a trigger instead of executing past it: a claim that returns a terminal job, an outcome batch answered with a different row count, a short heartbeat answer, a claim wider than its bound, a workflow member cycle, and a live member with no workflow row. The trigger reaches log 2001 asinvariant_trigger, thebackwave.invariant.violationscounter taggedbackwave.invariant.triggerandbackwave.invariant.action, and the health check state. A fence rejection degrades and is counted where it was dropped before. - Tag length bounds.
StoreBounds.MaxTagKeyLengthandStoreBounds.MaxTagValueLength(both 200 by default) are enforced on every tag write. An over-limit tag is rejected withEnqueueResult.TagTooLongorWorkflowEnqueueResult.TagTooLongand is never truncated. The SQL Server and Oracle adapters store tags in fixed-width columns (200 and 256 characters), so they reject a wider bound when the store is created. - Paged Workflow list.
IJobStore.ListWorkflowsAsync(WorkflowListQuery)pages by a keyset cursor on(created_at, workflow_id), clamped toMaxMonitorPageSize. The default implementation pages in memory. All five first-party stores read one bounded page. The Pro Workflows page and the MCPlist_workflowstool use it, so a refresh no longer loads every workflow. - Observer report outcome.
IJobStore.TryReportObserverDeliveriesAsyncreports whether a delivery report was applied, fenced, or unknown. The default implementation forwards toReportObserverDeliveriesAsync. The hosting layer now acts on a fenced report instead of discarding it. - Rolling upgrade tolerance. Every adapter now accepts a schema version newer than its own, so an N-1 node keeps running while the fleet upgrades. A version older than expected still fail-stops.
- Conformance Suite capability manifest. A store that declares a capability whose hook still returns the default now fails the suite instead of passing silently.
Changed
- CAUTION:
BackWaveHealthChecknow reportsDegradedon a transient store fault or a partially halted group. It reportedHealthybefore. A readiness probe that fails onDegradedwill restart pods on faults it used to ride out. ASP.NET mapsDegradedto 200 OK, so a stockMapHealthChecksendpoint is unchanged. Only a wholly halted group reportsUnhealthy. The degraded description names the group and the exception type only, so provider text (host, database, login) no longer reaches an unauthenticated probe. RetryPolicy.MaxAttemptsis bounded to the shared attempt ceiling of 1000, the same ceiling[Retry]has.- Lease sweep round trips. The lease sweep on Postgres, SQL Server, and SQLite records its transitions in one batch. At the default claim batch of 32 a full sweep costs 5 statements on Postgres and 6 on SQL Server, down from 67 and 68. The per-row transition prune is skipped under the retention cap on all three.
JobStateordinals are pinned as the persisted wire format and guarded by a test. No value changed.- Schema. SQL Server adds
0002_lease_owner_index.sql(an index that lets the hand-back lock only its own rows). SQLite adds0002_transition_position.sql(a high-water mark so the Transition Log position never regresses after a retention purge). Both are additive. WithAutoMigrateon, the store applies them on first use.
Fixed
- A Worker Group admitted far more than
PoolSizehandlers under a burst (measured 160 to 400 withPoolSize20). A claim batch now reserves its slots when it is issued. - Idle-poll backoff ramped on the empty-poll count, so a drain tail saturated the delay at once and a job enqueued right after waited the whole ceiling. The ramp now follows elapsed idle time.
- A recurring schedule with a Skip or Coalesce policy stalled after a long outage: the planner walked 32 missed ticks per poll. The fresh tick now mints in the first poll.
- A handler that returned a Job Output larger than the store cap halted the whole Worker Group. The job now dead-letters with the rejection as its Failure Detail and the group keeps running.
- SQL Server: hot-path writes that lose a deadlock (error 1205) retry up to four times. The claim retries one Queue's transaction, not the whole claim. Heartbeat and relinquish ids travel through
OPENJSON. - SQLite: the Transition Log position regressed after a retention purge, so an Observer cursor missed transitions. The store's own connection pool is now cleared on dispose.
- SQLite and Oracle: an expired parent's transition is recorded before its children's cascade, so an Observer sees the parent go terminal first.
- Oracle: heartbeat ids, hand-back transitions, and tag reads are sent in slices under the
IN-list limit, so a wide batch or a large workflow stays readable. - A drained workflow is purged whole under its worst member's retention class.
- The outcome fence now allows the clock skew a fleet can carry, so one skewed node no longer trips the invariant counter.
- The process span records an Error status and the exception on a handler-side halt.
- The MCP queue-depth description names the real job states.
- The source generator's
BW0004names the payload type, not the Wire Name.
All 14 packages ship at 1.7.0 on nuget.org.