Lifecycle
Each saga passes through well-defined states during its execution. This ensures complete traceability and predictable recovery.
Saga States
┌─────────┐
│ STARTED │
└────┬────┘
│
▼
┌───────────┐
│ EXECUTING │◄──────────────────┐
└─────┬─────┘ │
│ │
┌─────────────┴─────────────┐ (retry)
│ │ │
(success) (failure) │
│ │ │
▼ ▼ │
┌───────────┐ ┌─────────────┐ │
│ COMPLETED │ │ COMPENSATING│─────┘
└───────────┘ └──────┬──────┘
│
┌──────────────┴──────────────┐
│ │
(success) (failure)
│ │
▼ ▼
┌─────────────┐ ┌────────┐
│ COMPENSATED │ │ FAILED │
└─────────────┘ └────────┘
State Descriptions
STARTED
Initial state when the saga is created.
SagaExecution execution = sagaManager.start(PixPaymentSaga.class, context);
// execution.sagaId() returns the generated UUID for tracking
| Aspect | Value |
|---|---|
| Duration | Milliseconds |
| Next | EXECUTING |
| Trigger | sagaManager.start() |
EXECUTING
The saga is processing steps sequentially.
EXECUTING: step 1 of 4 - validate-dict ●
| Aspect | Value |
|---|---|
| Duration | Variable |
| Next | COMPLETED or COMPENSATING |
| Trigger | First step begins |
COMPLETED
All steps were executed successfully. Positive final state.
COMPLETED ✓ - 4/4 steps executed in 2.3s
| Aspect | Value |
|---|---|
| Duration | Final |
| Next | - |
| Trigger | Last step completed |
COMPENSATING
A step failed and compensations are being executed in reverse order.
COMPENSATING: rollback step 2 of 3 - block-balance ↩️
| Aspect | Value |
|---|---|
| Duration | Variable |
| Next | COMPENSATED or FAILED |
| Trigger | Step failure after retries |
COMPENSATED
All compensations were executed successfully. The saga was rolled back.
COMPENSATED ↩️ - 3 compensations executed in 0.8s
| Aspect | Value |
|---|---|
| Duration | Final |
| Next | - |
| Trigger | Last compensation completed |
FAILED
Terminal failure state. Occurs when:
- A PIVOT fails after all retry attempts
- A compensation fails (saga goes to Dead Letter)
FAILED ✗ - transmit-to-bacen: timeout after 3 attempts
| Aspect | Value |
|---|---|
| Duration | Final |
| Next | Manual (Dead Letter) |
| Trigger | Unrecoverable failure |
State Transitions
Scenario: Complete Success
STARTED → EXECUTING → COMPLETED
│ │ │
│ │ └─ End
│ └─ Steps 1, 2, 3, 4 ✓
└─ Start
Scenario: Failure with Compensation
STARTED → EXECUTING → COMPENSATING → COMPENSATED
│ │ │ │
│ │ │ └─ End (rollback OK)
│ │ └─ comp 2, comp 1 ↩️
│ └─ step 1 ✓, step 2 ✓, step 3 ✗
└─ Start
Scenario: PIVOT Failure
STARTED → EXECUTING → COMPENSATING → FAILED
│ │ │ │
│ │ │ └─ Dead Letter
│ │ └─ comp 2 ✓, comp 1 ✗
│ └─ step 1 ✓, step 2 ✓, step 3 (PIVOT) ✗
└─ Start
State Query
By ID (Java)
// Via Observability API: GET /api/sagas/{sagaId}
// Via SagaEngine (direct injection):
Optional<SagaInstance> instance = engine.findById(sagaId);
instance.ifPresent(i -> {
System.out.println("Status: " + i.status());
System.out.println("Steps: " + i.steps().size());
});
Via REST API
# Failed sagas
GET /api/sagas?status=FAILED
# Specific saga
GET /api/sagas/{id}
# Aggregated metrics
GET /api/sagas/metrics
tip
The Observability API provides filters by status, name, date, and idempotency key. See Observability API →.
Lifecycle Events
Sagaweaw emits events for each transition:
@Component
public class SagaEventListener {
@EventListener
public void onSagaStarted(SagaStartedEvent event) {
log.info("Saga {} started", event.getSagaId());
}
@EventListener
public void onSagaCompleted(SagaCompletedEvent event) {
log.info("Saga {} completed in {}ms",
event.getSagaId(),
event.getDuration());
}
@EventListener
public void onSagaFailed(SagaFailedEvent event) {
log.error("Saga {} failed: {}",
event.getSagaId(),
event.getError());
}
}