世界を動かす技術を、日本語で。

Jepsen: TigerBeetle 0.16.11

概要

  • TigerBeetle は金融取引向けの分散OLTPデータベース
  • Jepsenによる検証で複数の障害・バグ発見、ほとんどが修正済み
  • 強力な 耐障害性一貫性保証 (Strong Serializability)を実現
  • データモデルは アカウントとトランスファー に特化
  • 高スループット・高競合環境に最適化

TigerBeetleの検証レポート

  • TigerBeetle は金融トランザクション向け分散OLTPデータベース
  • バージョン0.16.11〜0.16.30で 7件のクラッシュ(クライアント・サーバ) を発見
    • クライアント終了時のセグフォールト、サーバアップグレード時のpanic等
  • 単一ノード障害時、 レイテンシ上昇リクエスト無限リトライ を確認
  • 安全性問題は2件 のみ:複数条件クエリの結果欠落、デバッグAPIのタイムスタンプ誤り
  • ディスク破損耐性 は非常に高く、全レプリカのファイル損傷にも耐性
    • ただしノード全データ消失時の復旧手段は未提供
  • バージョン0.16.30時点で Strong Serializabilityを達成
  • 0.16.45で既知の問題は 無限リトライ以外全て修正済み
  • Jepsen倫理方針に則り、TigerBeetle, Inc.の資金提供で実施

TigerBeetleの設計概要

  • ダブルエントリー会計 向けに設計されたOLTPデータベース
  • Viewstamped Replication(VR) プロトコルを採用し強一貫性を実現
  • データモデル は「アカウント」と「トランスファー」のみ
  • 高競合・高スループット ワークロードに最適
    • 例:中央銀行スイッチ、証券取引所の大量決済
  • スケールアップ型設計 :全書き込みをプライマリVRノード1コアに集約
    • バッチ処理、IO並列化、固定スキーマ、ハードウェア最適化を徹底
  • 障害モデル: メモリ、プロセス、クロック、ストレージ、ネットワーク 障害を明示的にサポート
    • ECCメモリ前提、プロセス停止・クラッシュ、クロックジャンプ、ディスク破損、ネットワーク遅延・損失等
  • Viewstamped ReplicationProtocol-Aware Recovery 技術で耐障害性強化
    • チェックサム、マルチコピー書き込み、ランタイムアサートで障害検知

データ損失耐性とテスト

  • 単一レプリカに記録が残ればデータ消失しない 設計
  • 全レプリカで記録破損時は安全停止
  • 決定論的シミュレーションテスト を重視
    • VOPRテストでクラスタ全体の障害を再現
    • サブシステムごとのストレステストや統合テストも実施

アップグレード戦略

  • バイナリに複数バージョンのコードを同梱
    • 例:0.16.21は0.16.17〜0.16.21対応
  • バイナリ差し替えのみで自動アップグレード
    • クラスタ全体でバージョンを順次切り替え
    • コミット済み操作のバージョン整合性も担保

時刻管理

  • Viewstamped Replication で完全順序の論理クロックを実装
  • 物理時刻(POSIXタイム)を近似しつつ強い順序保証
    • 各レプリカのPOSIXタイムスタンプを収集し、クォーラム内で合意
    • 20秒以上の時刻ずれが60秒続くとリクエスト拒否
  • タイムスタンプ仕様は「UNIXエポックからのナノ秒」だが、実際はPOSIXタイムとの差異あり

データモデル詳細

  • アカウント :128bitユーザー定義ID、元帳、フラグ、作成時刻、ユーザー定義カスタムフィールド等
    • 残高フィールド(pending/postedのdebit/credit)を持つ
  • トランスファー :128bit ID、コード、元帳、フラグ、カスタムフィールド
    • debit_account_id、credit_account_id、転送額
    • 単一フェーズ即時反映、二段階(pending→posted)も可能
    • pendingトランスファーはvoidや自動expire可能
    • アカウント閉鎖もpendingトランスファーで実現、voidで再開可能
  • アカウントはほぼ不変 (閉鎖フラグと4種残高のみ例外)、トランスファーは完全不変
    • 変更や取消は補償トランスファーで実現

操作・API

  • リクエスト単位でトランザクション性 を保証
    • イベントはバッチ実行(最大8190件)、順次実行
    • 各イベントに単調増加タイムスタンプ
    • インタラクティブトランザクションや複数リクエスト横断トランザクションは非対応
  • Strong Serializability を保証
    • 各リクエストは「最大1回」実行、他リクエストと非インターリーブ
    • セッション安全性(自分の書き込みを自分で読める、書き込み順序の可視化)も担保
  • 書き込みリクエスト :create_accounts、create_transfers
  • 読み取りリクエスト :lookup_accounts、lookup_transfers、query_accounts、query_transfers、get_account_transfers、get_account_balances
  • リクエストは全体でアトミック (一部イベント失敗時は失敗分のみエラーコード返却)
    • チェーン機能でサブトランザクション表現も可能(全成功・全失敗保証)

Jepsenテスト設計

  • Jepsenテストライブラリ でプロパティベース+障害注入テストを実施
  • TigerBeetleの複数バージョンに対し、耐障害性・一貫性を徹底検証

(以降、論点や話題が変わる場合は新しい記事タイトルで続けてください)

Hackerたちの意見

このレポートにはすごく感心したよ。TigerBeetleの信頼性やスケーラビリティについての主張を読むたびに、「じゃあ、Jepsenレポートを待とう」と思ってたんだ。このレポートではいくつかの問題が見つかったけど、それはちょっと心配だね。でも、問題を直すだけじゃなくて、将来の似たようなバグを見つけるために内部のテストスイートを拡張したのはポジティブだと思う。こういうエンジニアリングのアプローチなら、10年後にはTigerBeetleが金融アプリケーションのニッチで「とりあえずPostgresを使えばいい」レベルに達してるんじゃないかな。あと、aphyrも素晴らしい仕事してるね!このレポートを読んで、たくさん学んだ気がする。

ありがとう!そうだね、TigerBeetleには約6,000以上のアサーションがあるよ。その中には厳しすぎるものもあって、いくつかのクラッシュの原因になったんだ。でも、それはアサーションがちゃんと機能して、私たちがメンタルモデルを調整する必要があることを教えてくれたから。そうじゃなければ、内部テスト機能に追加した小さな正確性のバグ(Javaクライアントにだけあって、Jepsenの監査を助けるためのもの)を除けば、Jepsenによって見つかった正確性のバグは1つだけで、それは耐久性には影響しなかったよ。詳しくはここに書いてある: https://tigerbeetle.com/blog/2025-06-06-fuzzer-blind-spots-m... 最後に、公平を期すために言うと、TigerBeetleは(そしてテストもされているけど)Postgresよりも多くの障害に耐えられるように設計されているんだ。これは、明示的なストレージ障害モデルを持っていて、Postgresがリリースされた1996年には利用できなかった研究を基にしているから。TBの障害モデルは決定論的シミュレーションテストでさらにテストされていて、NASAの安全クリティカルコードのための「Power of Ten Rules」に従った静的メモリアロケーションの技術を使っているよ。文献には、Postgresがデータを失う原因となる既知のシナリオがあるけど、TigerBeetleはそれを検出して回復できるんだ。詳しくは、Kyleのレポートのヘリカル障害注入のセクションや、QConロンドンでのトークを見てね: https://m.youtube.com/watch?v=_jfOk4L7CiY

Kyleのレポートを読むのはいつも楽しみだよ。彼が何か出すたびに、分散システムの知識がレベルアップする気がする。

こちらもどうぞ: Fuzzer Blind Spots (Meet Jepsen!) – https://tigerbeetle.com/blog/2025-06-06-fuzzer-blind-spots-m...

TigerBeetleがaphyrによって検証された主張を実現しているのを見るのは本当に嬉しいよ。正しいアプローチを取れば、正しい結果が得られるってことが分かるからね。TigerBeetleを使う人たちについての質問なんだけど、アカウントやトランスファー以外のすべてのものに対して、TigerBeetleのインストール周りには多くの外部システムや他のデータベースがあると思う。そういう信頼性の低いシステムがTigerBeetleとどうやって調和するのか、特に両者の間の整合性の問題から回復するための典型的なパターンは何なんだろう?

TigerBeetleのJoranです!ありがとう!レポートが公開されて本当に嬉しいです。TigerBeetleを統合する際の典型的なパターンは、制御プレーン(一般目的用のPostgresやOLGP)とデータプレーン(トランザクション処理用のTigerBeetleやOLTP)を区別することです。すべてのユーザー(名前、住所、パスワードなど)や製品(説明、価格など)は、OLGPに「ファイリングキャビネット」として入ります。そして、これらのユーザー(またはエンティティ)が行うブラックフライデーのトランザクションは、在庫アカウントからショッピングカートアカウントへ、そこからチェックアウトや配送アカウントへ移動するすべてが、OLTPに「銀行の故障」として入ります。TigerBeetleを使うことで、アカウントごとに3つのユーザーデータ識別子を保存したり、イベント(エンティティ間の)をOLGPデータベースにリンクさせることができます。このアーキテクチャは、クリーンな「関心の分離」を提供し、異なるワークロードを独立してスケールさせたり管理したりできるようにします。例えば、もしあなたが銀行なら、顧客記録と一緒に現金をファイリングキャビネットに保管するのは良くないアイデアだと思います。むしろ、現金は銀行の金庫に保管する方がいいですね。なぜなら、情報のパフォーマンスやコンプライアンス、保持特性が異なるからです。このパターンは、ユーザーが名前やメールアドレス(OLGP)を変更する頻度が、取引(OLTP)を行う頻度よりも遥かに少ないからこそ意味があります。それから、一貫性を保つために、書き込みパスではTigerBeetleをOLTPデータプレーンとして「記録のシステム」として扱います。「ショッピングカートに移動」や「チェックアウト」のトランザクションが来たら、まずはすべてのデータ依存関係をOLGPに書き込み(関連するバイナリデータがあればS3にも)、最後にTigerBeetleに書き込んでトランザクションをコミットします。読み取りパスでは、まず記録のシステムをクエリして、厳密な直列性を保ちます。これでわかるかな?もっと詳しく掘り下げられることがあれば教えてね!

TigerBeetleのモデルがディスクセクター全体のエラーを想定しているけど、ビットやバイトのエラーを考慮していないという点が面白いと思った。エラー訂正コードを作ったことがある者として、これは私の理解とは合わない気がする。私が考えるに、これが機能するのはディスクやドライバがセクターをエンコード・デコードしている場合だけだと思うし、(重要なトランザクショナルデータベースを保存するために気にするようなディスクやドライバでは)TigerBeetleが気づく前に大量の(おそらく訂正された)障害を報告しているだろう。もしかしたら、最近の物理ディスクやドライバスタックの挙動についての私のメンタルモデルが古いのかもしれない。

ちょっと補足すると、私たちのモデルはビット/バイトエラーを完全に想定してるよ!ただ、私たちのファザーがバグってて、実際にはその障害をテストしていなかっただけなんだ!

これは、彼らのファザーの盲点に関する投稿を読んだ後の特に面白いJepsenレポートだね。JNI側のセグフォルトは、Rustや他のメモリ安全な言語が使われていたら保護されていなかっただろうみたいだ。メモリ安全性のバグがないことは、TigerBeetleのZigプログラミング(確かTigerStyleだったかな、笑)が意図した通りに機能していることの良い証明になるね。

こちらを見てください:https://news.ycombinator.com/item?id=44201189。実際、Rustがあれば助かったバグが一つあったんだけど(その代わり、アサーションで助かったから、ちょっとカリカリになっただけで、焦げはしなかった)。追記:でも、そうだね、TigerStyleがなかったら、鼻の悪魔にやられてたかも!

後から考えると、分散システムが実際に起こった時間や順序を報告する必要があるっていうのは面白いけど明らかだよね。壁時計の時間を使うんじゃなくて、外部モデルに対して正確な検証を可能にするために。

これは厳密な直列性があるからうまくいってるんだよね。弱い整合性の保証では、必ずしも単一のグローバルな一貫したタイムラインがあるわけじゃない。これって、何かを「難しく」することで実際にはシステムが簡素化される面白いメタパターンだよ。別の例として、ディスクが故障する可能性があると仮定して修復プロトコルを含めることで、遅延しているレプリカの状態同期が「無料」で得られるんだ。なぜなら、それはディスク全体が壊れたときとまったく同じ状況だから!

素晴らしく詳細なレポートが大好き!Jepsenによってテストされて承認されるのは、TigerBeetleにとって大きな後押しだね。まだv1.0にも達してないのに、今後の新たなマイルストーンが楽しみ!このスレッドで素晴らしい洞察を共有している創業者たちに特別な称賛を送りたい。

誤解されないことを願って質問があります。純粋に学びたいという気持ちからです。分散システムに新しく興味を持っていて、決定論的シミュレーションテストに魅了されています。TigerBeetleに関するJepsenのレポートや関連するブログ記事を読んで、GitHubのAntithesis統合コードをざっと見た後、テストの範囲をよりよく理解しようとしています。私の核心的な質問は、Jepsenのテストスイートで検出されたバグはAntithesisの統合でも見つけられた可能性があるのかということです。この質問は、私が立てた数個の仮定から来ていますが、間違っているかもしれません:- TigerBeetleはすでに内部テストスイートとAntithesis製品で包括的にテストされていると思っていました。- AntithesisのテストスイートはJepsenのものよりも堅牢だという印象を持っていたので、JepsenがAntithesisが見つけられなかった問題を見つけたことに驚きました。私の理解が間違っているのか気になっています。例えば:1. Antithesisのテストスイートはこの特定のバグクラスを検出する能力がなかったのか?2. このシステムの特定の部分はAntithesisのテストでまだカバーされていなかったのか?3. JepsenとAntithesisのテストスイートの異なる強みや目標を誤解して、リンゴとオレンジを比較しているのか?この件について理解を深めるための洞察をいただけると非常にありがたいです。私の目標はこれらのトピックについて学ぶことであり、誤った仮定をしたり責任を押し付けたりすることではないことを明確にしたいです。

そうだね、TigerBeetleのブログ記事にはもっと詳しく書いてあるけど、要するに、Antithesisで実行されていたテスト(かなり徹底的だった)では、インデックスバグを見つけるために必要な正確なクエリの組み合わせや順序が生成されなかったんだ。一方で、Jepsenのジェネレーターはその組み合わせに当たったんだよね。Jepsenのテストジェネレーターにも盲点がほぼ確実にあるから、異なるジェネレーターを設計することがとても役立つ理由の一つなんだ!

TigerBeetleに興味があるんだ。クライアントのドキュメントにはCやZigのクライアントが載ってないみたいだけど、Zigで書かれているから最初に存在すると思ってたんだ。これらは存在するの?それともまだ作業中?

「Panic! At the Disk 0」というセクションにちょっと感謝したい。