Saga Retention
Completed sagas are kept as tombstones so a late or duplicate event is skipped idempotently rather than resurrecting a finished saga. Retention removes those tombstones once they are old enough to no longer serve that purpose.
Retention is off by default. When enabled, a single-active sweeper periodically deletes completed sagas whose completion is older than a configurable age.
Enabling retention
spring:
ddd:
saga:
retention:
enabled: true
max-age: P30D # delete completed sagas older than 30 days
With only enabled: true, the sweep runs every hour. Completion is recorded in the completed_at column
of the saga instance table; the sweeper deletes rows whose completed_at is older than max-age.
A short max-age (for example PT30S) approximates delete-on-completion while still keeping the
idempotency tombstone for the grace window.
|
Scheduling
Choose either a fixed delay or a cron expression — setting both while enabled is rejected at startup.
| Property | Behaviour |
|---|---|
|
Fixed delay between sweeps (for example |
|
Spring 6-field cron expression (for example |
Cron is the better fit for a maintenance sweep — it lets you target an off-peak window instead of an arbitrary wall-clock time.
spring:
ddd:
saga:
retention:
enabled: true
max-age: P30D
polling:
cron: "0 0 3 * * *" # 03:00 every day
Per-saga overrides
Override the window or opt a saga type out entirely, keyed by the saga’s @Saga(type) id. Each field
falls back to the global value. Overrides refine the global policy while it is active — retention runs only
when the top-level enabled is true, so a per-type enabled: true cannot switch retention on by itself.
spring:
ddd:
saga:
retention:
enabled: true
max-age: P30D
overrides:
order-saga:
enabled: false # never delete order sagas
payment-saga:
max-age: P7D # keep payment sagas for 7 days
How the sweep runs
The sweeper is a single active instance across a deployment: each run acquires a lease before deleting, so
a saga is swept once even with many application instances. The lease reuses the deadline sweep-lock table
(SAGA_DEADLINE_LOCK) under a distinct lock name, so retention needs no additional table. Deletion runs in
batches of polling.batch-size (default 100, maximum 1000) and drains until a batch is not full.
|
The |
For the full spring.ddd.saga.retention property table, see
Configuration properties.
Related pages
-
Overview — concept, delivery model, lifecycle, starters.
-
Writing a saga —
@Saga,@SagaEventHandler, handler parameters, worked example. -
Storage & tables —
@SagaTable, JSON persistence, thecompleted_atcolumn, schema creation. -
Deadlines & timeouts — scheduling and cancelling deadlines from within a handler.
-
Ordered event delivery — opt-in ordered inbound from an event-sourced store.