- Uberの決済プラットフォームは、2017年以降に蓄積された台帳データが1兆件以上、数PB規模に拡大したため、DynamoDB・TerraBlob・LedgerStoreの混在構成をLedgerStore中心へ移行
- DynamoDBのコスト負担により直近12週間のデータのみを保持していた構成から、append-onlyの台帳型ストレージであるLedgerStoreが長期的な解決策として選ばれた
- 検証では、本番トラフィックを複製するシャドー検証と、全体ダンプを比較するオフライン検証を併用し、現在のデータとコールドデータのリスクを分けて確認
- バックフィルは通常トラフィックより大きな負荷を生む可能性があるため、小さなバッチ、冪等性、速度制御、緊急停止、問題レコードの分離によって段階的に進められた
- 切り替えは数週間かけて保守的にロールアウトされ、初期のフォールバックと1か月間のDynamoDBデータ保持を経て、最終バックアップとテーブル削除まで無停止で完了
移行対象と従来の保存構成
- Uberの決済プラットフォームGulfstreamは、2017年のローンチ時にDynamoDBをストレージとして使用していた
- Uber規模ではDynamoDBのコストが大きくなり、保存構成が3系統に分かれた
- 直近12週間のホットデータはDynamoDBに保存
- 古いコールドデータはUber社内のBlobストアTerraBlobに保存
- LedgerStoreにはすでにデータを書き込んでおり、最終的な移行先となった
- 移行範囲は、2017年以降のUber全事業の台帳データ
- 不変レコード: 圧縮後で1.2PB
- 補助インデックス: 非圧縮で0.5PB
- 台帳レコードは一度書き込むと実質的に変更できず、問題修正が必要な場合は補助インデックスデータを変更できる
LedgerStoreを選んだ理由
- LedgerStoreはappend-onlyの台帳スタイルのデータベース
- 決済系データに合わせた設計がGulfstreamの要件に合っていた
- 暗号署名でレコード変更の有無を確認する検証可能な不変性
- ホットデータとコールドデータを、リクエスト処理と保存コストに合わせて分ける階層型ストレージ
- 結果整合性を持つ補助インデックスの、より優れたレイテンシ特性
- 3つのストレージを1つに減らすことで、Gulfstreamのストレージアクセスコードとインデックス生成設計が単純になる
- LedgerStoreはUberのデータセンター内のオンプレミスで動作し、より短いネットワークレイテンシを提供できる
- LedgerStoreへの移行は、継続的コスト削減効果も大きい
シャドー検証で確認した現在トラフィックの安定性
- バックフィルが正しいか判断するため、5つの基準を置いた
- 完全性: すべてのレコードがバックフィルされている
- 正確性: すべてのレコードが正しい
- 負荷: LedgerStoreが現在の負荷を処理できる
- レイテンシ: LedgerStoreのP99レイテンシが許容範囲内にある
- インデックス遅延: バックグラウンドの補助インデックス生成遅延が許容範囲内にある
- シャドー検証では、既存ストレージに基づくレスポンスと、LedgerStoreをデータソースにした場合のレスポンスを比較した
- 目標は、シャドー検証基準でバックフィルの完全性と正確性を最低**99.99%**以上に合わせつつ、**99.9999%**を上限とすることだった
- 上限が必要な理由は、大規模データ検証ですべての疑わしいケースを最後まで調査すると、プロジェクトが止まりかねないため
- 過去データの移行には、初期開発時点の誤った書き込みや、規模に起因するデータ破損が混ざる可能性がある
- S3が11 ninesの耐久性を保証していても、1兆レコード規模では10件の破損が期待される
- 結果整合性のインデックスでは、数秒後に現れるレコードがシャドー検証で欠落に見える偽陽性が発生する
- 6 ninesを信頼性をもって確認するには1億件の比較が必要で、毎秒1,000件比較しても1日以上のデータ収集が必要
- 7 ninesは同じ条件で12日待つ必要がある
- 本番トラフィックをLedgerStoreに複製しながら、負荷、レイテンシ、インデックス遅延、アクセスコードの信頼性をあわせて確認できた
- 移行中に見つかったレイテンシとインデックス遅延の問題は、複数の修正につながった
- インデックスデータの分散を改善するためのパーティションキー最適化
- ポイントルックアップではなくレコードスキャンを引き起こしていたインデックス問題の修正
- ライブのシャドー検証は現在アクセスされているデータには有用だが、ほとんどアクセスされない過去データ全体に対して強い保証を与えるのは難しい
オフライン検証と増分バックフィル
- オフライン検証では、LedgerStoreの全データとDynamoDBのデータダンプを比較する
- ライブトラフィックは主に最近のデータにアクセスするため、コールドデータに潜む問題はシャドー検証だけでは見つけにくい
- データに問題のあるレコードは、バックフィルを進められるようスキップする必要があり、バックフィル作業自体のバグの可能性も同時に考慮する必要がある
- 最大の検証ジョブは、圧縮後70TB、非圧縮推定300TBのデータを対象とし、単一ジョブで7,600億レコードを比較した
- この規模のApache Sparkジョブにはデータシャッフルが必要で、Distributed Shuffle as a Service for Spark、Dynamic Resource Allocation、Speculative Executionを併用した
- オフライン検証で見つかった欠落レコードは、増分バックフィルの入力として活用された
- 検証とバックフィルを繰り返しながら、すべてのレコードが書き込まれたことを確認した
バックフィルで直面した運用上の問題
- バックフィルは小規模から始め、システムの限界まで段階的に大きくする必要がある
- 限界を超えて無理に押し進めると、自分たちのシステムにDDoSを仕掛ける状況になる
- ボトルネックを見つけて解消した後、再び拡張する必要がある
- 各拡張後には綿密なモニタリングが必要
- 数年分のデータを数か月以内にバックフィルすると、通常トラフィックよりはるかに大きな負荷が発生する
- 本番環境が毎秒1,000件を処理している状況で、毎秒10,000件で1,000億レコードをバックフィルすると120日が必要
- バックフィル作業が進行中に障害を引き起こす可能性がある場合は、即座に停止しなければならない
- バックフィルは一度に最後まで走らせる作業ではなく、増分バッチに分ける必要がある
- 各バッチは数分以内に終わる程度に小さくする必要がある
- バッチの途中で作業が終了する可能性があるため、冪等でなければならない
- バッチ完了時に、読み取ったレコード数、バックフィルしたレコード数などの統計をファイルに残して集計し、進捗を確認する
- 安全なバックフィルには、調整可能な速度制御が必要
- Java/ScalaではGuavaのRateLimiterを利用できる
- 本番トラフィックが少ないときにより速く実行できるなら、システム状態をモニタリングしてRPSを調整する
- Uberはadditive increase/multiplicative decrease方式でRPSを調整したが、安全のため上限は維持した
- 障害や過負荷が疑われる場合、バックフィルを素早く止められる必要がある
- 障害中のバックフィルは、予防措置とノイズ除去の観点から停止すべき
- 障害後も、システム復旧過程で追加負荷が発生する可能性がある
- 緊急停止機能は、規模に関する問題のデバッグにも役立つ
大容量ファイル、障害許容、ロギング
- データダンプファイルのサイズは約1GBに保ち、上下に10倍程度の柔軟性を持たせるのが適切
- ファイルが大きすぎると、複数のツールのMultiPart制限に引っかかる可能性がある
- ファイルが小さすぎると、ファイル数が多くなりすぎ、一覧取得だけでも大きく時間がかかる
- シェルコマンド実行時にARGMAX制限にぶつかる可能性がある
- バックフィルのデータ変換過程では、データ品質問題や破損レコードが必然的に現れる
- 問題レコードはランダムに分散しているため、そのたびに作業を止めるわけにはいかない
- 同時にコードのバグである可能性もあるため、無視してもいけない
- 問題レコードは別途ダンプし、統計をモニタリングする
- 失敗率が高い場合は手動でバックフィルを止め、問題を修正してから再開する
- RPC timeoutでレコード書き込みに失敗する可能性がある
- リトライは可能だが、どこかの時点では理由に関係なく諦めて進めなければ、全体作業が前に進まない
- デバッグと進捗確認のために多くのログを残したくても、ロギングインフラに大きな圧力がかかる可能性がある
- ログを残せたとしても、保存しなければならない量が過度に大きくなる可能性がある
- ログが多く発生する部分には速度制限を適用する
- エラーがまれに発生するなら、すべてのエラーログを残すこともできる
段階的ロールアウトとフォールバックの撤去
- LedgerStoreへの切り替えは、検証およびバックフィル統計の分析に加え、保守的なロールアウトでリスクを下げた
- ロールアウトは数週間にわたって進められ、主要な呼び出し元サービスのオンコールエンジニアの承認とともに実施された
- 初期には、LedgerStoreでデータが見つからなければDynamoDBから取得するフォールバックを使用した
- フォールバックログで欠落と表示された各レコードは、LedgerStoreで実際に抜けているか再確認した
- フォールバックを削除した後も、DynamoDBデータは1か月間保持した
- その後、DynamoDBへの書き込みを停止し、最終バックアップを作成したうえでテーブルを削除した
- 全体の移行は2年にわたって進められ、移行中および移行後にダウンタイムや障害なく完了した
1件のコメント
Hacker Newsのコメント
1.7ペタバイトのデータ(インデックス付きレコード1兆件)を、月数千ドル以下の非常に強力なベアメタルサーバー1台に載せて、SQLiteでサービス提供できるのか気になる
例えばこんなやり方: https://use.expensify.com/blog/scaling-sqlite-to-4m-qps-on-a...
SQLiteは、最大281テラバイトのデータベースをサポートしている。これは281テラバイトのファイルをサポートするディスクとファイルシステムが見つかる前提だ。それでも、内容のサイズがテラバイト級に増えそうなら、SQLiteより中央集権型のクライアント/サーバーデータベースを検討したほうがよいとされている
この程度のストレージをサーバー1台に載せるのは難しそうだし、月数千ドルという水準はなおさら無理だ。SQLiteもこの用途には向いていない
資本と組織力が十分ある会社なら、自前のクラウドをもっと安く作れるが、計算の大きな部分はリスク要因の外注にある
[0] https://www.sqlite.org/releaselog/3_33_0.html
[1] https://www.sqlite.org/limits.html (#12)
LedgerStoreはオープンソースではないようで[1]、関連情報を探すには相互リンクされたUberのブログ記事をたどる必要がある
2021年の記事の中でLedgerStoreについて最も情報が多いのはこれのようだ:
https://www.uber.com/en-US/blog/dynamodb-to-docstore-migrati...
[1]:https://github.com/uber
全体として、Uberは自前で作る志向がかなり強いように思える。既存のオープンソースの解決策では不十分だと判断して、自分たちで作ろうとする傾向がある。例えばFacebookがMySQLにMyRocks/RocksDBを加えて改良し、オープンソースとして維持したアプローチとは異なる
記事を読むと、Uberがかなり早い段階でDynamoDBを誤用していたことが分かる
一部の中核ユーザージャーニーには強い整合性が必要で、過去の取引には大量のデータウェアハウジングが必要だったようだ
まず2テーブル構成のDynamoDBを、DynamoDB + Redshiftのような構成に変えなかったのは不思議だ。かなり一般的なパターンだ
2015年ごろは、Netflix、Spotify、SoundCloud、Uberのようなクールな技術企業がインフラやデータベースツールをたくさん作っていた時期だった
最近のエンジニアはAWS/クラウド用語で話すことが多い
まだこういうツールを自分たちで作る組織があるのは新鮮に感じる
この特定プロジェクトの経済性は分からないが、DynamoDBは本当に高い
以前は、他の人たちがみんなDynamoDBを間違って使っていて、事前計算済みテーブルへのポイントルックアップではなくスキャンやクエリをしているからだと思っていた
だが、分散ハッシュテーブルのように使ってもなお大きなプレミアムを払うことになる
RCU 1つで最大4KB読めるので、100MB読むには100,000 RCU必要になり、これは年30,000ドル、つまり月2,500ドルだ。計算が間違っていないなら、価格面でこれに近いものすらないと思う
https://tigerbeetle.comを検討したのだろうかと気になる
そしてUberはおそらく、Zig Foundationとサポート契約を結んでいる数少ない大企業の1つだろう
この作業に関わった人たちに祝意を表したい。ただ、このチームを運営するコストだけでもかなり大きく、600万ドルの削減額と大差ないように思えるし、さらに保守負担も加わる
決済システムが長期的な賭けになる可能性も低そうなのに、なぜチームがこういうプロジェクトを引き受けるのか興味深い。すでに抱えているエンジニアリングチームによる一種のサンクコストなのだろうか?
その間のどこかには、もっと現実的なコスト見積もりが入り込む余地がかなりある
データとインデックスが1.7PBなら、DynamoDBストレージの定価ベースで年間約510万ドルだ
事業の中核部分にうまく適合した専用システムを持つには、十分現実的に見える
人々が言うほど大仕事ではない。しかもレコードは不変なので、多くの部分がずっと簡単になる
ある規模に達すると自前で作るほうが有利になる、もう1つの例外的なケースなのだろうかと気になる。Uberが扱わなければならない規模はかなり驚異的だ
元記事だけでは、新しくリファクタリングしたサービスの総所有コストがどれくらいなのか明確ではない。今後は自前のデータベースとその背後のストレージを管理しなければならないのでは? 何か見落としている?
その会社はエンドユーザー向け製品を売る会社ではなく、分散ファイルシステムを製品にしていた
私の考えでは、ほとんどの会社にはデータベースのようなシステムを作る専門性がなく、コスト面では開発が望ましく見えても、実際にはそうしたことを恐れている
独占的なクラウドベースのデータストアがどれほど高くつき得るか、そしてそこから別のものへ移行することが現実的に可能だということを非常によく示す事例だと思う
合理的に行動するという前提なら、クラウドサービスプロバイダーに対する精算の時が来そうだ
小さな会社で契約仕事をしたことがあるが、375MBのMySQL DBから出したデータでレポートを回すためにGCP Bigtableを使っており、月11,000ドル以上かかっていた
新卒のデータサイエンティストを雇ってレポートを作らせていたが、非常に小さいデータセットに対して信じがたいほど非効率なことをしていた。明日までに雀の涙の金額で直してくれと言われたので断った
単なる設計ミスのシステムだ。オンプレミスのデータベースを極端に過剰プロビジョニングして同じことを回していても、同じ問題が起きていただろう