Content last updated 2025-10-13

データベーストランザクション内での Topology Service クレーム

This page contains information related to upcoming products, features, and functionality. It is important to note that the information presented is for informational purposes only. Please do not rely on this information for purchasing or planning purposes. The development, release, and timing of any products, features, or functionality may be subject to change or delay and remain at the sole discretion of GitLab Inc.
StatusAuthorsCoachDRIsOwning StageCreated
sxuerebayufanayufandevops tenant-scale2025-05-09

コンテキスト

GitLab の Cells アーキテクチャでは、Topology Service のトランザクションで詳しく説明されているように、メールアドレスやルートなどのクラスター全体で一意なリソースをクレームするために Topology Service との調整が必要です。 重要な決定事項は、これらのクレームリクエストをデータベーストランザクションの内部で行うか外部で行うかという点です。

主な要件:

  • 複数の Cell をまたいだ競合を防ぐためにリソースをクレームする。
  • Rails DB と Topology Service の間のデータ整合性を維持する。
  • パフォーマンスクレームのバッチ処理をサポートする。
  • 平均 50 クレーム/秒を処理し、最大 6 倍の 300 クレーム/秒に対応できること。
  • データベースの安定性に影響を与えないこと。

技術的な制約:

  • Topology Service へのネットワーク呼び出し:P99.95 で約 200ms
  • 現在のコネクションプール:58 コネクションで 200ms で約 290 クレーム/秒をサポート
  • user.id など、Postgres のプライマリキーが必要。これはトランザクション内でレコードをデータベースに保存するときに生成される。
  • Rails はトランザクション内で作成/更新を自動的にラップする

決定事項

ActiveRecord トランザクション内部でコールバックを使用してクレームを実装します。実装では以下を行います:

  • after_save(公式にドキュメント化)または before_commit(ドキュメント未掲載)コールバックを使用し、トランザクション内で実行します。
  • INSERT 後、COMMIT 前(すべての ID が利用可能になった時点)にリソースをクレームします。
  • 可能な場合は同一トランザクションからの複数のクレームをバッチ処理します。
  • Topology Service リクエストにクライアント側 200ms タイムアウトを設定します。
  • トランザクション内の Topology Service リクエストが N クレーム/秒になった時点でクライアント側サーキットブレーカーを実装します。N は設定可能で、データベース負荷を考慮します。
  • クレームのロールアウトでは、フィーチャーフラグを使用して実施するクレームの割合を段階的に増やし、データベースへの悪影響を観察します。正確なロールアウト手順はまだ決定されておらず、レガシー Cell として GitLab.com を Cell クラスターに採用の一部として決定されます。

監視要件:

  • コネクションプールの使用率と待機時間。
  • クレーム関連操作のトランザクション時間。
  • Topology Service リクエストのレイテンシ(P50、P99、P99.95)。
  • クライアント側サーキットブレーカーのヒット数。
  • クライアント側タイムアウトのヒット数。
  • クライアントとサーバーからの失敗したクレームとロールバック率。

結果と影響

ポジティブ:

  • シンプルな実装:既存の Rails パターンとコールバック内で動作する
  • 早期本番投入:最小限のアーキテクチャ変更が必要
  • アトミックなロールバック:クレームが失敗した場合にトランザクションがクリーンにロールバックされる
  • 副作用の問題なし:コミット前にクレームが発生するため、Topology Service への確認前にデータベースにコミットしてしまう状況を防ぐ
  • 十分な容量:現在のインフラストラクチャがピーク負荷の 6 倍をサポート
  • バッチ処理サポート:すべての ID が利用可能になった後、同一トランザクション内でクレームをバッチ処理できる

ネガティブ:

  • コネクションプールの使用:ネットワーク呼び出し中はクレームのためにデータベースコネクションを保持する。
  • トランザクション時間:約 200ms のより長いトランザクションはデータベースへの負荷増加につながる可能性:
    • WAL の蓄積
    • 各トランザクションがコネクションを保持するため、コネクションプールの枯渇
  • スケーラビリティの上限:コネクションプールサイズに制限される(現在 290 クレーム/秒)

代替案

オプション 1:トランザクション外でのクレーム

以下の理由で延期:

  • 大規模なリファクタリングが必要。例えば何千もの User.create! 呼び出しの変更が必要で、クレームするモデルごとに必要となる。
  • トランザクションの自動ラップという Rails の慣習に反する。
  • 実装の複雑さとエラーハンドリングが高い。
  • ID がまだ利用できない場合にクレームのバッチ処理が難しい。
  • 現在の容量(ピークの 6 倍)は最適化を時期尚早にする。

オプション 2:データベーストリガー

以下の理由で却下:

  • データベース以外の副作用を処理できない。
  • Rails アプリケーション層の外に複雑さを追加する。
  • テストと保守が難しい。
  • カスケード削除ケースはすでに検証ループで処理されている。
  • クラスター全体でリソースがクレーム可能かどうかを検証できない。

オプション 3:ActiveRecord の save/update のオーバーライド

以下の理由で却下:

  • 複数の属性を効率的にバッチ処理できない。
  • バッチリクエストではなく属性ごとに 1 つのクレームをトリガーする。
  • update_column などのメソッドでは動作しない。