LLM WikiAccess-protected knowledge portal
← 스터디 홈
160편 · 약 15분

FoundationDB: ACID 보장 분산 키-값 저장소와 결정론적 시뮬레이션 테스팅

요약

FoundationDB는 Apple iCloud와 Snowflake가 핵심 데이터 계층으로 사용하는 오픈소스 분산 키-값 저장소다. 완전한 ACID 트랜잭션과 선형화 가능(serializable) 격리를 제공하면서도, 트랜잭션 관리·스토리지·조율을 분리한 언번들드(unbundled) 아키텍처로 높은 수평 확장성을 달성한다. 무엇보다 독특한 점은 결정론적 시뮬레이션 테스팅—전체 클러스터를 단일 스레드 시뮬레이션으로 재현해 네트워크 장애, 디스크 오류, 타이밍 이벤트를 완전히 제어하에 테스트하는 기법—으로, 이 접근법이 FoundationDB 신뢰성의 핵심이다.


FoundationDB란 무엇인가

FoundationDB는 정렬된 키-값 쌍(ordered key-value pair)을 저장하는 분산 데이터베이스다. 2013년 출시, 2015년 Apple 인수, 2018년 오픈소스 공개의 이력을 갖는다. 핵심 설계 목표는 두 가지다.

첫째, 완전한 ACID 트랜잭션. 분산 환경에서 직렬화 가능한 격리 수준을 제공한다. 많은 NoSQL 데이터베이스가 성능을 위해 ACID를 완화하는 것과 달리, FoundationDB는 "코어는 완전한 트랜잭션성을 제공하고, 상위 계층이 그 위에 다양한 데이터 모델을 구현한다"는 방향을 선택했다.

둘째, 계층화된 아키텍처. FoundationDB 자체는 정렬 키-값 저장소만 제공한다. 문서, 관계형, 그래프 등 다양한 데이터 모델은 그 위에 쌓이는 레이어(layer)로 구현한다. Apple이 사용하는 레코드 레이어(Record Layer)나 Snowflake의 트랜잭션 레이어가 그 예다.


언번들드 아키텍처: 세 구성 요소의 분리

FoundationDB의 가장 특징적인 설계 결정은 일반적인 데이터베이스가 단일 프로세스로 묶는 기능들을 독립적으로 확장 가능한 세 역할로 분리한 것이다.

클라이언트 애플리케이션 트랜잭션 관리 시스템(TMS) Sequencer Proxy × N Resolver × N CommitProxy MVCC / OCC 분산 스토리지 Storage Server × N LSM-tree 기반 독립 확장 가능 복제 = Paxos 로그 읽기 ← 블로킹 없음 조율 시스템 Coordinators (Paxos) Flow 언어 (C++ 확장) 액터 모델 + async/await + 결정론적 시뮬레이션
FoundationDB 언번들드 아키텍처

트랜잭션 관리 시스템 (TMS)

TMS는 트랜잭션의 생명 주기를 전적으로 담당한다. Sequencer는 읽기 버전과 커밋 버전을 단조 증가하는 정수로 발급한다. Proxy는 클라이언트의 트랜잭션 시작·커밋 요청을 받아 Resolver에 위임한다. Resolver는 커밋 시 충돌 탐지를 수행한다. TMS는 상태를 메모리에만 유지하기 때문에 재시작이 빠르고, 장애 시 새로운 TMS가 스토리지와 로그를 통해 상태를 복구한다.

분산 스토리지

스토리지 서버는 키-값 데이터를 LSM-tree 구조로 저장하고, 쓰기는 분산 트랜잭션 로그(commit log)를 통해 복제된다. 읽기는 항상 로컬에서 처리되며 TMS에 요청을 보내지 않는다. 이것이 "읽기는 쓰기를 블로킹하지 않는다"는 MVCC의 핵심이다. 스토리지 서버를 늘리면 읽기 처리량이 선형에 가깝게 증가한다.

조율 시스템

Coordinators는 Paxos 프로토콜로 클러스터 멤버십, 현재 TMS 위치, 로그 서버 구성 등 클러스터 메타데이터를 유지한다. 메타데이터 변경이 드물기 때문에 소수의 Coordinator로도 충분하다.


Flow: 결정론적 동시성을 위한 언어

FoundationDB 내부 구현에서 가장 독창적인 요소는 Flow 언어다. Flow는 C++ 위에 async/await 문법과 액터(actor) 모델을 추가한 도메인 특화 언어(DSL)다.

액터 모델로 동시성 표현

Flow의 코드 단위는 액터다. 각 액터는 상태와 동작을 캡슐화하며, ACTOR 키워드로 선언한다. 액터 간 통신은 Promise와 Future 패턴으로 이루어진다. 비동기 호출은 wait()로 명시적으로 일시 중단점을 표시한다.

ACTOR Future<Void> myActor(Database db) {
    state Transaction tr(db);
    loop {
        tr.reset();
        Optional<Value> val = wait(tr.get(LiteralStringRef("key")));
        if (val.present()) {
            // 값 처리
            wait(tr.commit());
            return Void();
        }
    }
}

이 코드는 언뜻 일반 C++ 비동기 코드처럼 보이지만, Flow 컴파일러가 이를 상태 머신(state machine)으로 변환한다. 변환된 코드는 단일 스레드에서 실행될 수 있다.

결정론적 스케줄링

Flow의 진짜 힘은 런타임 스케줄러에 있다. 실제 배포 환경에서는 멀티스레드로 실행되지만, 시뮬레이션 모드에서는 단일 스레드에서 모든 액터를 결정론적 순서로 실행한다. 랜덤 시드를 고정하면 동일한 실행 순서가 보장된다.


결정론적 시뮬레이션 테스팅

FoundationDB의 신뢰성 비결은 여기에 있다. FoundationDB 팀은 "실제 분산 시스템에서 재현 불가능한 버그를 잡는 것은 매우 어렵다"는 문제를 정면으로 해결하기 위해 시뮬레이션 테스팅 인프라를 처음부터 설계에 포함시켰다.

시뮬레이션의 작동 방식

시뮬레이션 테스트는 실제 네트워크·디스크 대신 가상화된 네트워크와 가상화된 디스크를 사용한다. 모든 I/O, 타이머, 랜덤 이벤트가 시뮬레이터의 통제 하에 놓인다.

  • 네트워크 시뮬레이터: 패킷 손실, 지연, 재전송, 파티션을 정밀하게 주입한다.
  • 디스크 시뮬레이터: 쓰기 실패, 파일 시스템 오류, 지연된 fsync를 시뮬레이션한다.
  • 시간 시뮬레이터: 실제 시계 대신 가상 시계를 사용해 타임아웃을 수백 배 빠르게 경험할 수 있다.

단일 스레드에서 전체 클러스터가 실행되기 때문에 락(lock), 레이스 컨디션, 교착 상태가 없다. 버그가 발생하면 동일한 랜덤 시드로 재현이 보장된다.

실제 테스트 사례

FoundationDB는 하루에 수백만 번의 시뮬레이션 테스트를 실행한다. 각 테스트는 다음을 포함할 수 있다.

  • 클러스터 노드 임의 종료
  • 네트워크 파티션 발생
  • 디스크 I/O 지연 삽입
  • TMS(트랜잭션 관리자) 재시작
  • 스토리지 서버 데이터 손상

이 모든 시나리오가 단일 프로세스 안에서 수초 안에 완료된다. 실제 클러스터를 수십 분 돌려야 재현할 수 있는 장애가 시뮬레이터에서는 초 단위로 수천 번 반복된다.


MVCC와 낙관적 동시성 제어

FoundationDB의 트랜잭션 처리는 다중 버전 동시성 제어(MVCC)낙관적 동시성 제어(OCC)를 결합한다.

읽기: 트랜잭션 시작 시 읽기 버전(read version)이 할당된다. 해당 버전 기준으로 스냅샷을 읽는다. 다른 트랜잭션의 미커밋 쓰기는 보이지 않는다. 읽기는 락을 획득하지 않는다.

쓰기: 쓰기 연산은 클라이언트 측에서 로컬로 버퍼링된다. 커밋 시점에 Resolver가 읽기-쓰기 충돌을 탐지한다. 다른 트랜잭션이 같은 키를 이미 커밋했다면 현재 트랜잭션은 충돌로 중단(abort)된다.

5초 트랜잭션 제한: FoundationDB는 트랜잭션 시간을 5초로 제한한다. 이 제한은 버전 이력을 관리하는 비용과, 길게 지속되는 트랜잭션이 다른 트랜잭션에 미치는 영향을 제어하기 위한 의도적 설계 결정이다.


Apple iCloud와 Snowflake에서의 사용

Apple Record Layer

Apple은 FoundationDB 위에 Record Layer를 구축해 iCloud 사용자 데이터(주소록, 캘린더, 메모 등)를 저장한다. Record Layer는 Protobuf 스키마 기반 레코드, 인덱스, 쿼리 인터페이스를 제공한다. 수십억 사용자 규모에서 ACID 트랜잭션이 필요한 이유는 단순하다—사용자 데이터의 일관성은 타협할 수 없기 때문이다.

Snowflake 메타데이터 저장소

Snowflake는 쿼리 플랜, 데이터 카탈로그, 트랜잭션 메타데이터를 FoundationDB에 저장한다. Snowflake의 분산 트랜잭션 처리를 뒷받침하는 메타데이터 계층이다. 대규모 OLAP 워크로드의 메타데이터 일관성을 보장하는 데 FoundationDB의 직렬화 가능 격리가 핵심적 역할을 한다.


설계 트레이드오프

FoundationDB의 설계에는 명확한 트레이드오프가 있다.

5초 제한: 장기 실행 분석 쿼리를 단일 트랜잭션으로 처리할 수 없다. OLTP 워크로드에는 적합하지만, 대규모 배치 처리에는 애플리케이션 레벨의 분해가 필요하다.

단일 키스페이스: FoundationDB 코어는 정렬 키-값만 제공한다. 관계형, 문서, 그래프 등 다른 데이터 모델은 레이어로 구현해야 한다. 이는 유연성을 주지만, 레이어 구현의 복잡성을 개발자가 부담한다.

언번들드 운영: 컴포넌트별 독립 스케일링은 강점이지만, 클러스터 운영이 모놀리식 데이터베이스보다 복잡하다. TMS·스토리지·Coordinator를 각각 모니터링하고 조정해야 한다.


요점 정리

  • FoundationDB는 언번들드 아키텍처(TMS + 스토리지 + 조율)로 수평 확장성과 완전한 ACID 트랜잭션을 동시에 달성한다.
  • Flow 언어의 액터 모델과 결정론적 스케줄러가 분산 시스템을 단일 스레드 시뮬레이션으로 재현 가능하게 만들며, 이것이 FoundationDB 신뢰성의 핵심이다.
  • MVCC + OCC 조합으로 읽기는 쓰기를 블로킹하지 않고, 충돌 탐지는 커밋 시점에만 발생한다.
  • 5초 트랜잭션 제한은 의도적 설계 결정으로, 메타데이터와 OLTP 워크로드에 최적화됐다.
  • Apple iCloud와 Snowflake의 메타데이터 계층에서 수십억 사용자·쿼리 규모의 일관성을 뒷받침하고 있다.

References

  • FoundationDB SIGMOD 2023 논문: https://dl.acm.org/doi/pdf/10.1145/3592838
  • FoundationDB 공식 문서: https://apple.github.io/foundationdb/
  • FoundationDB GitHub 저장소: https://github.com/apple/foundationdb
  • Apple Record Layer (오픈소스): https://github.com/FoundationDB/fdb-record-layer
  • Snowflake의 FoundationDB 활용 블로그: https://www.snowflake.com/blog/how-foundationdb-powers-snowflake-metadata-forward/
  • Flow 언어 설계 문서: https://apple.github.io/foundationdb/flow.html