Concept
Sagas and compensation
How Kiwi Fabric orchestrates multi-system writes, undoes them on failure, and reports the gaps where it cannot.
Why sagas#
A single business operation often has to change several systems. Two-phase commit does not work across most vendors, and event sourcing is a large undertaking. A saga is the practical middle path: run each step, and if a later step fails, run a defined compensation for every step that already succeeded.
Execution model#
- Plan. Steps declare
dependsOn. The orchestrator builds a dependency graph and sorts it into waves. - Run. All steps in a wave run in parallel. A step receives the resolved outputs (for example a generated identity) of the steps it depends on, via
{ "$ref": "stepId.keyName" }. - Record. Each handler returns a snapshot token when it writes. The platform stores it durably. The token holds what is needed to reverse that one write, so compensation never has to re-query the database to work out how to undo it.
- Compensate on failure. If any step fails, every completed step is compensated in reverse order of completion.
The rules compensation follows#
- Each compensation uses only its own snapshot token and original write specification.
- Compensation never depends on the outcome of another step's compensation. This keeps rollback predictable even when it partly fails.
- Steps can carry an idempotency key, so a retry after a timeout does not apply a write twice.
Automatic and explicit compensation#
| Step | Compensation |
|---|---|
| Insert | Automatic: delete by the generated key |
| Upsert | Automatic: restore the previous row |
| Update | Automatic: restore the snapshotted rows |
| Delete | Automatic: re-insert the snapshotted rows |
| Raw SQL | You define it: forward SQL plus compensation SQL |
| REST call | You define it: a compensation URL |
| Execute stored procedure | The compensation procedure registered with the capability |
Compensation gaps#
Not every system can be rolled back. Some legacy systems and third-party APIs have no "undo". Kiwi Fabric does not pretend otherwise. When a write path has no compensation, or compensation fails, the platform produces a structured gap report containing the saga id, the gap type, the affected table, the business impact and remediation options with complexity estimates.
kiwi fabric saga-gaps --id <saga-id>
Gap reports turn an invisible operational risk into a visible, prioritisable list of work. Declare accurately whether each write-enabled capability has compensation (hasCompensation) and whether it is idempotent, so the report reflects reality.
Observability#
Per-step telemetry records the step id, source or table, operation, whether it succeeded, whether it was compensated, duration, retry count and error message. The full saga trace is linked from the request detail view, and the Saga Runner shows execution and rollback live.
What a saga is not#
A saga gives eventual rollback, not isolation. Other sessions can observe intermediate state between a step's write and its compensation. Design compensations with that in mind.