Commit EnvelopesCausal Ordering

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
FieldRequiredDescription
modeyesstrict — enforce monotonic sequence ordering
cursor_fieldyesThe field name in participant event payloads that carries the sequence number
seq_sourceyesenvelope — the envelope assigns sequence numbers to each participant’s events

How it works

When causal_ordering is enabled:

  1. The envelope assigns a cursor value (sequence number) to each participant event at prepare time
  2. Participants process events in cursor order — lower cursor values first
  3. If an event arrives out of order, the runtime holds it until its predecessors are processed
  4. 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.ts

When to use strict ordering

SituationUse ordering?
Low-volume transfers (< 100/s per entity)Optional
High-volume wallet top-ups / debitsYes
Multiple envelopes targeting the same entity simultaneouslyYes
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.