Skip to navigation

Reconnect without a silent gap

View as Markdown

Reconnect from the last locally committed checkpoint, not the last received batch or the last ACK in a log.

Normal reconnect

  1. Fence handlers from the old connection before they can update the new state.
  2. Receive a new hello, validate stream/history and authenticate.
  3. Send subscribe with the same consumer/subscription IDs and exact filter, using start.mode: "resume" and the saved token unchanged.
  4. Validate the accepted subscribed binding. ACKs now use its generation and the new connection ID.
  5. Apply replay through the ordinary idempotent reducer, then continue live after the checkpoint and caught_up at the cut.

For committed checkpoint C=42 and cut H=47, replay scans (42,47]. Even when no events match, the checkpoint reaches the cut. Do not wait for a trade before saving progress.

Crash boundaries

BoundaryRecovery
Batch received before local commitResume from the earlier token; apply redelivery once
Effects/token committed before ACKResume from the newly saved token
ACK lost after local commitThe durable client token remains authoritative
Old socket still drains buffered framesReject the stale session locally; do not mix its effects or ACKs into the current session

Server ACK persistence can lag behind client state. A valid saved token does not depend on the server having persisted its ACK. Normal live sends are not stop-and-wait, but finite unacknowledged windows can close a slow session.

When retry is insufficient

cursor_mismatch requires restoring the exact binding or explicitly choosing another start. reset_required means continuity cannot be guaranteed. Preserve the token and resolve the gap explicitly; do not silently delete it and choose live_cut.

For retryable overload, use finite backoff with the same committed token. Limit attempts/time and watch the retention boundary. Retryability does not promise unlimited history.

Durable state recovery does not make external trading actions exactly-once. Those actions need their own idempotency and reconciliation. See retention/reset and errors.