분산 데이터 정합성
계약부터 시작하라
불변 조건(invariant), 그 범위, 그리고 견뎌야 할 실패를 작성하라. "주문은 최대 한 번 청구된다"는 유용하지만, "강한 일관성"은 불충분하다. 권위 있는 레코드(authoritative record), 파생된 뷰(derived views), 액터(actors), 그리고 각 커밋/확인 경계를 식별하라. 클라이언트가 읽기-쓰기 일관성(read-your-writes), 인과적 순서(causal order), 선형화 가능 연산(linearizable operations), 직렬화 가능 트랜잭션(serializable transactions)이 필요한지, 아니면 단순한 최종 수렴(eventual convergence)만 필요한지 질문하라.
검토 절차
- 데이터베이스 커밋, 브로커 확인, 외부 부수 효과를 포함한 쓰기 경로를 그려라. 각 성공 응답이 프로세스 충돌, 머신 손실 또는 복제본 장애 조치 후에 무엇을 보장하는지 기록하라. 타임아웃은 결과를 알 수 없음을 의미할 뿐, 반드시 실행 실패를 의미하지는 않는다.
- 모든 내구성 있는 단계 전후의 충돌 윈도우를 열거하라. 각각에 대해 연산을 재생하고 불변 조건을 확인하라. 순차적 재시도뿐만 아니라 동시 시도도 테스트하라. 실패 원장을 사용하라.
- 비즈니스 효과와 중복 제거 마커가 동일한 데이터베이스를 공유할 때는 하나의 트랜잭션에 유지하라. 발신 이벤트의 경우 트랜잭셔널 아웃박스(transactional outbox)를 고려하라. 원격 효과의 경우 내구성 있는 공급자 멱등성 계약 또는 조정 상태 머신(reconciliation state machine)이 필요하다. 브로커 전달 보장은 문서화된 트랜잭션 경계에서 멈춘다.
- 순서 지정 범위(ordering scope)를 식별하라: 키, 파티션, 스트림 또는 전역. 불변 조건을 유지하는 가장 좁은 범위를 선택하라. 파티셔닝된 로그는 파티션 내에서 순서를 정렬하며, 다른 파티션에 있는 관련 이벤트를 자동으로 정렬하지는 않는다.
- 복제 및 장애 조치의 경우 선출(election), 펜싱(fencing), 확인 정책(acknowledgment policy) 및 복구(recovery)를 검사하라.
R + W > N은 고정된 멤버십 하에서 집합 교차를 제공하지만, 이것만으로 선형화 가능성(linearizability)을 확립하지는 않는다. 일관성을 참조하라. - 샤딩의 경우 트랜잭션 지역성, 핫 키, 리밸런싱 및 고유성 적용을 검증하라. 집계 처리량뿐만 아니라 가장 바쁜 샤드도 측정하라.
- 의심스러운 보장에 대한 가장 작은 반례와 이를 실행하는 테스트를 제시하라. 필수 보장과 허용 가능한 지연(staleness)을 분리하라.
검토 결과물
반환: 불변 조건 → 가정 → 실패 타임라인 → 위반된 보장 → 최소 수정 → 비용 → 검증. 이미 일관성이 깨진 데이터에 대한 복구 경로를 포함하라. 데이터베이스, 브로커 또는 공급자 버전에 의존하는 모든 동작을 명시하라.
권장 전 확인 사항
- 재시도된 요청이 변경된 매개변수로 안정적인 작업 ID를 재사용할 수 있는가?
- 리스(lease) 홀더가 리소스에 의해 적용되는 단조 증가 펜싱 토큰(monotonically increasing fencing token)을 필요로 하는가? 리스 만료만으로는 일시 중단된 프로세스를 막을 수 없다.
- 복제본이 성공적인 쓰기 직후에 오래된 값(stale values)을 제공할 수 있는가?
- 톰스톤(tombstones) 또는 중복 제거 레코드가 재생 시간 범위 전에 만료되는가?
- 다중 레코드 불변 조건이 트랜잭션 또는 명시적 조정이 필요한가?
범위 및 참고 자료
Designing Data-Intensive Applications 2판 (Kleppmann and Riccomini)에 맞춰 부분적으로 다룹니다. 이 원래 절차들은 공개 저자 강의 노트와 추가 1차 문서를 사용하며, 전체 책은 이용할 수 없었습니다. 정확한 출처는 sources를 읽으십시오. 이 스킬은 합의 구현체를 제공하거나 비잔틴 장애 허용(Byzantine fault tolerance)을 가정하지 않습니다.