CAP 정리 정확히 읽기: 네트워크 분할 때 일관성과 가용성의 선택

반응형

CAP 정리를 “일관성, 가용성, 분할내성 중 두 개만 고르는 법칙”으로 외우면 실제 설계에서 자주 틀린 결론에 도달한다. 핵심은 세 개의 기능을 평소에 임의로 두 개만 켜는 것이 아니다.

CAP가 말하는 경계는 네트워크 분할로 노드 사이의 메시지가 전달되지 않는 동안, 같은 데이터에 대해 일관성과 가용성을 동시에 보장할 수 없다는 것이다. 분할이 없을 때의 latency, 장애 복구, 데이터 모델, transaction 범위까지 설명하는 만능 분류표도 아니다.

C, A, P의 뜻부터 좁혀야 한다

CAP의 용어는 일상적인 뜻보다 강하고 구체적이다.

Consistency: atomic consistency, 즉 linearizability

모든 replica가 매 순간 물리적으로 같은 bytes를 가진다는 뜻으로만 설명하면 부족하다. 성공한 연산들이 하나의 전역 순서로 실행된 것처럼 보이고, 완료된 write 뒤의 read가 그보다 오래된 값을 반환하지 않는 보장에 가깝다.

write(x = 2) 성공 완료
        ↓
그 뒤 시작한 read(x)는 2 또는 더 새로운 값을 봐야 함

여기서 consistency는 ACID의 C나 eventual consistency라는 넓은 표현과도 구분해야 한다.

Availability: 정상 노드의 요청이 결국 응답한다

가용성은 “대부분 200 OK가 나온다”거나 SLA가 99.99%라는 통계적 표현이 아니다. CAP의 모델에서는 실패하지 않은 노드가 받은 모든 요청이 결국 응답해야 한다. 최신 값을 확인할 수 없다는 이유로 무기한 기다리는 것은 availability를 만족하지 못한다.

오류 응답을 무엇이든 반환하면 가용하다고 보는 식으로 단순화해서도 안 된다. 데이터 연산이 정한 응답 의미를 충족해야 한다.

Partition tolerance: 메시지 유실에도 모델이 계속 성립한다

노드 집합이 둘로 갈라져 서로의 메시지를 받지 못해도 시스템이 다룰 수 있어야 한다는 조건이다. 실제 분산 시스템에서 네트워크 단절 가능성을 설계로 없앨 수는 없다. 그래서 P는 보통 제품 옵션처럼 포기하는 항목이 아니라, 발생했을 때 어떤 응답을 할지 정해야 하는 환경 조건으로 보는 편이 정확하다.

두 replica가 갈라지면 왜 둘 다 얻을 수 없을까

초기 값이 x = 1인 두 replica A와 B를 생각해 보자.

client 1 → replica A     X     replica B ← client 2
                네트워크 분할

분할 중 client 1이 A에 x = 2를 쓰고 성공 응답을 받았다. 같은 시간에 client 2가 B에서 x를 읽는다. B는 A의 write를 알 수 없다.

  • B가 즉시 1을 반환하면 요청에는 응답했지만 최신 완료 write를 반영하지 못한다.
  • B가 A와 확인될 때까지 기다리거나 요청을 거절하면 단일 순서의 보장을 지킬 수 있지만 모든 요청에 정상 응답하지 못한다.

메시지가 전달되지 않는 동안 B가 2를 알아낼 방법은 없다. Gilbert와 Lynch의 Brewer's conjecture에 대한 논문은 비동기 네트워크 모델에서 availability와 atomic consistency를 함께 보장할 수 없음을 형식화했다.

CP와 AP는 제품 이름보다 분할 시 동작이다

분할이 실제로 발생했을 때 선택은 대략 두 방향으로 보인다.

분할 중 우선하는 보장 시스템의 행동 감수하는 것
일관성(C) quorum을 확보하지 못한 read·write를 거절하거나 기다림 일부 요청의 availability
가용성(A) 각 partition에서 요청을 받아 로컬 상태로 응답 stale read, conflict, 사후 병합 가능성

이를 CP와 AP라는 약어로 부를 수는 있지만, 데이터베이스 제품 하나에 영구 라벨을 붙이는 방식은 거칠다. 같은 제품도 다음 조건에 따라 보장이 달라질 수 있다.

  • read·write quorum 설정
  • 요청한 consistency level
  • leader와 follower의 위치
  • 동기·비동기 replication
  • key 또는 transaction 범위
  • timeout과 실패 감지 정책
  • conflict resolution 방식

“MongoDB는 CP, Cassandra는 AP”처럼 제품 이름만 외우면 실제 설정과 연산의 보장을 놓친다. 설계 문서에는 제품 분류 대신 특정 장애에서 특정 API가 무엇을 반환하는지 적는 편이 낫다.

CA는 왜 실무 분류로 조심해야 하나

CA를 “관계형 데이터베이스”의 동의어로 쓰는 설명도 흔하다. 단일 노드 데이터베이스는 노드 간 partition이라는 CAP 전제가 적용되지 않을 수 있다. 여러 노드로 복제한 관계형 데이터베이스라면 partition 중 어느 쪽에서 write를 받을지 다시 결정해야 한다.

즉 CA는 네트워크 분할을 다루지 않는 모델이나 범위에서는 말할 수 있지만, partition이 가능한 분산 배치에서 C와 A를 모두 지킨 채 P만 선택적으로 끄는 실용적 해법은 아니다.

CAP가 설명하지 않는 것

CAP만으로는 다음 질문에 답할 수 없다.

  • partition이 없을 때 read·write latency는 얼마인가?
  • replica lag은 평소에 어느 정도인가?
  • 데이터 유실 없이 복구하는 RPO·RTO는 얼마인가?
  • conflict를 자동 병합할 수 있는 데이터 타입인가?
  • 여러 key에 걸친 transaction을 지원하는가?
  • stale read가 허용되는 최대 시간은 얼마인가?
  • 장애 감지가 틀렸을 때 어떤 일이 생기는가?

Eric Brewer도 이후 CAP를 연속적인 설계 선택과 복구 관점에서 더 세밀하게 읽어야 한다고 설명했다. Google Research의 Spanner, TrueTime and the CAP Theorem은 높은 가용성과 강한 일관성을 제공하는 시스템도 실제 partition에서는 CAP의 정의와 모순되지 않음을 분석한다.

주문과 피드에 같은 결정을 쓰지 않는다

CAP의 선택은 서비스 전체보다 데이터와 연산별로 내리는 편이 현실적이다.

재고 차감과 결제 상태

중복 판매나 이중 처리를 막는 것이 중요하다면, 필요한 quorum이나 leader에 도달하지 못했을 때 write를 실패시키고 재시도하도록 설계할 수 있다. 이때 idempotency key, transaction 경계, 재시도 정책까지 함께 정의한다.

소셜 피드와 조회수

잠시 오래된 값을 보여 줘도 사용 흐름을 유지하는 편이 나을 수 있다. 대신 conflict가 발생했을 때 last-write-wins로 충분한지, 합산 가능한 자료형이 필요한지, 사용자에게 언제 일관된 값을 보여 줄지 정한다.

어느 쪽도 보편적으로 우월하지 않다. 잘못된 결제 한 건의 비용과 피드가 잠시 비는 비용이 다르기 때문이다.

Amazon Dynamo의 설계 맥락은 Dynamo 논문 독서 기록에서 이어서 볼 수 있다. 여기서도 “eventual”이라는 단어보다 실패 중 write 수용과 reconciliation 방식이 핵심이다.

설계 문서에 남길 질문

  1. partition의 단위는 region, AZ, node, process 중 무엇인가?
  2. 어떤 read와 write에 linearizability가 필요한가?
  3. quorum을 못 얻었을 때 timeout, 오류, stale response 중 무엇을 반환하는가?
  4. client는 재시도해도 안전한가? 중복 요청은 어떻게 제거하는가?
  5. 양쪽 partition에서 write를 받았다면 conflict를 어떻게 병합하는가?
  6. 복구 뒤 convergence를 어떻게 확인하는가?
  7. 정상 상태의 latency와 partition 상태의 availability를 각각 어떻게 측정하는가?

CAP 정리의 실용적인 문장은 “세 가지 중 둘”보다 다음에 가깝다.

통신이 끊긴 동안 최신 단일 순서를 지키려면 일부 요청을 멈춰야 하고, 모든 요청을 계속 받으려면 서로 다른 상태가 생길 가능성을 받아들여야 한다.

이 문장에서 출발하면 제품 이름을 분류하는 데 그치지 않고, 장애 시 사용자가 실제로 보게 될 동작을 설계할 수 있다.

참고 자료

반응형
KEEP READING
카테고리 전체 보기 →

댓글