합계 재가산과 증가 중복
CRDT 카운터를 선택하려면 먼저 변경이 “누적된 관찰 상태”인지 “한 번만 실행되어야 하는 업무 이벤트”인지 구분합니다. 전자는 병합 대수로 다룰 수 있지만, 후자는 이벤트 ID·권한·전역 조건을 추가하지 않으면 카운터만으로 중복 의미를 표현할 수 없습니다.
A가 5, B가 3을 만들고 A가 B의 합계를 받아 8로 저장했습니다. B가 그 8을 받아 자기 3에 더하면 11이 됩니다. 이미 A가 포함한 B의 3을 다시 센 것입니다. 네트워크 재전달·반복 동기화가 있는 환경에서 전체 합계를 덧셈으로 병합할 수는 없습니다.
CRDT는 특정 상태·연산·병합 규칙과 전달 가정 아래 중복·재정렬에도 수렴하도록 설계한 자료형 계열입니다. 임의 데이터를 아무렇게나 합쳐도 충돌이 없다는 의미가 아닙니다.
G-counter의 참여자별 누적값
참여자 A·B가 각자 자기 성분만 증가시키는 grow-only counter를 생각합니다. A의 상태 [5,0], B의 상태 [0,3]을 성분별 max로 병합하면 [5,3], 합계 8입니다. 같은 상태를 다시 받아도 max 결과는 그대로입니다.
| 수신 전 | 받은 상태 | 성분별 max | 표시 합계 |
|---|---|---|---|
| [5,0] | [0,3] | [5,3] | 8 |
| [5,3] | [0,3] | [5,3] | 8 |
| [5,3] | [4,2] | [5,3] | 8 |
| [5,3] | [0,4] | [5,4] | 9 |
increment_at(node, delta):
require delta >= 0
state[node] += delta
merge(remote):
for node in union of participant IDs:
state[node] = max(state[node], remote[node])
value():
return sum(state.values)
이것은 state-based G-counter 모형입니다. 메시지는 단순 delta 명령이 아니라 성분별 누적 상태를 전달합니다. operation-based CRDT의 전달·중복 조건은 별도로 정의해야 하며 같은 코드라고 보지 않습니다.
그림을 글로 읽기
- A 상태 [5,0]
- B 상태 [0,3]
- 병합 [5,3] · 합계 8
- A 상태 [5,0] → 병합 [5,3] · 합계 8: 성분별 max
- B 상태 [0,3] → 병합 [5,3] · 합계 8: 성분별 max
교환성·결합성·멱등성과 반복 동기화
max 병합은 순서를 바꿔도 같은 교환성, 묶는 순서를 바꿔도 같은 결합성, 같은 상태와 합쳐도 같은 멱등성을 가집니다. 각 성분이 단조 증가하고 필요한 상태가 결국 전달되는 전제 아래 같은 증가 집합에 대해 수렴할 수 있습니다. 전송이 영원히 끊기면 최신 합계를 모든 곳이 즉시 아는 것은 아닙니다.
감소가 필요하면 증가용 G-counter P와 감소용 N을 따로 유지하고 sum(P)-sum(N)을 표시하는 PN-counter 같은 모델이 있습니다. 두 성분 집합은 각각 max로 병합합니다. 하나의 성분을 음수로 낮추면 grow-only 단조 조건이 깨집니다.
G-counter는 모든 참여자가 자기 성분을 단조 증가시키고 상태가 결국 전달된다는 전제에 맞습니다. 감소·취소·보정이 있으면 PN-counter, 재고·잔액처럼 하한을 지켜야 하면 escrow나 권위적 조건부 쓰기를 검토합니다. “오프라인 가능”만으로 G-counter가 적합해지는 것은 아닙니다.
재시작·참여자 ID·누적 상태 보존
A가 자기 5를 잃고 같은 ID로 0부터 다시 시작하면, 다른 노드가 기억한 5와 max할 때 새 증가가 옛 최댓값 아래에 가려질 수 있습니다. 같은 ID를 두 실행자가 동시에 쓰면 두 증가 중 더 큰 성분만 남아 다른 증가가 사라질 수 있습니다. 그러므로 참여자 identity·epoch를 실행 단위와 함께 관리하고, 자기 성분의 내구 보존과 단일 writer 규칙을 지켜야 합니다.
참여자가 많아지면 metadata가 커집니다. 옛 성분을 지우면 지연된 상태와 다시 병합할 때 과거 증가를 잃거나 재계수할 수 있어 causal stability·퇴역·안전한 축약·snapshot 복구 프로토콜을 마련합니다. “노드가 지금 오프라인”만으로 삭제할 수는 없습니다.
수렴과 잔액 규칙의 분리
A·B가 각각 잔액 10을 보고 8씩 감소시키면 합친 결과가 -6이 될 수 있습니다. PN-counter가 정확히 수렴해도 잔액 음수 금지·마지막 좌석 중복 판매 같은 전역 조건은 깨집니다. 단일 권위 조건부 변경 또는 미리 분배한 제한된 권리·escrow 등 다른 모델이 필요합니다.
좋아요도 논리 사용자별 한 번 같은 제약이 있다면 단순 증가 counter로 표현 가능한지 검토합니다. 같은 사용자 이벤트를 두 번 “새 증가”로 실행하면 state merge의 멱등성이 그 앱 중복을 자동 제거하지 않습니다. 논리 이벤트와 참여자 누적 연산의 경계를 유지합니다.
병합 순서·재전달 전수 비교
작은 성분 상태의 병합 순서를 바꾸고 중복·옛 상태를 끼워 넣어 같은 결과인지 확인합니다. 재시작의 성분 손실·ID 재사용·감소 모델과 하한 반례도 따로 검사합니다. 현재 본문은 state-based counter의 설명이며 실제 분산 CRDT 제품을 실행한 결과가 아닙니다.
예상 결과는 A=[5,0], B=[0,3]을 어떤 순서와 중복으로 병합해도 [5,3]이 되는 것이지만, A가 같은 ID를 잃고 0부터 재시작하면 새 증가가 옛 최댓값 아래에서 사라질 수 있는 것입니다. 따라서 시험은 정상 재전달과 identity/epoch 손실을 분리해 기록해야 합니다.