Saga steps and compensations must be retryable because network failures can hide a successful response and transient failures can disappear on a later attempt. An orchestrator also needs a deterministic point at which it stops retrying and starts compensating completed work.
Design a SagaExecutionPlanner class:
SagaExecutionPlanner() creates a stateless planner.String[] plan(String[] steps, int[] failuresBeforeSuccess, int maxAttempts) returns the ordered action trace.
For step i, failuresBeforeSuccess[i] is the number of attempts that fail before the step would succeed. Each actual attempt contributes "TRY:<step>" to the trace.
- If
failuresBeforeSuccess[i] < maxAttempts, the step succeeds on attempt failuresBeforeSuccess[i] + 1, and the saga continues. - If
failuresBeforeSuccess[i] >= maxAttempts, all allowed attempts fail. Emit exactly maxAttempts tries, stop forward execution, and append "COMPENSATE:<step>" for every previously completed step in reverse order.
The permanently failing step did not complete and is not compensated. Steps after it are never attempted.
Example 1:
Input:
Output:
Explanation: Reserve succeeds on its second try and charge on its first. Ship needs more than two attempts, so it exhausts the cap and triggers reverse compensation of the completed prefix.
Example 2:
Input:
Output:
Explanation: Zero preceding failures means each step succeeds on its first attempt.
Constraints
1 <= steps.length <= 100failuresBeforeSuccess.length == steps.length0 <= failuresBeforeSuccess[i] <= 1001 <= maxAttempts <= 20- Step names are non-empty and contain no colon.
- At most
100 calls are made to plan, and calls are independent.