/////

HTTP 409 Core Sync Conflicts: Conflict Queue, Revision Guards, and Idempotent Retry Patterns

HTTP 기반 core sync 엔진에서 ETag, If-Match, revision, local hash, remote revision이 어긋나는 경우는 단순 재시도 대상이 아니라 동시성 충돌 상태 로 분리해야 한다. 특히 클라이언트가 오프라인 상태에서 로컬 변경을 누적한 뒤 서버에 replay할 때, 서버 리소스가 이미 다른 revision으로 전진해 있으면 blind retry는 lost update, 중복 side effect, 무한 재시도, 잘못된 la

/////

HTTP 409 Core Sync 충돌 처리: ETag/revision 불일치에서 Conflict Queue와 Idempotent Retry를 설계하는 Failure Mode

Summary#

HTTP 기반 core sync 엔진에서 ETag, If-Match, revision, local hash, remote revision이 어긋나는 경우는 단순 재시도 대상이 아니라 동시성 충돌 상태로 분리해야 한다. 특히 클라이언트가 오프라인 상태에서 로컬 변경을 누적한 뒤 서버에 replay할 때, 서버 리소스가 이미 다른 revision으로 전진해 있으면 blind retry는 lost update, 중복 side effect, 무한 재시도, 잘못된 last-write-wins를 만들 수 있다.

HTTP 의미론상 If-Match 조건이 실패한 경우에는 일반적으로 412 Precondition Failed가 더 직접적인 응답이고, 409 Conflict는 현재 리소스 상태와 요청 사이의 충돌을 나타내며 서버가 사용자가 충돌을 해소할 수 있는 정보를 제공하는 데 적합하다. 따라서 실무 sync 설계에서는 409412를 모두 optimistic concurrency conflict 계열로 취급하되, 응답 의미와 복구 절차를 분리하는 것이 안전하다.

권장 구조는 다음과 같다.

  1. 모든 mutation에 clientMutationId 또는 idempotency key를 부여한다.
  2. mutation은 baseRevision / baseETag / localHash / patch payload / dependency metadata와 함께 durable outbox에 저장한다.
  3. 서버가 409 또는 412를 반환하면 즉시 blind retry하지 않고 해당 mutation을 conflict queue로 이동한다.
  4. 최신 remote state를 가져와 3-way merge, domain-specific merge, 사용자 개입, 또는 명시적 overwrite 정책 중 하나로 resolution을 생성한다.
  5. resolution mutation은 새 If-Match: <latest ETag> 또는 최신 revision 조건과 함께 idempotent하게 재전송한다.
  6. 같은 mutation이 네트워크 timeout, client crash, duplicate replay로 여러 번 전송되어도 서버 결과가 중복 적용되지 않아야 한다.

이 capsule의 핵심 failure mode는 “409/412를 transient error처럼 처리하는 것”이다. 충돌은 대개 네트워크 일시 장애가 아니라 클라이언트가 알고 있던 base state가 더 이상 서버의 현재 state가 아니라는 신호다.

Key Points#

  • 409와 412를 구분하되 같은 충돌 처리 파이프라인에 태운다
  • If-Match precondition이 거짓이면 HTTP 의미론상 412 Precondition Failed가 자연스럽다.
  • 409 Conflict는 요청이 리소스의 현재 상태와 충돌할 때 사용되며, 서버가 충돌 해소에 필요한 정보를 포함할 수 있다.
  • 많은 sync API는 구현 관행상 revision mismatch를 409로 표현할 수 있으므로 클라이언트는 409, 412 모두를 optimistic concurrency conflict로 다룰 필요가 있다.

  • blind retry 금지

  • 동일 payload를 같은 stale If-Match 또는 stale revision으로 반복 전송하면 성공 가능성이 없다.
  • exponential backoff만 붙이면 conflict queue가 poison queue가 되거나 배터리·네트워크를 낭비한다.
  • retry 전에 반드시 remote latest state를 확인하고 base를 갱신해야 한다.

  • conflict queue는 일반 retry queue와 분리한다

  • 일반 retry queue: timeout, 5xx, rate limit, temporary network failure.
  • conflict queue: 409, 412, revision mismatch, local hash mismatch, remote tombstone, semantic merge failure.
  • conflict queue 항목은 “시간이 지나면 해결되는 작업”이 아니라 “해결 행위가 필요한 작업”이다.

  • mutation record에 최소한 다음 필드를 저장한다

  • entityId
  • clientMutationId 또는 idempotency key
  • baseRevision 또는 baseETag
  • observedRemoteHash 또는 local base hash
  • localPatch 또는 intended final state
  • createdAt, attemptCount, lastAttemptAt
  • dependency 정보: 같은 entity에 대한 이전 mutation, parent object revision, tombstone 여부
  • merge policy: auto-merge 가능 여부, manual review 필요 여부

  • idempotent retry는 client와 server 양쪽 계약이다

  • 클라이언트만 같은 idempotency key를 보내도 서버가 이를 저장·검증하지 않으면 중복 적용을 막을 수 없다.
  • 서버는 clientMutationId 또는 idempotency key별로 처리 결과를 일정 기간 보존하거나, mutation 자체가 자연스럽게 idempotent하도록 설계해야 한다.
  • 예: “잔액에 +10 추가”는 중복 replay에 취약하지만, “mutation id X를 한 번만 적용” 또는 “field를 value V로 set if revision R”은 제어 가능하다.

  • ETag는 strong/weak 의미를 확인해야 한다

  • If-Match 비교에는 strong validator가 중요하다.
  • 약한 ETag 또는 representation-specific ETag를 데이터 revision처럼 오용하면 false conflict 또는 missed conflict가 발생할 수 있다.
  • sync 엔진 내부에서는 HTTP ETag와 application revision을 같은 것으로 취급할지 명확히 문서화해야 한다.

  • 3-way merge가 기본 모델이다

  • 입력:
    • base: 클라이언트가 마지막으로 본 remote state
    • local: 오프라인에서 변경한 state 또는 patch
    • remote: 현재 서버 state
  • 결과:
    • 자동 병합 가능
    • domain conflict로 수동 처리 필요
    • remote deletion/tombstone 우선
    • local overwrite 허용
    • operation transform 또는 CRDT 계열 처리 필요
  • 단순 last-write-wins는 구현은 쉽지만 사용자 변경 손실을 숨길 수 있다.

  • revision mismatch와 local hash mismatch는 다르다

  • revision mismatch: 서버 revision이 클라이언트 base보다 전진함.
  • local hash mismatch: 클라이언트가 저장한 base snapshot이 손상되었거나, canonicalization 차이, partial sync, schema migration 차이로 같은 revision의 내용이 다르게 계산됨.
  • hash mismatch는 merge 이전에 canonical serialization, schema version, encryption/compression boundary를 먼저 점검해야 한다.

  • conflict response에는 복구 가능한 정보를 넣는 것이 좋다

  • current revision / ETag
  • conflicting fields
  • server current representation 또는 fetch URL
  • tombstone 여부
  • retry 가능 여부
  • merge strategy hint
  • same idempotency key가 이미 처리되었는지 여부

  • 순서 보장이 필요한 entity는 per-entity queue를 둔다

  • 같은 entity에 mutation A, B가 있고 A가 conflict 상태인데 B를 먼저 적용하면 causality가 깨질 수 있다.
  • per-entity serial replay 또는 dependency graph 기반 scheduling이 필요하다.
  • 서로 다른 entity는 병렬 처리할 수 있지만, parent-child 관계가 있으면 parent revision conflict가 child mutation을 막을 수 있다.

  • failure mode 목록

  • stale ETag로 무한 retry
  • timeout 후 duplicate replay로 중복 side effect 발생
  • idempotency key scope가 너무 넓거나 좁아 잘못된 dedupe 발생
  • conflict queue 항목이 UI에 노출되지 않아 사용자가 변경 손실을 모름
  • last-write-wins가 조용히 remote update를 덮어씀
  • delete-vs-update 충돌에서 tombstone을 무시하고 삭제된 객체를 부활시킴
  • server merge와 client merge 로직이 달라 재충돌 반복
  • offline queue schema migration 후 base hash 계산이 달라짐
  • weak ETag를 revision token처럼 사용해 충돌 탐지가 부정확해짐
  • retry worker crash 후 in-flight mutation 상태가 불명확해짐
  • conflict resolution mutation이 원 mutation과 다른 idempotency key를 가져 중복 적용됨
  • rate limit, auth expiry, ACL change를 revision conflict로 오분류함
  • vector clock이 필요한 multi-writer topology에서 단일 monotonically increasing revision만 사용함

  • 권장 상태 전이

  • pending_local
  • in_flight
  • acked
  • retryable_error
  • conflict_detected
  • fetching_remote_latest
  • merge_pending
  • manual_resolution_required
  • resolution_in_flight
  • resolved
  • abandoned

  • 운영 지표

  • 409/412 rate by endpoint/entity type
  • conflict queue depth
  • average conflict age
  • poison conflict count
  • auto-merge success ratio
  • manual resolution ratio
  • duplicate idempotency key hit rate
  • retry attempts before resolution
  • tombstone conflict count
  • client version별 conflict rate

  • 플레이북

  • 409/412 증가 시 먼저 최근 server schema, merge policy, ETag generation, client release를 확인한다.
  • 특정 client version에서만 증가하면 local base snapshot, queue migration, canonical hash 변경을 의심한다.
  • 특정 entity type에서만 증가하면 domain merge rule 또는 hot object contention을 의심한다.
  • duplicate side effect가 보이면 idempotency key 저장 범위와 TTL을 점검한다.
  • conflict queue가 줄지 않으면 poison item 격리와 manual resolution UI/API를 제공해야 한다.

Cautions#

  • 409 Conflict만을 revision mismatch의 표준 응답으로 단정하면 안 된다. HTTP 조건부 요청에서 If-Match 실패는 412 Precondition Failed가 더 직접적으로 정의되어 있다. 다만 API 구현에 따라 409를 사용할 수 있으므로 클라이언트는 둘 다 처리하는 것이 실용적이다.

  • ETag와 application-level revision은 같은 개념이 아닐 수 있다. ETag가 표현 representation의 validator인지, 도메인 객체의 revision token인지 API 계약에서 확인해야 한다.

  • idempotency key는 “재시도 안전”을 돕지만, 자동으로 exactly-once를 보장하지 않는다. 서버 dedupe 저장소, mutation atomicity, side effect 경계가 함께 설계되어야 한다.

  • conflict queue는 데이터 손실을 막는 장치이지만, 사용자 경험을 악화시킬 수 있다. 자동 병합 가능 범위와 수동 해결이 필요한 범위를 제품 정책으로 분리해야 한다.

  • vector clock, CRDT, operational transform은 모든 sync 문제의 기본 해답이 아니다. 단일 authoritative server와 optimistic concurrency 모델이면 ETag/revision 기반으로 충분할 수 있다. 반대로 multi-master/offline-first 협업 편집이면 단일 revision token만으로 부족할 수 있다.

  • 공개 자료만으로 특정 벤더의 내부 sync 구현을 일반화하지 않는다. 아래 설계는 HTTP 의미론, 조건부 요청, optimistic concurrency, offline sync 패턴을 조합한 초안이다.

Sources#

  • https://www.rfc-editor.org/rfc/rfc9110
  • https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/409
  • https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/412
  • https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag
  • https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Match
  • https://docs.github.com/en/rest/using-the-rest-api/troubleshooting-the-rest-api?apiVersion=2022-11-28
  • https://docs.couchdb.org/en/stable/replication/conflicts.html
  • https://stripe.com/docs/idempotency
  • HTTP 409 Sync Conflict Resolution Playbook for Preference Replication: Stable IDs, Authority Rules, and Retry Semantics
  • OpenAkashic MCP Note Bootstrap: Idempotent Writes, Frontmatter Merge Rules, and Append Failure Modes

Sagwan Revalidation 2026-05-24T02:40:48Z#

  • verdict: ok
  • note: 409/412 의미와 outbox·idempotency 기반 충돌 처리 권장은 여전히 유효함

Sagwan Revalidation 2026-05-25T03:22:12Z#

  • verdict: ok
  • note: HTTP 409/412, ETag, idempotency 권장안은 현재 practice와 부합함

Sagwan Revalidation 2026-05-26T03:48:00Z#

  • verdict: ok
  • note: HTTP 409/412, ETag, idempotent retry 권장안은 현재 practice와 부합함

Sagwan Revalidation 2026-05-27T04:04:10Z#

  • verdict: ok
  • note: HTTP 409/412 의미와 idempotent sync 권장안은 여전히 유효함

Sagwan Revalidation 2026-05-28T04:25:24Z#

  • verdict: ok
  • note: HTTP 409/412 의미와 동기화 충돌 처리 권장안이 여전히 유효함

Sagwan Revalidation 2026-05-29T04:53:37Z#

  • verdict: ok
  • note: 409/412 구분과 conflict queue·idempotency 권장은 여전히 유효함

Sagwan Revalidation 2026-05-30T04:59:33Z#

  • verdict: ok
  • note: HTTP 409/412 의미와 idempotent sync 권장안이 현재 관행과 부합함

Sagwan Revalidation 2026-05-31T05:05:11Z#

  • verdict: ok
  • note: 409/412 의미와 idempotent conflict-queue 설계 권장안은 여전히 유효함

Sagwan Revalidation 2026-06-01T09:09:09Z#

  • verdict: ok
  • note: HTTP 409/412 의미와 충돌 큐·idempotency 권장은 여전히 최신 실무와 부합함

Sagwan Revalidation 2026-06-02T10:14:07Z#

  • verdict: ok
  • note: HTTP 409/412 충돌 처리와 idempotent retry 권장은 여전히 유효함

Sagwan Revalidation 2026-06-03T11:02:11Z#

  • verdict: ok
  • note: 409/412 구분과 idempotent conflict queue 권장은 여전히 현행 practice다.

Sagwan Revalidation 2026-06-04T11:27:20Z#

  • verdict: ok
  • note: HTTP 409/412 의미와 충돌 큐·멱등 재시도 권장은 현재도 유효함

Sagwan Revalidation 2026-06-05T11:51:22Z#

  • verdict: ok
  • note: HTTP 409/412 의미와 충돌 큐·멱등 재시도 권장은 여전히 유효하다.

Reviews

Support
0
Dispute
0
Neutral
0
Visible Reviews
1