Causal Ordering
By default, commit envelope events may be processed by participants in any order. For high-throughput streams where out-of-order processing could cause consistency issues, you can enable strict causal ordering.
What problem does this solve?
Consider a wallet that receives many small top-ups and transfers simultaneously. Without ordering, two TRANSFER_PREPARED events for the same wallet might stage in arbitrary order, leading to incorrect intermediate balance calculations.
With causal_ordering: { mode: strict }, the runtime assigns monotonically increasing sequence numbers to participant events and ensures each participant processes them in order.
Configuration
commit_envelopes:
wallet_transfer:
# ... other fields ...
causal_ordering:
mode: strict
cursor_field: cursor
seq_source: envelope| Field | Required | Description |
|---|---|---|
mode | yes | strict — enforce monotonic sequence ordering |
cursor_field | yes | The field name in participant event payloads that carries the sequence number |
seq_source | yes | envelope — the envelope assigns sequence numbers to each participant’s events |
How it works
When causal_ordering is enabled:
- The envelope assigns a
cursorvalue (sequence number) to each participant event at prepare time - Participants process events in cursor order — lower cursor values first
- If an event arrives out of order, the runtime holds it until its predecessors are processed
- Out-of-order events are retried automatically once predecessors complete
The cursor_field value appears in the event payload so participant rules and projections can reference it:
derive:
cursor: event.cursor # sequence number for this participant event
applied_at: event.tsWhen to use strict ordering
| Situation | Use ordering? |
|---|---|
| Low-volume transfers (< 100/s per entity) | Optional |
| High-volume wallet top-ups / debits | Yes |
| Multiple envelopes targeting the same entity simultaneously | Yes |
| One-time operations (e.g., account creation) | No |
Strict ordering adds latency because the runtime must hold out-of-order events. For most applications with modest throughput, you can omit causal_ordering entirely.