1P by GN⁺ | ★ favorite | 댓글 1개
  • 복식부기용 OLTP 데이터베이스 TigerBeetle은 안전성과 속도를 앞세우며, Jepsen은 0.16.11~0.16.30 계열을 3~6대 Debian 클러스터에서 장애 주입과 함께 검증함
  • 테스트는 명시적 타임스탬프 순서와 문서 기반 단일 스레드 상태 머신 모델을 결합해 Strong Serializability와 계정·전송·쿼리 의미론을 함께 확인함
  • 주요 안전성 버그는 다중 필터 쿼리 결과 누락과 Java 클라이언트 헤더 타임스탬프 오류였으며, 0.16.26 이상에서는 여러 장애 조합에서도 Strong Serializability 주장과 일치하는 결과가 관찰됨
  • 가용성 측면에서는 클라이언트 무한 재시도, 세션 eviction 시 프로세스 크래시, 단일 노드 장애 지연 급증, 디스크 비트플립·업그레이드 중 서버 panic, 단일 노드 디스크 손실 복구 경로 부재가 드러남
  • TigerBeetle 0.16.43은 보고된 이슈 대부분을 반영해 단일 노드 장애 지연 완화와 tigerbeetle recover를 포함하며, 운영자는 0.16.43 업그레이드와 0.16.26 이상 전환 시 릴리스 노트 확인이 필요함

TigerBeetle의 설계와 테스트 범위

  • TigerBeetle은 복식부기용 OLTP 데이터베이스로, 임의의 행·객체·그래프·blob 대신 계정(accounts)과 전송(transfers)만 저장함
  • Viewstamped Replication(VR)을 기반으로 Strong Serializable 일관성을 제공한다고 약속하며, 금융 거래·재고·티켓팅·유틸리티 계량 같은 모델에 맞춰 설계됨
  • 고경합·고처리량 워크로드에 맞춰 모든 쓰기를 primary VR 노드의 단일 코어로 통과시키며, scale-out보다 scale-up에 초점을 둠
    • 성능을 위해 배치 처리, I/O 병렬화, 고정 스키마, 고정 크기·캐시 정렬 데이터 구조를 사용함
  • 장애 모델은 메모리, 프로세스, 시계, 스토리지, 네트워크를 명시적으로 다룸
    • 프로세스는 멈추거나 크래시할 수 있음
    • 시계는 앞뒤로 점프할 수 있음
    • 디스크는 완전 실패뿐 아니라 부분 쓰기 손상과 데이터 오염도 겪을 수 있음
    • 네트워크는 지연, 드롭, 중복, 오배송, 메시지 손상을 일으킬 수 있음
  • TigerBeetle은 결정론적 시뮬레이션 테스트를 사용하며, VOPR 테스트는 클러스터 전체와 시계·디스크·네트워크 인터페이스를 시뮬레이션함

데이터 모델과 요청 의미론

  • 데이터 모델은 accountstransfers 두 종류의 레코드로 구성됨
    • 계정은 사용자 지정 128비트 id, ledger, flags, timestamp, code, user_data_32, user_data_64, user_data_128 등을 가짐
    • 전송은 debit_account_id, credit_account_id, amount, ledger, flags, 사용자 지정 필드 등을 포함하는 불변 레코드임
  • 전송은 단일 단계로 즉시 post될 수 있고, pending과 post/void로 나뉘는 2단계 전송도 가능함
    • pending transfer는 debit·credit 계정의 용량을 예약함
    • 이후 pending amount 이하를 post하거나 void할 수 있음
    • timeout 필드로 자동 만료를 제어함
  • 계정은 closed 플래그와 네 개의 잔액 필드를 제외하면 불변이고, 전송은 항상 불변임
    • 전송을 변경하거나 되돌리려면 보상 전송을 새로 만들어야 함
  • 요청은 단일 종류의 논리 연산을 나타내며, 보통 최대 8190개 이벤트 배치를 포함함
    • create_accounts, create_transfers는 쓰기 요청임
    • lookup_accounts, lookup_transfers, query_accounts, query_transfers, get_account_transfers, get_account_balances는 읽기 요청임
  • 각 요청은 데이터베이스 관점에서 하나의 트랜잭션이지만, 커밋된 요청 안의 일부 이벤트는 논리적으로 실패해 오류 코드를 반환할 수 있음
    • 이벤트 간 조건부 원자성이 필요하면 chain을 사용해 같은 chain 안의 이벤트가 모두 성공하거나 모두 실패하게 함

Jepsen 테스트 방식

  • Jepsen 테스트 스위트는 Jepsen testing library를 사용해 속성 기반 테스트와 장애 주입을 결합함
  • 테스트 대상은 TigerBeetle 0.16.11부터 0.16.30까지이며, 여러 개발 빌드도 포함됨
    • 클러스터는 3~6대 Debian 노드로 구성됨
    • LXC 컨테이너와 EC2 VM 양쪽에서 실행됨
  • TigerBeetle의 공식 클라이언트는 모든 노드에 연결하는 smart client라서 동시성 오류를 숨길 수 있음
    • Jepsen은 일반 smart-client 동작도 테스트함
    • 동시에 각 클라이언트를 단일 노드에 제한하는 방식도 사용함
  • 검증기는 두 단계로 동작함
    • 성공한 요청의 실행 타임스탬프를 읽고, 실패·타임아웃된 쓰기는 이후 관찰된 효과에서 타임스탬프를 추론함
    • 추론된 타임스탬프 순서대로 문서 기반 TigerBeetle 상태 머신 모델을 실행해 결과와 오류 코드를 검증함
  • 상태 머신 모델은 1,600줄 이상의 Clojure로 작성됐으며, 계정·전송 맵, 인덱스, transient error, 내부 통계, 시계 흐름 등을 포함함
    • 중복 ID, 비단조 타임스탬프, 잔액 제약, 호환되지 않는 플래그, chain의 speculative execution과 rollback 등을 처리함
    • 고성능 영속 자료구조 라이브러리 Bifurcan을 사용함

장애 주입과 파일 손상 테스트

  • Jepsen은 프로세스 SIGKILL, SIGSTOP, 여러 형태의 네트워크 파티션, 밀리초부터 수백 초까지의 시계 변경, 빠른 시계 왕복 변경을 주입함
  • 테스트 중 여러 버전으로 노드 업그레이드도 수행함
  • 새 파일 손상 nemesis로 다양한 스토리지 장애를 만들었음
    • 랜덤 비트 flip으로 우주선 간섭 같은 손상을 시뮬레이션함
    • 파일 청크를 다른 청크로 대체해 misdirected write를 시뮬레이션함
    • 파일 청크 스냅샷을 나중에 복원해 lost write를 시뮬레이션함
  • TigerBeetle 노드는 단일 데이터 파일을 가지며, 파일은 예측 가능한 offset의 zone으로 나뉨
    • WAL header, superblock zone의 중복 copy 등 특정 zone만 손상시키는 테스트를 수행함
    • 여러 zone이나 전체 파일을 손상시키는 테스트도 포함됨
  • “helical” disk fault는 모든 노드의 파일을 손상시키되 각 노드마다 다른 청크를 손상시키는 방식임
    • TigerBeetle의 최신 replica 파일 배치가 일반적으로 bit-for-bit 동일하기 때문에, 단일 레코드가 모든 replica에서 복구 불가능하게 손상되는 상황을 피하려는 목적임
    • WAL의 head는 노드별 위치가 다를 수 있어 예외가 됨

발견된 안전성 문제

  • 0.16.13에서 query_accounts, query_transfers, get_account_transfers 응답이 일부 또는 전체 결과를 누락하는 문제가 자주 발생함
    • 누락된 결과는 항상 응답 끝부분이었고, 응답은 올바른 결과의 prefix였음
    • 단일 필터 쿼리에서는 나타나지 않았고, ledgercode 같은 다중 필터 조합에서 발생함
    • 원인은 여러 인덱스 간 zig-zag merge join의 bounds check 버그였음
    • #2544로 추적됐고 0.16.17에서 수정됨
  • 0.16.13에 Jepsen 테스트 지원용으로 추가된 Java 클라이언트 헤더 API가 잘못되거나 중복된 실행 타임스탬프를 반환함
    • 원인은 Java 클라이언트의 mutable singleton 응답 객체 Batch.EMPTY였음
    • 성공 응답이 빈 batch로 표현되면서 여러 응답이 같은 객체의 header를 덮어씀
    • #2495로 수정됐고 0.16.14에 반영됨
    • 실제 데이터 정합성에는 영향을 주지 않고 Java 클라이언트 header API의 요청 타임스탬프에만 영향을 줌
  • 0.16.26 이상에서 관찰된 결과는 TigerBeetle의 Strong Serializability 주장과 일치함
    • 프로세스 pause, crash, 네트워크 partition, clock error, disk corruption, upgrade 조합에서도 해당 속성이 유지됨

클라이언트와 요청 처리 문제

  • TigerBeetle 문서는 요청이 timeout되지 않고 클라이언트가 응답을 받을 때까지 계속 retry한다고 설명함
    • Java 비동기 메서드는 CompletableFuture를 반환하며 .get(timeout, timeUnit)이나 .orTimeout(...) 같은 timeout API를 사용할 수 있음
    • .NET 클라이언트의 Task도 timeout 기반 Wait()를 제공함
  • 무한 retry는 definite error와 indefinite error를 모두 감출 수 있음
    • 예를 들어 TCP 연결이 ECONNREFUSED로 실패하면 해당 원 요청은 실행될 수 없다는 definite failure임
    • 하지만 클라이언트가 이를 호출자에게 알리지 않고 내부 retry만 계속하면, 호출자 관점에서는 timeout이나 중단 같은 indefinite failure가 됨
  • 이 문제는 #206에서 논의 중이며, 보고서 기준으로 unresolved임
    • Jepsen은 definite error와 indefinite error를 1급으로 표현하고 호출자에게 반환할 것을 권고함
    • 자동 retry는 유지할 수 있지만 설정 가능해야 하며, 연결 개시와 응답 대기 최대 시간을 옵션으로 둘 것을 권고함
  • 0.16.11 Java 클라이언트는 timeout 처리를 위해 동기 호출 스레드를 interrupt하거나 비동기 호출 후 client를 close할 때 JVM 전체가 segfault되는 문제가 있었음
    • 원인은 request data structure의 unset field였음
    • 클라이언트가 request 생성과 제출 사이에 닫히면 Zig 기본값인 0xaaa... 주소를 역참조함
    • #2435로 수정됐고 0.16.12에 반영됨
  • 공식 클라이언트는 서버가 session eviction을 알릴 때 프로세스 전체를 crash시켰음
    • TigerBeetle은 기본적으로 concurrent session을 64개로 제한함
    • 서버보다 새로운 클라이언트 버전을 사용할 때도 eviction이 발생함
    • #2484 이후 0.16.13부터는 eviction 시 프로세스 crash 대신 호출자에게 error를 반환함

단일 노드 장애 시 지연 급증

  • 단일 노드 장애에서 client latency가 3~5자릿수까지 증가하는 사례가 반복됨
    • 5노드 클러스터에서 단일 노드를 죽이면 minimum latency가 1ms 미만에서 10초로 증가함
    • 3노드 클러스터에서 한 노드를 죽인 테스트에서는 1~50ms였던 latency가 요청당 약 100초까지 증가하고, 노드 재시작 전까지 거의 1000초 동안 지속됨
  • 원인은 TigerBeetle의 prepare 전파 방식과 관련됨
    • 전통적인 VR은 primary가 모든 secondary에 prepare를 보내고 ack를 직접 받음
    • TigerBeetle은 노드를 ring으로 배치하고 primary가 다음 secondary에 prepare를 보내면, 각 secondary가 다음 노드로 전달함
    • 이 방식은 노드 하나의 bandwidth 요구를 줄이지만, ring의 다음 f개 replica 중 하나가 실패하면 commit이 막힐 수 있음
  • 이 문제는 #2739로 추적됨
  • 0.16.30은 prepare 메시지 절반을 ring 반대 방향으로 보내는 방식으로 완화함
    • 일부 prepare가 장애 노드를 우회할 수 있음
    • Jepsen 테스트에서 100초대 latency가 1~30초 수준으로 줄어듦
  • 0.16.43은 추가 성능 개선을 포함함
    • 노드가 ring 양방향으로 replicate함
    • ring topology가 동적으로 바뀌며, 클러스터가 네트워크 조건과 장애에 따라 노드 순서를 조정함

디스크 손상과 서버 crash

  • 0.16.20에서 superblock, WAL, grid zone의 단일 비트 손상이 startup crash를 유발하는 경우가 있었음
    • 로그는 panic: reached unreachable code를 출력하고 종료됨
    • 원인은 sector padding 검사 버그였음
  • TigerBeetle checksum은 청크의 데이터는 덮지만 padding은 제외함
    • padding의 0 비트가 1로 바뀌면 checksum은 통과함
    • 이후 padding이 여전히 0인지 확인하는 assertion이 실패해 서버가 crash함
    • padding 손상은 safety를 해치지 않으며, 다시 0으로 만들거나 다른 replica에서 복구할 수 있음
  • VOPR은 기존에 sector 전체를 손상시켰기 때문에 이 버그를 찾지 못함
    • sector 손상은 checksum 실패와 repair 경로를 유발했고, padding assertion까지 도달하지 않았음
    • TigerBeetle은 #2681에서 VOPR에 single-byte error를 추가함
    • 0.16.26부터 padding 손상 sector를 crash 대신 repair함
  • superblock copy number 비트플립도 같은 panic을 일으킬 수 있었음
    • superblock 네 copy는 각기 다른 2바이트 copy 번호를 가지며, checksum은 이 번호를 건너뜀
    • 디스크에서 손상된 copy number가 메모리로 읽힌 뒤 write 시 0~3 범위 assertion을 실패시킴
    • 0.16.26에서 copy number를 reset하는 방식으로 해결됨

업그레이드 관련 문제

  • 0.16.25 이하에서 0.16.26 이상으로 업그레이드할 때 panic: checkpoint diverged crash가 반복 관찰됨
    • 원인은 0.16.26의 CheckpointState 구조 변경이었음
    • 새 버전은 released blocks 집합을 포함했지만, 이전 버전 호환 상태 전송 과정에서 이 정보가 비어 있을 수 있었음
    • 이후 노드가 0.16.26으로 재시작하면 다른 replica가 아는 released blocks를 잃은 상태가 될 수 있음
    • assertion이 divergence를 감지하고 crash해 클라이언트가 불일치 데이터를 관찰하지 못하게 함
  • 이 문제는 #2745로 changelog에 문서화됨
    • TigerBeetle은 패치된 0.16.26을 릴리스하지 않음
    • 운영자는 0.16.26 이상으로 업그레이드하기 전 client를 멈추고 replica catch-up을 기다려야 함
  • 0.16.16에서 0.16.28로 여러 upgrade를 약 20초 안에 연속 수행하거나 upgrade 중 노드가 pause/crash될 때 release_transition assertion failure가 발생함
    • 실행 중인 노드는 새 binary를 memfd로 열고 exec()로 바꾸지만, 그 사이 디스크의 binary가 더 새 버전으로 교체될 수 있음
    • 코드가 디스크의 version header까지 현재 실행 버전과 같다고 assert하면서 실패함
    • #2758로 0.16.29에서 assertion을 warning으로 바꿈
  • 0.16.26에서 0.16.27로 업그레이드할 때 deprecated message type 때문에 panic: switch on corrupt value가 발생함
    • 새 노드의 switch문에 오래된 메시지 타입 case가 없어 crash함
    • #2763로 0.16.29에서 deprecated message type을 다시 case에 넣고 무시하도록 수정함

단일 노드 디스크 손실 복구

  • TigerBeetle은 파일 손상에 강하지만, 디스크 실패·화재·EBS volume error·운영자 실수 등으로 노드의 데이터 파일 전체가 없어지거나 복구 불가능하게 손상될 수 있음
  • 보고서 시점의 문서에는 실패 노드 교체 방법이 없었고, undocumented recovery 절차로 tigerbeetle format을 실행해 빈 데이터 파일로 초기화한 뒤 repair를 기대하는 방식이 있었음
  • Jepsen은 reformat이 대부분 동작하지만 안전하지 않을 수 있음을 확인함
    • 3노드 중 2노드에 committed operation op가 있고 그중 하나를 reformat하면, op를 관찰하지 못한 2/3 majority가 view change를 수행해 operation이 손실될 수 있음
    • 실제 테스트에서 acknowledged transfer 5개가 손실된 run이 있었음
    • upgrade 중 newer binary로 format된 노드가 cluster version transition을 완료하기 전 startup crash하는 사례도 있었음
  • 이 문제는 #2767로 추적됨
  • 이후 TigerBeetle 0.16.43은 catastrophic data loss를 겪은 노드를 복구하는 tigerbeetle recover 명령을 포함함

Jepsen의 결론과 권고

  • 발견된 안전성 문제는 두 가지임
    • 0.16.17 이전의 다중 필터 쿼리 결과 누락
    • Jepsen 테스트용 Java 클라이언트 디버깅 API의 잘못된·중복 타임스탬프
  • crash 문제는 총 7개였음
    • Java 클라이언트 2개: uninitialized memory access, eviction 시 프로세스 crash
    • 서버 5개: 디스크 손상 관련 panic 2개, 업그레이드 관련 panic 3개
    • #2745는 문서화됐고, 나머지 crash는 0.16.29까지 해결됨
  • 0.16.43은 보고서의 이슈 중 하나를 제외한 나머지를 해결함
    • unresolved 항목은 client request가 설계상 계속 retry되는 문제임
  • 사용자 권고는 명확함
    • 0.16.43으로 업그레이드할 것
    • 0.16.26으로 또는 그 이후 버전으로 넘어갈 때 release note를 확인할 것
    • 테스트 환경에서 단일 노드 장애를 시뮬레이션하고 애플리케이션이 상승한 latency에 어떻게 반응하는지 측정할 것
  • TigerBeetle의 아키텍처는 sound해 보이며, VR·flexible quorum·protocol-aware recovery 통합이 Strong Serializability 핵심 불변식을 훼손하지 않은 것으로 관찰됨
  • 다만 Jepsen 검증은 실험적 접근이므로, 버그의 존재는 증명할 수 있지만 부재는 증명할 수 없음

댓글과 토론

Hacker News 의견들
  • 함께 볼 만한 글: Fuzzer Blind Spots (Meet Jepsen!) – https://tigerbeetle.com/blog/2025-06-06-fuzzer-blind-spots-m...

  • 이 보고서가 정말 인상적임. TigerBeetle의 신뢰성과 확장성 주장을 볼 때마다 “좋아, Jepsen 보고서를 기다려보자”라고 생각했었음
    보고서에서 여러 이슈가 나왔고 우려할 수도 있지만, 단순히 고친 데서 끝나지 않고 비슷한 버그를 앞으로 잡도록 내부 테스트 묶음을 확장했다는 점이 긍정적임. 이런 엔지니어링 접근이라면 10년 뒤 TigerBeetle은 금융 애플리케이션 틈새에서 “그냥 Postgres 쓰면 됨” 수준의 기본 데이터베이스가 될 수 있을 듯함
    aphyr의 작업도 훌륭했고, 보고서를 읽으며 많이 배운 느낌임

    • TigerBeetle에는 6,000개 이상 assertion이 있고, 일부는 너무 빡빡해서 크래시가 났지만 그 assertion들이 제 역할을 해서 mental model을 조정해야 한다는 신호를 줬고 실제로 조정했음
      그 외에는 Jepsen 감사를 돕기 위해 Java 클라이언트에만 넣은 내부 테스트 기능의 작은 정확성 버그를 제외하면, Jepsen이 찾은 정확성 버그는 하나뿐이었고 내구성에는 영향을 주지 않았음. 관련 글은 여기 있음: https://tigerbeetle.com/blog/2025-06-06-fuzzer-blind-spots-m...
      공정하게 말하면 TigerBeetle은 Postgres보다 더 많은 장애를 견디도록 설계되고 테스트됨. 명시적인 저장소 장애 모델을 두고, Postgres가 1996년에 나왔을 때는 없던 연구를 활용했기 때문임. TB의 장애 모델은 결정적 시뮬레이션 테스트로 추가 검증되며, NASA의 Safety-Critical Code용 Power of Ten Rules에 따른 정적 메모리 할당 같은 기법도 사용함. 문헌상 Postgres가 데이터를 잃는 알려진 시나리오가 있지만 TigerBeetle은 이를 감지하고 복구할 수 있음
      더 보려면 Kyle 보고서의 helical fault injection 섹션을 보면 됨. 대부분의 Raft와 Paxos 구현은 이를 견디도록 설계되지 않았고, QCon London 발표도 있음: https://m.youtube.com/watch?v=_jfOk4L7CiY
    • Kyle의 글은 항상 기대됨. 새 글이 나올 때마다 분산 시스템 지식이 한 단계 올라가는 느낌임
  • aphyr가 검증한 결과 TigerBeetle이 자기 주장에 부합하는 모습을 보여줘서 정말 반가움. 올바른 접근을 택하면 올바른 결과가 나온다는 걸 보는 건 좋음
    TigerBeetle을 실제로 어떻게 쓰게 되는지 궁금함. Account나 Transfer가 아닌 모든 것을 위해 TigerBeetle 설치 주변에 외부 시스템과 다른 데이터베이스가 많이 있을 텐데, 그런 덜 신뢰할 수 있는 시스템들이 TigerBeetle과 맞춰 가는 전형적인 패턴은 무엇인지, 특히 둘 사이의 일관성 문제가 생겼을 때 어떻게 복구하는지 궁금함

    • TigerBeetle을 통합할 때의 전형적인 패턴은 제어 평면데이터 평면을 구분하는 것임. 일반 목적 또는 OLGP에는 Postgres를 쓰고, 트랜잭션 처리 또는 OLTP에는 TigerBeetle을 씀
      사용자 정보(이름, 주소, 비밀번호 등)와 상품 정보(설명, 가격 등)는 “서류함”으로서 OLGP에 넣음
      그리고 블랙프라이데이에 사용자가 상품을 재고 계정에서 장바구니 계정으로, 다시 결제와 배송 계정으로 이동시키는 모든 트랜잭션은 “금고”로서 OLTP에 넣음. TigerBeetle은 계정이나 이체마다 최대 3개의 사용자 데이터 식별자를 저장하게 해 주므로, 엔티티 사이의 이벤트를 해당 엔티티를 설명하는 OLGP 데이터베이스와 연결할 수 있음
      이 아키텍처 [1]는 깔끔한 관심사 분리를 제공해서 서로 다른 작업부하를 독립적으로 확장·관리할 수 있게 함. 은행이라면 고객 기록이 들어 있는 서류함에 모든 현금을 보관하기보다는, 성능·규제 준수·보존 특성이 다른 현금은 금고에 보관하는 편이 타당함
      이 패턴이 맞는 이유는 사용자가 이름이나 이메일 주소를 바꾸는 빈도(OLGP)가 거래하는 빈도(OLTP)보다 훨씬 낮기 때문임
      일관성을 보존하려면 쓰기 경로에서 TigerBeetle을 OLTP 데이터 평면이자 “기록의 원천”으로 다룸. “장바구니로 이동”이나 “결제” 트랜잭션이 들어오면 먼저 필요한 데이터 의존성을 OLGP에 쓰고, 관련 blob 데이터가 있다면 S3 같은 곳에도 쓴 다음, 마지막으로 TigerBeetle에 써서 트랜잭션을 커밋함. 읽기 경로에서는 기록의 원천을 먼저 조회해 엄격한 직렬 가능성을 보존함
      [1] https://docs.tigerbeetle.com/coding/system-architecture/
  • TigerBeetle의 퍼저 사각지대 글을 읽고 나서 보니 특히 재미있는 Jepsen 보고서
    JNI 쪽 세그폴트는 Rust나 다른 메모리 안전 언어를 썼어도 막지 못했을 것처럼 보임. 메모리 안전성 버그가 거의 없다는 점은 TigerBeetle의 Zig 프로그래밍 접근법, 기억하기로 TigerStyle이 의도한 역할을 꽤 잘 해낸다는 증거로 보임

    • https://news.ycombinator.com/item?id=44201189 참고. Rust였다면 살렸을 버그가 하나 있긴 했음. 대신 assertion이 살려줘서 베이컨이 살짝 바삭해졌을 뿐 타지는 않았음
      그래도 맞음. TigerStyle이 아니었다면 nasal demons에게 당했을 것임
  • 훌륭하게 상세한 보고서가 마음에 듦. Jepsen이 테스트하고 서명했다는 건 TigerBeetle에 엄청난 보증임. 아직 v1.0에도 도달하지 않았는데 앞으로의 새 이정표가 기대됨
    이 스레드에서 좋은 통찰을 공유하는 창업자들에게도 특별히 박수를 보냄

    • Kyle은 놀라운 일을 했고, 보고서에 담긴 디테일도 정말 좋았음. 읽는 내내 “이건 예술 작품 같다”고 생각했을 정도로 장인정신과 정밀함이 느껴졌음
      곧 Amsterdam의 SD25 발표에서도 새로운 내용을 공유할 예정이라 기대됨
  • “Panic! At the Disk 0”라는 섹션 제목이 작게나마 마음에 듦

  • 테스트 대상 분산 시스템이 실제로 일이 발생한 시간과 순서를 보고해야, 벽시계 시간을 쓰는 대신 시스템의 외부 모델과 정확히 검증할 수 있다는 점이 뒤늦게 보면 당연하지만 흥미로움

    • 이게 동작하는 건 엄격한 직렬 가능성이 있기 때문임. 더 약한 일관성 보장에서는 반드시 하나의 전역 일관 타임라인이 존재하지 않음
      더 어려운 일을 해내면 오히려 시스템이 단순해지는 흥미로운 메타 패턴임
      또 다른 예로, 디스크가 실패할 수 있다고 가정하고 복구 프로토콜을 포함해야 하므로, 뒤처진 복제본의 상태 동기화를 사실상 “공짜로” 얻게 됨. 전체 디스크가 손상된 상황과 정확히 같은 문제이기 때문임
    • 이건 고전적인 접근이라고 봄. 예: https://lamport.azurewebsites.net/pubs/time-clocks.pdf
  • 글에서 “Viewstamped Replication” 논문으로 가는 링크가 아쉽게도 깨져 있음. https://pmg.csail.mit.edu/papers/vr-revisited.pdf는 연결이 거부됨
    아마 http://pmg.csail.mit.edu/papers/vr-revisited.pdf처럼 https가 아니라 http 스킴이어야 하는 듯함
    이제 금요일 저녁에 읽을거리가 생겼음

    • 곧 고쳐질 예정임
      VSR 2012 논문은 가장 좋아하는 논문 중 하나이고, “Protocol-Aware Recovery for Consensus-Based Storage”도 정말 강력함
      즐겁게 읽길 바람
  • 순수하게 배우고 싶어서 묻는 질문이고 오해되지 않았으면 함. 분산 시스템은 새로 배우는 중이고 결정적 시뮬레이션 테스트에 매료되어 있음
    TigerBeetle에 대한 Jepsen 보고서, 관련 블로그 글, GitHub 워크플로의 Antithesis 통합 코드를 간단히 보고 나서 테스트 범위를 더 잘 이해하고 싶어졌음
    핵심 질문은 Jepsen 테스트 묶음이 찾아낸 이 버그들을 Antithesis 통합도 찾아낼 수 있었느냐는 것임
    몇 가지 가정에서 나온 질문인데 틀렸을 수 있음. TigerBeetle은 이미 내부 테스트 묶음과 Antithesis 제품으로 포괄적으로 테스트되고 있다고 생각했고, Antithesis 테스트 묶음이 Jepsen보다 더 강력하다고 이해해서 Jepsen이 Antithesis가 못 찾은 문제를 발견한 게 의외였음
    이해가 잘못된 것인지 궁금함. 예를 들어 1) Antithesis 테스트 묶음이 이 특정 버그 부류를 감지할 수 없었는지, 2) 시스템의 이 부분이 아직 Antithesis 테스트로 커버되지 않았는지, 3) Jepsen과 Antithesis 테스트 묶음의 서로 다른 강점과 목표를 오해해서 사과와 오렌지를 비교하고 있는지 알고 싶음

    • TigerBeetle 블로그 글에 더 자세히 나오지만, 간단히 말하면 Antithesis에서 돌던 테스트는 상당히 철저했음에도 교차하는 쿼리순서가 뒤바뀐 값의 정확한 조합을 만들지 못했고, Jepsen 생성기가 그 조합을 맞혔음
      Jepsen 테스트 생성기에도 거의 확실히 사각지대가 있음. 그래서 서로 다른 생성기를 설계하는 게 도움이 되는 이유임
    • 분산 시스템의 생성 테스트에는 보통 세 가지 구성요소가 필요함. 첫째, 시스템을 실행할 환경이 필요함. 가장 단순하게는 실제 머신 클러스터를 띄우는 것이지만, 성능, 외부 API 응답 제어, 결정성, 재현성을 높이려면 더 정교한 것이 좋음. 둘째, 환경 속 시스템이 흥미로운 일을 하도록 만드는 부하 생성기가 필요함. 셋째, 부하가 걸린 시스템의 동작을 관찰하고 명세에 맞는지 판단하는 감사기가 필요함
      Antithesis는 주로 1번 문제를 다루며, 가상 머신으로 결정적 시뮬레이션 환경을 제공함. 같은 문제를 Jepsen은 실제 머신을 쓰되 운영체제 수준에서 장애를 주입하는 방식으로 다루고, TigerBeetle의 자체 VOPR은 데이터베이스와 함께 설계되어 전체 클러스터를 단일 스레드에서 실행할 수 있음. 이 세 접근은 상호보완적이고 각자 잘하는 영역이 다름
      이 버그에서 결정적이었던 부분은 2번과 3번, 즉 버그를 실제로 유발할 수 있는 작업부하 검증기와 감사기를 작성하는 것이었음. 여기서는 aphyr가 작성한 TigerBeetle 전용 Clojure 코드 1,600줄이 버그를 유발하고 감지했으며, 이후 TigerBeetle 쪽의 동등한 테스트도 이를 유발하도록 패치했음. 사실 여기서 버그가 있는 건 데이터베이스라기보다 VOPR임. 데이터베이스에 버그가 있는 건 당연한 일이고 의지만으로 버그를 피할 수는 없음. 그래서 대부분의 버그를 유발할 수 있는 테스트 전략이 필요하고, 빠져나간 버그는 작업부하 생성기의 결함을 가리킴
    • 결정적 시뮬레이션 테스트의 90%는 주로 TigerBeetle이 자체 제작한 결정적 시뮬레이터 VOPR가 수행함. 전용 CPU 코어 1,000개 규모에서 24/7로 돌고 있음
      Antithesis도 사용하지만, 결정적 시뮬레이션 테스트의 두 번째 층으로 씀
      쿼리 엔진 버그가 왜 빠져나갔는지는 여기 참고: https://tigerbeetle.com/blog/2025-06-06-fuzzer-blind-spots-m...
  • 대형 은행이나 증권거래소가 TigerBeetle을 쓰는지 궁금함

    • 국가 단위로는 Gates Foundation과 함께 TigerBeetle을 비영리 중앙은행 스위치에 통합하고 있으며, 이 시스템은 올해 말 Rwanda의 National Digital Payments System 2.0을 구동할 예정임 [1]
      기업 단위로는 TigerBeetle이 이미 월 1억 건 이상 트랜잭션을 처리하는 고객들의 프로덕션에서 사용되고 있고, 최근 유럽의 20억 달러 규모 핀테크 유니콘과 첫 계약을 맺었으며 미국에서도 몇 건이 곧 마무리될 예정임. 전 세계적으로 실시간 트랜잭션 처리로 이동하는 흐름 [2] 때문에 더 높은 성능을 위해 TigerBeetle로 옮기려는 회사들의 관심이 꽤 많음
      질문에 답하자면, Wall Street의 꽤 큰 브로커리지인 Clear Street의 일부 창업자들이 TigerBeetle에 투자 [3]했음
      [1] https://mojaloop.io/how-mojaloop-enables-rndps-2-0-ekash/
      [2] https://tigerbeetle.com/blog/2024-07-23-rediscovering-transa...
      [3] https://tigerbeetle.com/company
    • 은행이나 거래소는 아니지만, 아주 큰 핀테크 회사에서 일하고 있고 신규 제품에 TigerBeetle을 쓰고 있음
    • 그런 고객이 있었다면 홈페이지에서 자랑했을 것 같음. 지금까지 홈페이지에서 가장 큰 보증은 어떤 YouTuber에게서 나온 것임. 인기 YouTuber인 건 맞지만 그래도 YouTuber임