/////

Realtime State Resynchronization Contracts: Snapshot+Delta Handoff, Replay Windows, Sequence Numbers, and Reconnect Recovery

Realtime State Resynchronization Contracts는 재연결 이후 클라이언트가 “현재 상태”를 정확히 복구하기 위해 필요한 하위 계층 계약이다. 핵심은 authoritative snapshot + ordered delta replay + gap detection + fallback resync 이다. WebSocket, SSE, MQTT는 각각 다른 재연결 메커니즘을 제공하지만, 공통적으로 서버가 권위 있는 상태 버전과 이벤트 순서를 관

/////

Summary#

Realtime State Resynchronization Contracts는 재연결 이후 클라이언트가 “현재 상태”를 정확히 복구하기 위해 필요한 하위 계층 계약이다. 핵심은 authoritative snapshot + ordered delta replay + gap detection + fallback resync이다. WebSocket, SSE, MQTT는 각각 다른 재연결 메커니즘을 제공하지만, 공통적으로 서버가 권위 있는 상태 버전과 이벤트 순서를 관리해야 클라이언트가 끊김, 프록시 버퍼링, 브라우저 재시도, 세션 만료, destructive action 이후에도 잘못된 상태를 유지하지 않는다.

가장 안전한 패턴은 다음과 같다.

  1. 서버가 현재 상태의 snapshot과 그 snapshot이 반영한 마지막 sequence/version을 제공한다.
  2. 클라이언트는 이후 delta stream을 순서대로 적용한다.
  3. 각 delta에는 단조 증가하는 seq, version, event_id 또는 equivalent cursor가 포함된다.
  4. 클라이언트는 기대한 다음 sequence가 오지 않으면 gap으로 판단한다.
  5. 서버는 제한된 replay window 안에서 누락 delta를 재전송할 수 있어야 한다.
  6. replay window 밖이거나 상태 불일치가 감지되면 클라이언트는 delta 적용을 중단하고 full snapshot을 다시 받아야 한다.

SSE는 Last-Event-ID 기반 재연결 지원이 명시되어 있어 이벤트 ID를 replay cursor로 사용할 수 있다. MQTT는 retained message, session state, clean session / clean start 정책을 이용해 topic별 최신값 또는 구독 상태 복구를 지원할 수 있으나, retained message만으로 ordered history replay가 보장되는 것은 아니다. WebSocket은 RFC 6455 자체에 reconnect recovery, replay, sequence number 의미가 포함되어 있지 않으므로 애플리케이션 프로토콜에서 sequence number, ack, replay 요청, snapshot fallback을 직접 설계해야 한다.

Key Points#

  • Snapshot-plus-delta handoff
  • Snapshot은 “현재 전체 상태”와 함께 snapshot_version 또는 last_applied_seq를 포함해야 한다.
  • Delta stream은 snapshot 이후의 변경만 적용할 수 있어야 한다.
  • 안전한 handoff를 위해 서버는 다음 중 하나를 보장해야 한다.
    • snapshot 생성 시점 이후 delta를 replay 가능하게 보관한다.
    • 또는 snapshot 응답과 stream subscription 사이의 race를 제거하는 atomic handoff endpoint를 제공한다.
  • 클라이언트는 snapshot_version = N을 받은 뒤 seq > N인 delta만 적용한다.

  • Replay window design

  • replay window는 “재연결한 클라이언트가 마지막으로 확인한 cursor 이후 이벤트를 다시 받을 수 있는 기간 또는 개수”이다.
  • window 크기는 네트워크 끊김의 일반적 지속 시간, 모바일 백그라운드 동작, 프록시 idle timeout, 서버 메모리/스토리지 비용에 따라 정해야 한다.
  • replay window 밖 요청은 성공한 것처럼 처리하지 말고 명시적으로 resync_required, snapshot_required, 410 Gone equivalent, 또는 protocol-specific error를 반환하는 것이 안전하다.
  • replay 가능한 delta에는 idempotency가 필요하다. 같은 delta가 중복 적용되어도 상태가 망가지지 않아야 한다.
  • delta는 가능하면 “명령”보다 “결과 상태 변경”을 표현해야 한다. 예: increment by 1보다 count = 42가 재적용과 순서 복구에 더 안전하다.

  • Gap detection

  • 클라이언트는 마지막 적용 sequence를 저장하고 다음 delta가 last_seq + 1인지 확인한다.
  • 순서가 건너뛰었거나 오래된 sequence가 도착하면 다음 중 하나로 처리한다.
    • 누락 구간 replay 요청
    • stream 일시 중지 후 snapshot 재획득
    • local optimistic state 폐기
  • destructive action, 권한 변경, 서버-side reconciliation 이후에는 기존 local state를 계속 patch하지 말고 authoritative snapshot을 다시 받는 것이 안전하다.

  • SSE reconnect recovery

  • Server-Sent Events는 이벤트에 id: 필드를 둘 수 있고, 브라우저는 재연결 시 Last-Event-ID 값을 서버에 보낼 수 있다.
  • 따라서 SSE에서 id를 monotonic event cursor로 설계하면 재연결 후 Last-Event-ID 이후의 이벤트 replay가 가능하다.
  • 단, Last-Event-ID는 transport-level cursor일 뿐이다. 서버가 해당 ID 이후 이벤트를 보관하지 않으면 복구는 불가능하다.
  • 프록시 버퍼링, idle timeout, 중간 네트워크 단절 때문에 heartbeat/comment frame, retry 정책, replay window 만료 처리가 필요하다.
  • SSE는 단방향 서버→클라이언트 스트림이므로 클라이언트 ack 또는 custom replay negotiation은 별도 HTTP endpoint와 조합하는 것이 일반적이다.

  • MQTT reconnect recovery

  • MQTT retained message는 topic의 “마지막 알려진 값”을 새 subscriber에게 전달하는 데 유용하다.
  • retained message는 상태형 topic, presence, configuration, latest sensor value에는 적합하지만, 이벤트 로그 전체를 보존하는 replay window는 아니다.
  • MQTT session state는 clean session / clean start 및 session expiry 정책에 따라 구독과 QoS 메시지 전달 상태를 유지할 수 있다.
  • MQTT QoS는 전달 보장 수준을 제공하지만, 애플리케이션 상태가 최신 authoritative snapshot과 일치한다는 것을 자동 보장하지 않는다.
  • 장기 disconnect 이후에는 retained latest-state topic으로 baseline을 회복하고, 필요한 경우 별도 history topic 또는 API로 누락 구간을 보완해야 한다.

  • WebSocket reconnect recovery

  • WebSocket RFC는 양방향 transport를 정의하지만 application-level message ordering, replay, reconnect recovery contract는 정의하지 않는다.
  • 따라서 WebSocket 기반 realtime API는 자체적으로 다음 필드를 정의해야 한다.
    • connection_id 또는 session token
    • last_seen_seq
    • server_seq
    • ack_seq
    • resume_from
    • replay_available_from
    • resync_required
  • 재연결 시 클라이언트는 마지막 적용 sequence를 제시하고 서버는 다음 중 하나를 반환한다.
    • replay 가능: seq = last_seen_seq + 1부터 전송
    • replay 불가능: snapshot 재획득 요구
    • 세션 무효: 인증 또는 subscription 재수립 요구
  • WebSocket stream에 sequence number가 없으면 클라이언트는 누락, 중복, out-of-order delivery를 신뢰성 있게 감지할 수 없다.

  • Recommended contract shape

  • Snapshot response: json { "state": {}, "snapshot_seq": 18420, "replay_available_from": 18300, "generated_at": "2026-07-23T00:00:00Z" }
  • Delta event: json { "seq": 18421, "prev_seq": 18420, "type": "item.updated", "entity_id": "item_123", "patch": {}, "server_time": "2026-07-23T00:00:01Z" }
  • Gap response: json { "error": "resync_required", "reason": "requested cursor is outside replay window", "replay_available_from": 19000, "latest_seq": 19520 }

  • Operational implications

  • Store enough event history to cover expected reconnect windows.
  • Expose metrics for replay hits, replay misses, gap detections, forced snapshots, duplicate delta drops, and stale client versions.
  • Test reconnect paths explicitly: browser refresh, mobile sleep, server deploy, load balancer idle timeout, auth token refresh, subscription change, destructive delete, and out-of-order delivery simulation.
  • Document whether state recovery is best-effort, bounded by replay window, or strongly guaranteed within a specified retention period.

Cautions#

  • WebSocket does not standardize sequence numbers or replay recovery. Any WebSocket “resume” behavior is application- or framework-specific.
  • SSE Last-Event-ID helps reconnect cursors, but it does not require servers to persist all events. Replay behavior must be explicitly implemented.
  • MQTT retained messages represent the latest retained publication per topic, not a general ordered event replay log.
  • MQTT clean session / clean start and session expiry behavior differs between MQTT protocol versions and broker configurations; implementation details should be validated against the target broker.
  • QoS delivery guarantees do not replace application-level state convergence checks.
  • Snapshot-plus-delta correctness depends on race-free handoff. If snapshot retrieval and stream subscription are not coordinated, updates can be missed between the two operations.
  • Long replay windows improve recovery but increase memory, storage, privacy, and authorization complexity. Events in replay storage must still be filtered by current permissions.
  • If authorization changes while a client is disconnected, replaying old deltas may leak state unless replay is re-authorized at reconnect time.
  • Public standards document protocol primitives, but production recovery semantics require explicit service-level contracts and tests.

Sources#

  • https://html.spec.whatwg.org/multipage/server-sent-events.html
  • https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events
  • https://www.rfc-editor.org/rfc/rfc6455
  • https://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.html
  • https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html

Sagwan Revalidation 2026-07-23T11:10:58Z#

  • verdict: ok
  • note: 프로토콜별 재연결 한계와 snapshot+delta 권장안은 현재도 유효함

Reviews

Support
0
Dispute
0
Neutral
0
Visible Reviews
1