"Amazon's Dynamo"를 읽고 나서 - 1. Vector Clock
1. 2007년 글에서 얻어올 수 있는 교훈
2007년 당시 아마존 시스템 내의 분산 저장소 설계에 대한 베르너 보겔스 (Werner Vogels) 의 Dynamo 논문과 글은 지금도 학습 가치가 크다고 생각해서 본 글을 쓴다. 핵심 아이디어(Consistent Hashing, Vector clocks, Sloppy Quorum 등)을 정리해보는 목적이 있다.
아마존은 전 세계 여러 데이터센터에 위치한 수만 대의 서버를 사용해 피크 시간대에 수천만 명의 고객을 서비스한다. 아마존 플랫폼은 성능, 신뢰성, 효율성 측면에서 엄격한 운영 요건을 가지며, 지속적인 성장을 지원하기 위해서는 매우 높은 확장성이 필요하다. 신뢰성은 가장 중요한 요구사항 중 하나인데, 아주 짧은 서비스 중단조차도 막대한 재정적 손실과 고객 신뢰도 하락으로 이어지기 때문이다. 또한 지속적인 성장을 지원하려면 플랫폼이 높은 확장성을 가져야 한다.
아마존이 플랫폼을 운영하며 얻은 교훈 중 하나는 시스템의 신뢰성과 확장성이 애플리케이션 상태(state)를 어떻게 관리하느냐에 달려 있다는 점이다.
아마존은 수백 개의 서비스로 구성된 고도로 분산되고, 느슨하게 결합된 서비스 지향 아키텍처(SOA)를 사용한다. 이처럼 수많은 구성요소가 분산된 환경에서는 일부 서버나 네트워크가 항상 실패하더라도 전체 서비스가 중단되지 않아야 하며, 이를 위해 핵심 서비스가 의존하는 스토리지는 언제나 가용해야 한다. 아마존은 장애를 예외가 아니라 일상으로 받아들이고, 쇼핑 카트 같은 서비스가 언제든 읽기·쓰기를 보장받도록 설계했다.
Dynamo는 Primary key 기반 단순 인터페이스와 함께 Consistent Hashing, Vector Clocks, Quorum, Gossip 같은 기법을 결합해 고가용성과 확장성을 달성했으며, Eventual Consistency 저장소도 대규모 프로덕션 환경에서 충분히 활용될 수 있음을 보여주었다.
2. 분산 시스템의 트레이드오프
분산 시스템을 설계할 때 가장 어려운 점은 “모든 것을 다 가질 수 없다"는 현실을 받아들이는 것이다. 논문은 이러한 트레이드오프를 정면으로 다루며, 아마존이 어떻게 비즈니스 요구사항에 맞춰 균형점을 찾았는지 보여준다.
논문은 CAP 이론을 직접 언급하지 않지만, 전체 설계가 이를 중심으로 전개된다. 아마존은 네트워크 장애 상황에서 강한 일관성과 높은 가용성을 동시에 달성할 수 없다는 것을 인정하고, 가용성을 우선하는 명확한 선택을 했다.
이것이 단순한 이론적 선언이 아니라는 점이 중요하다. 아마존은 실제로 짧은 서비스 중단조차도 막대한 재정적 손실과 고객 신뢰도 하락으로 이어진다는 것을 경험했고, 이에 따라 가용성 중심의 설계를 선택했다.
“Always Writeable” - 쓰기를 절대 거부하지 않는 철학
Dynamo의 가장 독특한 설계 철학은 “always writeable” 데이터 스토어를 목표로 한다는 점이다. 왜 이것이 중요할까?
“For instance, the shopping cart service must allow customers to add and remove items from their shopping cart even amidst network and server failures. This requirement forces us to push the complexity of conflict resolution to the reads in order to ensure that writes are never rejected.”
쇼핑카트 예제가 이를 완벽하게 설명한다. 전통적인 데이터베이스라면 분산 락을 획득하려 시도하고, 실패하면 에러를 반환했을 것이다. 하지만 아마존은 고객의 구매 의도를 절대 놓치지 않는 것이 기술적 일관성보다 중요하다고 판단했다. 이를 위해 충돌 해결의 복잡성을 쓰기가 아닌 읽기 시점으로 미루는 대담한 결정을 내렸다. (“Eventual Consistency라는 실용적 접근을 택했다. 모든 업데이트가 결국에는 모든 레플리카에 도달한다는 보장이다.)
충돌 해결의 주체: 누가 결정할 것인가?
Dynamo 논문에서 중요한 기여 중 하나는 데이터 버전 관리 방식으로 Vector Clock을 채택한 점이다. 전통적인 데이터베이스는 단일 타임스탬프나 락을 이용해 업데이트의 순서를 강제하는 방식으로 일관성을 유지한다. 하지만 Dynamo가 직면한 상황은 훨씬 더 복잡했다. 수천 개 노드가 지리적으로 분산된 상태에서 동시에 쓰기가 발생할 수 있고, 네트워크 장애로 인해 잠시 서로 다른 업데이트가 독립적으로 진행될 수도 있었다. 단일 시계나 락으로는 이 환경을 감당할 수 없었던 것이다.
Vector Clock은 이러한 문제를 해결하기 위해 각 데이터 아이템에 “원인 관계(causality)“를 추적할 수 있는 버전 벡터를 부여한다. 이 벡터는 노드별 카운터의 집합으로, 어떤 업데이트가 다른 업데이트의 “후속”인지, 혹은 서로 독립적으로 발생한 “충돌”인지를 판단할 수 있도록 해준다.
위 그림은 Vector Clock의 핵심 아이디어를 시각화한 것이다. 파란 영역은 사건 B4에 도달하기까지의 원인들이며, 빨간 영역은 B4 이후에 발생한 결과들이다. 이런 방식으로 각 이벤트가 다른 이벤트의 조상(ancestor)인지, 혹은 서로 독립적으로 발생한 것인지가 구분된다. 따라서 어떤 업데이트가 단순한 순차적 연속인지, 아니면 병렬로 발생한 충돌인지 시스템이 스스로 판별할 수 있다.
예를 들어 A 노드와 B 노드가 각각 같은 키에 대해 동시에 다른 값을 썼다면, 두 Vector Clock은 공통 조상을 공유하지만 서로 다른 진화 경로를 가진다. Dynamo는 이 상황을 감지하여 두 버전을 모두 저장하고, 나중에 애플리케이션이 이를 병합하도록 위임한다.
즉, Vector Clock은 데이터 일관성을 “엄격하게 동기화”하는 대신 “원인 관계를 기록”하는 쪽을 택한 것이다. 이 선택은 높은 가용성과 쓰기 성능을 유지하는 대가로, 충돌 해결 책임을 시스템이 아닌 애플리케이션에 넘기는 아마존 특유의 실용주의적 접근을 잘 보여준다. 결국 Dynamo의 Vector Clock은 단순한 기술적 도구가 아니라, 분산 환경에서 정합성을 희생하더라도 언제나 응답하는 시스템을 만들기 위해 어떤 트레이드오프를 선택했는지를 상징적으로 드러내는 장치라 할 수 있다.
Dynamo의 또 다른 혁신적 선택은 충돌 해결을 애플리케이션에게 위임한 것이다. 데이터 스토어가 충돌을 해결한다면 “마지막 쓰기 우선” 같은 단순한 정책만 사용할 수 있다. 하지만 애플리케이션은 비즈니스 로직을 이해하고 있어 더 의미 있는 해결이 가능하다.
구체적으로 Dynamo는 충돌을 두 단계로 나누어 다룬다. 먼저 구문적 조정(Syntactic Reconciliation) 은 시스템 수준에서 자동으로 수행된다. Dynamo는 각 객체에 대해 Vector Clock을 유지하면서 버전 간의 인과 관계를 추적한다. 새로운 쓰기가 도착했을 때, 해당 버전이 기존 버전의 명확한 후손임이 벡터 클록 비교로 판별되면 이는 충돌이 아니라 단순한 연속 업데이트로 간주된다. 즉, 시스템은 이전 버전을 폐기하고 최신 상태만 남긴다. 이 과정은 전적으로 자동화되어 있고, 애플리케이션이 개입할 필요가 없다.
하지만 서로 독립적인 브랜치로 진화한 경우에는 의미론적 조정(Semantic Reconciliation) 단계가 필요하다. 이때는 시스템이 모든 충돌 버전을 애플리케이션에 반환하고, 최종 병합은 애플리케이션이 맡는다. 중요한 점은, 병합 로직이 서비스마다 다를 수 있다는 것이다.
이를 잘 보여주는 사례가 바로 쇼핑카트다. 사용자가 노트북에서 책 A를 카트에 추가한 직후, 모바일에서 (아직 동기화되지 않은 상태로) 책 B를 추가했다고 하자. 두 버전이 충돌했을 때 단순히 “마지막 쓰기 우선” 정책을 적용하면 책 B만 남게 되고, 사용자의 책 A 구매 의도는 사라진다. Dynamo의 방식은 다르다. 두 버전을 모두 보존하고 애플리케이션이 이를 병합하여 최종적으로 A와 B가 모두 카트에 남도록 한다. 삭제된 아이템이 다시 나타날 수 있다는 부작용이 있지만, 구매 의도를 잃는 것보다는 낫다는 비즈니스적 판단에 따른 것이다.
다른 서비스들은 각기 다른 전략을 택한다. 세션 관리 서비스는 최신성이 중요하기 때문에 타임스탬프 기반의 “최신 우선” 정책을 사용한다. 제품 카탈로그는 읽기 중심이라 충돌 자체가 드물다. 위시리스트는 쇼핑카트와 마찬가지로 합집합 병합을 채택한다. 이처럼 충돌 해결 방식을 중앙에서 일률적으로 정하지 않고, 서비스 특성에 맞게 애플리케이션이 선택할 수 있다는 점이 Dynamo의 가장 큰 유연성이었다.
결국 Dynamo의 Vector Clock과 충돌 해결 철학은 하나의 메시지로 요약할 수 있다. “완벽한 일관성 대신, 언제나 살아있는 시스템을 우선하라.” Dynamo는 스토리지가 단순한 데이터 저장소가 아니라, 분산 환경에서 가용성과 일관성 사이의 균형점을 어떻게 잡을 것인지 보여주는 실험장이었다. 그리고 그 철학은 오늘날 DynamoDB뿐 아니라, Cassandra, Riak 같은 수많은 분산 데이터베이스의 설계에 직접적인 영향을 주었다.
실제 운영 데이터가 이런 접근의 타당성을 증명한다
“During this period, 99.94% of requests saw exactly one version; 0.00057% of requests saw 2 versions; 0.00047% of requests saw 3 versions and 0.00009% of requests saw 4 versions.”
- 99.94%는 충돌 없음
- 0.06%만이 다중 버전 조회
- 4개 이상 버전은 극도로 희귀
흥미롭게도, 충돌 증가의 주원인은 장애가 아니라 동시 쓰기 증가였다. 특히 자동화된 봇이 대량 작업을 수행할 때 충돌이 늘어났다. 18년이 지난 지금, 이러한 트레이드오프는 여전히 유효하다. 오히려 마이크로서비스 아키텍처와 글로벌 분산 시스템이 보편화되면서 더욱 중요해졌다. 현대의 시스템들 - Cassandra, DynamoDB, Cosmos DB - 모두 비슷한 튜닝 가능한 일관성 모델을 제공한다. Dynamo가 보여준 “비즈니스 요구사항이 기술적 순수성보다 우선한다"는 교훈은 엔지니어링의 황금률이 되었다.
무엇보다 중요한 것은, Dynamo가 트레이드오프를 숨기지 않고 명시적으로 드러냈다는 점이다. 개발자에게 선택권을 주고, 그 선택의 결과를 명확히 이해할 수 있도록 했다. 이것이야말로 성숙한 분산 시스템 설계의 표본이라 할 수 있다.
다음 글에서 Consistent Hashing, Quorum 를 다룹니다.