質問の歴史
グローバルに展開する企業は、GDPRやCCPAのような厳格なデータ居住法に直面しています。従来のモノリシックデータベースはデータを1つのリージョンに集中させ、主権を侵害したり、高レイテンシを引き起こします。初期の分散システムはアクティブ-パッシブレプリケーションを使用しましたが、これは単一障害点を作り、書き込みレイテンシの問題を引き起こします。現代のアーキテクチャは、EU、US、およびAPACのユーザーがデータのローカリティ制約を尊重しつつ、ローカルで書き込めるマルチアクティブリージョンをサポートする必要があります。
問題
コアな課題は、CAP定理のトレードオフです。低レイテンシとパーティション耐性の両方を同時に持ちながら、リージョン間で強い整合性を持つことはできません。さらに、リージョンを越える外部キー関係は、データが国境を越えられない場合、実現不可能になります。リージョン間のトランザクションは、PIIが調整中に漏洩するもしれないため、コンプライアンスに違反するリスクがあります。100ms未満の読み取りを維持するためにはキャッシュが必要ですが、主権の境界を越えたキャッシュの無効化は複雑です。
解決策
データベースネイティブの地理的分割を使用したセルベースアーキテクチャを実装します(例: CockroachDBまたはGoogle Cloud Spanner)。region列でテーブルを分割し、PIIが物理的なセルから外に出ないことを確保します。非機密のメタデータのみをグローバルに複製するために、Apache Kafkaを介したChange Data Capture (CDC)を使用します。リージョン間のトランザクションには、分散ロックを避けるためにローカル補償を持つSagaパターンを実装します。読み取りが多いワークロード用に、Cache-AsideパターンでエッジにRedisクラスターを展開し、リージョン間のキャッシュ調整を避けるためにTTLベースの無効化を使用します。
文脈
あるグローバルな決済処理業者は、ドイツとシンガポールでの展開を必要とし、同時に米国のデータセンターを保つ必要がありました。規制要件により、EUユーザーの取引履歴は物理的にフランクフルトに保持する必要があり、APACデータはシンガポールに保持する必要がありました。しかし、国境を越えた送金は、同じ論理トランザクション内で米国の口座から資金を差し引き、EUの口座に入金する必要があり、全てのバランス照会を100ms未満に維持しなければなりませんでした。
解決策1: 地域読み取りレプリカを持つ中央集権型データベース
このアプローチでは、US-Eastにプライマリデータベースをホストし、EUおよびAPACに読み取りレプリカを設置します。シンプルな整合性モデルと複雑な同期なしでの明快なACID保証を提供します。しかし、これによりデータ主権法に違反し、書き込みトラフィックがUS-Eastに流れ、物理的にEUのPIIがUSの土壌に残る可能性があり、シンガポールからの書き込みは200ms以上のレイテンシがかかり、ユーザー体験の要件を満たさなくなります。また、このアーキテクチャはUS-Eastに単一障害点を生じさせ、地域の自律性を必要とするグローバル決済プラットフォームには不適切です。
解決策2: 完全に独立した地域シロにおける夜間ETL
この設計では、各地域に独立したPostgreSQLクラスターを運用し、夜間保守ウィンドウでリージョン間送金を処理し、完全なコンプライアンス隔離とシンプルな地域の自律性を保証します。このアプローチはリアルタイムの国際送金をサポートしないため、ユーザー体験が悪化し、バッチ処理中の照合エラーを解消するのが難しくなります。さらに、このアーキテクチャは、大幅な遅延なしにグローバルな口座残高の集計をサポートできないため、現代のフィンテックプラットフォームにとって不適切です。
解決策3: Sagaオーケストレーションを用いた地理的分割データベース
この戦略では、ユーザーのホームリージョンにマッピングされたpartition_keyを使用した地理的に分割されたテーブルを持つCockroachDBを展開し、補償アクションを持つローカルトランザクションとしてリージョン間送金を管理するためにTemporalワークフローを実装します。この設計は、パーティション制約によってネイティブなデータ居住を強制しつつ、地域ノードに固定されたリースホルダーを介して50ms未満のローカル読み取りを実現しますが、分散したSQLの専門知識を必要とするオペレーションの複雑性を導入します。このソリューションは、Kafka CDCストリームを介してリージョン間メタデータの最終的な整合性を管理し、サガ実行中の一時的不整合をTTLベースの保留状態の可視性を通じて管理します。
選択されたアプローチ
チームは解決策3を選択しました。なぜなら、それがトランザクションのセマンティクスを損なうことなく、データ主権とレイテンシの制約を独自に満たしたからです。彼らはCockroachDB REGIONAL BY ROWテーブルを設定し、EUの行をフランクフルトノードに固定し、メタデータキャッシュのためにエッジロケーションにRedis Clusterを展開し、自動的な失敗時の補償を持つクロスリージョン送金をオーケストレートするためにTemporalサガを実装しました。
結果
このシステムは、GDPR監査に合格し、クロスボーダーでのPII漏洩はゼロであり、日々50,000件のリージョン間トランザクションを処理し、99パーセンタイルの読み取りレイテンシは45msでした。カスタマーサポートチームは、地域の障害中に一時的不整合を解決するために保留中のサガの状態をAPIエンドポイントを介して照会できました。このアーキテクチャは、アプリケーションの変更なしにCockroachDBクラスターに新しいセルを追加するだけで新市場への拡張をサポートします。
データ主権ゾーンを跨いで外部キー関係がある場合、参照整合性をどのように維持しますか?
データが物理的にそのゾーンから出ていけない場合、リージョン間でデータベースレベルの外部キー制約を強制することはできません。UUID参照を使用し、Outbox Patternを介してKafkaに発行する非同期検証を使用して、アプリケーションレベルの参照整合性を実装します。消費者は参照を確認し、確認を発行し、タイムアウト後に孤立検出を行います。これにより、コンプライアンスのために即時の整合性は犠牲になりますが、データ移行なしに最終的な整合性が確保され、無効な外部キーを参照するトランザクションをロールバックするためにSaga補償を使用します。
リージョンがクロスリージョンサガの間に失敗した場合、搭乗中のトランザクションはどうなりますか?
Sagaパターンは自動的に失敗を処理しない; idempotencyキーを各リージョンにローカルにRedisまたはEtcdに格納し、リトライ中の重複処理を防ぐためにidempotencyの設計が必要です。クレジット操作中にリージョンBが失敗した場合、オーケストレーターのタイムアウトがリージョンAでの補償トランザクションをトリガーし、差し引かれた金額を払い戻します。これには、整合性の競合を防ぐためにPostgreSQL Advisory LocksまたはZooKeeper Distributed Locksを利用します。システムは、カスタマーサポートの介入のために保留中のトランザクション状態をAPIエンドポイントを通じて公開し、部分的な失敗状態がクエリ可能で解決可能であることを保証します。
ジオパーティションセル間で異なる保守ウィンドウを持つゼロダウンタイムスキーママイグレーションを実行するにはどうしますか?
Expand-Contractパターンを使用し、Feature FlagsをLaunchDarklyによって管理し、最初にすべてのリージョンで加算的なDDL変更(新しい列、テーブル)を各リージョンの保守ウィンドウ中に展開し、アプリケーションを後方互換性を保持します。Debezium CDCパイプラインを使用してデータを非同期に移行し、すべてのリージョンでスキーマ普及が確認された後にのみ、新しいコードパスを機能フラグを介して有効にし、いかなるリージョンも古いデータを提供しないことを保証します。すべてのリージョンが移行完了を確認するまで、破壊的なDDL(列の削除)を決して行わず、各セル内でのBlue-Greenデプロイメントを利用して、複製遅延が閾値を超えた場合には即座にロールバックします。