Consistency
Understand which workflow state changes must be observed together and which data remains live.
Consistency defines which state changes must be observed together and which data is intentionally allowed to remain live. Sonar uses database serialization and transactions around critical boundaries rather than treating RabbitMQ or an in-memory loop as the source of truth.
One reconciler per scan
Before reconciling a scan, Sonar attempts a session-scoped PostgreSQL advisory lock derived from
the scan ID. If another backend already holds it, the current tick skips that scan. The lock is
released in a finally path.
This serializes task creation, readiness checks, dispatch budgeting, and completion decisions for one scan while allowing different scans to reconcile independently.
Transactional dispatch intent
Marking a task Published and recording the intent to publish happen in the same database save.
An EF interceptor converts the task's domain event into an outbox row in that transaction.
Outbox processors claim rows with FOR UPDATE SKIP LOCKED, publish to RabbitMQ, and mark each row
processed. A crash cannot commit Published without also leaving durable publish intent. Outbox
delivery can repeat, so downstream handling remains idempotent rather than relying on a single
publish.
Atomic result acceptance
The result endpoint locks the scan row and, in one transaction:
- authenticates the current dispatch attempt;
- accepts the task's terminal status and result digest; and
- records durable result-finalization work.
The worker receives success only after this state commits. The slower MinIO read, parse, and Inventory upsert happen from leased durable work rather than inside the callback request.
For manifest-backed recon scans, final result records are frozen to one result revision only after all expected work is represented, no tasks remain active, and no current finalization work remains unsettled.
Workflow snapshot boundary
A scan's WorkflowSnapshot is immutable historical and rendering data. It is not the runtime
execution source.
The reconciler loads the live workflow definition on every tick. Consequently:
- history continues to display the graph captured at launch;
- an operator can inspect what the scan originally showed; but
- an edit to the live definition can affect later reconciliation and dispatch for that active scan.
This is an explicit boundary, not snapshot isolation for execution. Operationally, keep workflows stable until their active scans finish.
Scope of the guarantees
The advisory lock serializes one scan's reconciler, not external command side effects. The outbox couples database state to publish intent, not RabbitMQ delivery to worker execution in one global transaction. Sonar therefore combines these consistency boundaries with the idempotency rules required by at-least-once delivery.