Skip to main content

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
AspectValue
DurationMilliseconds
NextEXECUTING
TriggersagaManager.start()

EXECUTING

The saga is processing steps sequentially.

EXECUTING: step 1 of 4 - validate-dict ●
AspectValue
DurationVariable
NextCOMPLETED or COMPENSATING
TriggerFirst step begins

COMPLETED

All steps were executed successfully. Positive final state.

COMPLETED ✓ - 4/4 steps executed in 2.3s
AspectValue
DurationFinal
Next-
TriggerLast step completed

COMPENSATING

A step failed and compensations are being executed in reverse order.

COMPENSATING: rollback step 2 of 3 - block-balance ↩️
AspectValue
DurationVariable
NextCOMPENSATED or FAILED
TriggerStep failure after retries

COMPENSATED

All compensations were executed successfully. The saga was rolled back.

COMPENSATED ↩️ - 3 compensations executed in 0.8s
AspectValue
DurationFinal
Next-
TriggerLast 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
AspectValue
DurationFinal
NextManual (Dead Letter)
TriggerUnrecoverable 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());
}
}

Next Steps