You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
RFC: Saga Pattern — automatic compensation on pipeline failure
Summary
Add support for the Saga Pattern, allowing each action in a pipeline to declare a compensating function. When an action fails (after retry exhaustion), the pipeline automatically executes compensations in reverse order for all previously completed actions, restoring the system to a consistent state.
Motivation
Dotflow already supports retry for transient failures (network timeout, temporary unavailability). However, there is no mechanism for business failures — cases where retrying will never succeed and the correct behavior is to undo what was already done.
Example: a pipeline that charges a credit card, reserves inventory, and creates a shipment. If the shipment step fails, the payment must be refunded and the inventory released.
Business failures (out of stock, card declined, invalid data)
Proposed API
fromdotflowimportactiondefrefund_card(ctx):
refund(ctx["transaction_id"])
defrelease_inventory(ctx):
release(ctx["item_id"])
@action(name="charge_card", compensate=refund_card)defcharge_card(ctx):
return {"transaction_id": "abc123"}
@action(name="reserve_inventory", compensate=release_inventory)defreserve_inventory(ctx):
return {"item_id": "xyz789"}
@action(name="create_shipment")defcreate_shipment(ctx):
# no compensate — this is the step that failed, nothing to undo
...
Execution flow
flowchart LR
A[Action 1] -->|success| B[Action 2]
B -->|success| C[Action 3]
C -->|FAIL| D[Start Saga]
D --> E[Compensate Action 2]
E --> F[Compensate Action 1]
F --> G[Clean state]
Loading
sequenceDiagram
participant P as Pipeline
participant A1 as charge_card
participant A2 as reserve_inventory
participant A3 as create_shipment
participant S as Saga
P->>A1: execute
A1-->>P: success (transaction_id)
P->>A2: execute
A2-->>P: success (item_id)
P->>A3: execute
A3-->>P: FAIL (after retry exhaustion)
P->>S: trigger compensation
S->>A2: release_inventory(ctx)
A2-->>S: done
S->>A1: refund_card(ctx)
A1-->>S: done
S-->>P: saga complete — clean state
Loading
Behavior rules
Compensations run in reverse order (LIFO) — only for actions that completed successfully
Each compensation receives the context produced by its corresponding action
Compensation functions must be idempotent — running twice produces the same result
Compensation functions should have aggressive retry — a failed compensation leaves the system in an inconsistent state
Actions without a compensate parameter are skipped during rollback
Task result with Saga
When Saga is triggered, the workflow result should include compensation status:
RFC: Saga Pattern — automatic compensation on pipeline failure
Summary
Add support for the Saga Pattern, allowing each action in a pipeline to declare a compensating function. When an action fails (after retry exhaustion), the pipeline automatically executes compensations in reverse order for all previously completed actions, restoring the system to a consistent state.
Motivation
Dotflow already supports retry for transient failures (network timeout, temporary unavailability). However, there is no mechanism for business failures — cases where retrying will never succeed and the correct behavior is to undo what was already done.
Example: a pipeline that charges a credit card, reserves inventory, and creates a shipment. If the shipment step fails, the payment must be refunded and the inventory released.
Proposed API
Execution flow
flowchart LR A[Action 1] -->|success| B[Action 2] B -->|success| C[Action 3] C -->|FAIL| D[Start Saga] D --> E[Compensate Action 2] E --> F[Compensate Action 1] F --> G[Clean state]sequenceDiagram participant P as Pipeline participant A1 as charge_card participant A2 as reserve_inventory participant A3 as create_shipment participant S as Saga P->>A1: execute A1-->>P: success (transaction_id) P->>A2: execute A2-->>P: success (item_id) P->>A3: execute A3-->>P: FAIL (after retry exhaustion) P->>S: trigger compensation S->>A2: release_inventory(ctx) A2-->>S: done S->>A1: refund_card(ctx) A1-->>S: done S-->>P: saga complete — clean stateBehavior rules
compensateparameter are skipped during rollbackTask result with Saga
When Saga is triggered, the workflow result should include compensation status:
{ "workflow_id": "037dbfb0-5f19-4f30-970b-4b00840c58b3", "saga": { "triggered": true, "failed_task_id": 2, "compensations": [ { "task_id": 1, "name": "release_inventory", "status": "Completed", "duration": 0.002 }, { "task_id": 0, "name": "refund_card", "status": "Completed", "duration": 0.015 } ] }, "tasks": ["..."] }Open questions
DotFlow(saga=False))?SagaCompensationErrorwith details of what could not be rolled back?Related issues