Cells ADR 018: Topology Service のトランザクション動作
| Status | Authors | Coach | DRIs | Owning Stage | Created |
|---|---|---|---|---|---|
| accepted | @ayufan | ~devops::tenant scale | 2025-08-04 |
コンテキスト
GitLab Cells アーキテクチャでは、複数の分散 Cell をまたいでグローバルに一意なクレーム(ユーザー名、メールアドレス、ルート)の調整が必要です。このシステムは、ネットワーク障害、アプリケーションのクラッシュ、Cell をまたいだ並行操作が発生した場合でも、高可用性を維持しながらデータの一貫性を確保し、競合を防ぐ必要があります。
決定事項
分散リースベースの調整: Cell が中央の Topology Service から排他的なリースを取得してからローカルへの変更を行う「リース先行・コミット後」パターンを実装します。
アトミックなバッチ操作: 複数の関連モデルの変更(User + Email + Route)を単一のバッチリクエストで処理し、分散システム全体でオールオアナッシングのセマンティクスを確保します。
時間制限付きリース管理: 明示的な有効期限の代わりに、設定可能な古さのしきい値(デフォルト 10 分)を持つ作成タイムスタンプを使用し、調整による自動クリーンアップを可能にします。
Rails 主導の調整: 障害からの回復を処理するために、Topology Service の冪等操作を使用した Rails 主導のバックグラウンド調整プロセスを実装します。
オーナーシップセキュリティモデル: クレームを作成した Cell のみがそれを削除できるよう強制し、Cell 間の干渉を防いでセキュリティの分離を確保します。
3フェーズトランザクションパターン:
- フェーズ 1: ローカル DB トランザクションの前にプリフライトリース取得
- フェーズ 2: リース追跡付きのローカルデータベーストランザクション
- フェーズ 3: 操作を完了するためのリースのコミット
メリット
- 分散ロック: 排他的なリースアクセス制御により、並行変更によるデータ破損を防ぎます。
- 適切な回復: 永続的な競合(重複)と一時的な競合(アクティブなリース)を区別し、適切なユーザーフィードバックを提供します。
- 自己修復: バックグラウンド調整による自動回復と明確な障害モードを持つ自己修復システムです。
- 使いやすさ: 宣言的な設定を使用した ActiveRecord との透過的な統合。
- ネットワーク耐性: ネットワーク分断、Cell のクラッシュ、さまざまな障害シナリオをエラーハンドリングとリトライロジックで処理します。
- Cell 間の独立性: 競合が発生するまで Cell は独立して動作し、Cell 間の通信と調整のオーバーヘッドを最小化します。
- 即時ルーティング: リース取得後すぐにクレームがルーティング可能になり、グローバルレプリケーションの遅延を隠してユーザーエクスペリエンスを最適化します。
デメリット
- 複雑さの増加: 単一データベースソリューションと比較して分散調整の複雑さが増します。
- ネットワーク依存: クレーム属性を変更するすべての操作で追加のネットワークラウンドトリップが発生します。
- 調整のオーバーヘッド: 障害回復とデータ整合性の検証を処理するためのバックグラウンドプロセスが必要です。
- 運用上の監視: 本番の健全性のためにリースの経過時間、競合率、調整プロセスの監視が必要です。
- バッチ制約の限界: 同じクレームを単一のバッチ操作で作成と削除の両方を行うことはできないため、特定の管理操作が複雑になる場合があります。
実装要件
Rails アプリケーションの変更
- CellsUniqueness コンサーン: 透過的なクレーム処理のための ActiveRecord 統合
- バッチコレクションロジック: 複数のモデルからのクレームを単一リクエストに集約
- リース追跡: 調整の同期のためのローカル
leases_outstandingテーブル - エラー変換: gRPC エラーを Rails バリデーションエラーに変換
バックグラウンド調整サービス
- 失われたトランザクションの回復: 孤立したリースの分単位の調整
- データ検証プロセス: Rails と Topology Service 間の日次整合性チェック
- カーソルベースの処理: 大規模な検証操作に最適化
Topology Service の実装
- リース排他性の強制: 同一オブジェクトへの並行操作を防ぐデータベース制約
- アトミックなバッチ操作: 複数の作成/削除の単一トランザクション処理
- 冪等な操作: リトライに安全な CommitUpdate/RollbackUpdate 操作
- セキュリティ検証: クレームオーナーシップの強制のための Cell ID 検証
Cloud Spanner スキーマ設計
- 統合クレームテーブル: リース追跡(
lease_id、lease_op)を組み込んだ単一テーブル - 未処理リーステーブル: Rails と Topology Service 間の同期ポイント
- パフォーマンスインデックス: リース操作と検証クエリに最適化
代替ソリューション
トランザクション内クレーム処理
Rails トランザクションの前ではなく内部でクレームを実行:
1. Local DB: BEGIN
2. Local DB: Operations
3. TS: BeginUpdate() (within transaction)
4. Local DB: COMMIT
5. TS: CommitUpdate()
トレードオフ: エラーハンドリングはシンプルになりますが、コネクションプールの枯渇とトランザクション時間の増加の可能性があります。
別テーブルのリース設計
UUID クロスリファレンスを持つ専用 leased テーブルの作成:
トレードオフ: 関心の分離はきれいになりますが、JOIN の複雑さとトランザクションのオーバーヘッドが増加します。
2フェーズコミット(2PC)
従来の分散トランザクションの使用:
トレードオフ: 実績のあるパターンですが、このユースケースには過剰で、複雑さとパフォーマンスのオーバーヘッドが大きくなります。変更を行う際に Cell 間の調整が必要です。
障害回復シナリオ
ネットワーク障害
- リース取得中: リトライしても安全、リソースは割り当てられていない
- ローカルトランザクション中: 調整による自動ロールバック
- コミット中: バックグラウンド調整が操作を完了
アプリケーションのクラッシュ
- ローカルコミット前: 調整が孤立したリースをロールバック
- ローカルコミット後: 調整が保留中の操作をコミット
- クリーンアップ中: バックグラウンドプロセスが結果的な整合性を処理
Topology Service の停止
- グレースフルデグラデーション: 操作は適切なユーザーフィードバックとともに素早く失敗
- 回復: サービスが利用可能になると自動再開
- データ整合性: 調整によりデータ損失なしを確保
監視と運用上の考慮事項
主要メトリクス
- リース競合率: 競合レベルの指標
- 調整頻度: 障害回復の健全性
- 古いリースのカウント: システム健全性の指標
- 操作レイテンシ: パフォーマンス監視
管理インターフェース
- 未処理リースの検査: 失敗した操作のデバッグ
- 手動リースのクリーンアップ: エッジケースの緊急手順
- Cell の廃止: 管理上のクレームクリーンアップ
