GitHubの新しいGitストレージアーキテクチャ
GitHub's new Git storage architecture (github.blog)
要約
GitHubは、特にエージェント開発に対応するため、リポジトリあたり毎秒数千件の書き込みを処理できる、大規模なスケーラビリティを備えた新しいGitストレージアーキテクチャを発表しました。このアーキテクチャはストレージとコンピューティングを分離し、GitオブジェクトにはAzure Blob Storageを、参照更新にはRef-CASという新しいサービスを使用することで、コンポーネントの独立したスケーリングとパフォーマンスの向上を実現します。
全文翻訳
GitHubの新しいGitストレージアーキテクチャの内部
先週のGitHub Universeで、毎秒数千件の書き込みを処理し、オンデマンドでスケーリング可能な読み取りを備えた新しいGitストレージアーキテクチャを発表しました。内部テストを通じて、7,200件のプッシュ/秒をリポジトリに送信し、一貫して低レイテンシで読み取りトラフィックを提供しながら、5,000のエージェントがリポジトリで同時に作業できる能力を実証しました。この新しいアーキテクチャは、GitHubが何億人もの開発者に対してエージェント開発をサポートするための基盤であり、GitHubが稼働し続ける中でその下で構築してきました。
過去1年間で、エージェントはGitHub上のビジーなリポジトリの外観を変えました。このシリーズの最初の投稿では、次世代Gitストレージアーキテクチャの背後にある2つの原則、すなわち「調整を最小限に抑える」と「ストレージとコンピューティングを分離する」を説明しました。前回の投稿では、99.99999%のリポジトリをAzureに移行し、書き込みクォーラムを個々の参照にスコープし、Azure Blob Storageによってバックアップされた仮想レプリカを構築した「ブリッジ」について説明しました。
また、書き込みのほとんどは順序付けする必要がないという観察で締めくくりました。Gitオブジェクトは不変であり、その内容によって名前が付けられているため、2人のライターは何も合意せずに同時にそれらを保存できます。プッシュで合意が必要な部分は参照の更新であり、参照の更新は比較的小さなものです。仮想レプリカは、その周りに構築するための最初のステップであり、リポジトリを新しいアーキテクチャにオンボードする手段でした。
アーキテクチャの残りの部分は、その観察に基づいています。合意が必要なのは参照の更新のみです。これにより、かつて密接に結合されていたGitインフラストラクチャのコンポーネント(データの保存、リクエストの提供、リポジトリの保守)が独立してスケーリングできるようになります。開発者にとっては、GitはGitのままです。プッシュ、フェッチ、ブランチは同じ方法で、同じ保護措置が講じられたまま行われます。以下では、設計の背後にある要件を説明し、システムを通過するリクエストを追跡し、2つのコンポーネントを深く掘り下げます。
エージェントスケールのための構築
GitHubプラットフォームのスケーリングを2つの軸で考えています。
- 水平スケーリングは、多くのリポジトリで作業できる開発者とエージェントの数です。この軸におけるGitHubの現在の多くのアプローチは、長年にわたって実証されており、新しいアーキテクチャに引き継がれています。例えば、インフラストラクチャ全体でのテナントのパーティショニング、テナントトラフィックの分離および/または公平性の強制、負荷を考慮したルーティングなどです。
- 垂直スケーリングは、1つのリポジトリで作業できる開発者とエージェントの数です。エージェントがより多くのコードを書くにつれて、個々のリポジトリはより大きく、よりビジーになると予想されるため、ほとんどの要件は垂直軸から来ています。
今日、GitHub.comで最もビジーなリポジトリは、通常は短時間のバーストで、1秒あたり数十件の書き込みを受け取ります。エージェントが並列で24時間体制で作業するようになると、これらのバーストは来年中に持続的なレートになると予想されます。各プッシュはより多くの読み取りにつながります。CIの実行、デプロイシステムが新しいコミットを取得し、他のエージェントがコンテキストのためにそれをフェッチします。この読み取りのファンアウトは、書き込みとともにスケーリングする全体的なトラフィックに大きな増幅効果をもたらします。
すでに、カーブのトップにあるリポジトリでこの傾向が見られます。以下のチャートは、3月から5月にかけてのGitHub上の1つのリポジトリへの日次リクエストを示しており、そのトラフィックは約4倍になりました。それ以降、その成長は指数関数的な軌道をたどっています。
[画像: 2026年3月から5月にかけての単一リポジトリへの日次リクエストの折れ線グラフ。3月の平均は1日あたり4400万リクエスト、5月の平均は1日あたり1億8100万リクエストで、約4倍の増加。]
エージェント駆動開発がこの速度で加速し続ける世界のために計画を立てる必要があります。次の増分ではなく、少なくとも2桁のスケールアップを目標としました。単一のリポジトリの場合、新しいアーキテクチャは以下を行う必要がありました。
- 毎秒数千件の書き込みを受け入れる。
- 複数のライターノードをサポートする。これには2つの理由があります。
- 経験上、受信したパックのインデックス作成とオブジェクト接続性のチェックが書き込みのCPUコストが最も高く、そのコストは書き込みサイズとともにスケーリングするため、単一ノードはすぐにコンピューティングバウンドになります。
- その作業を複数のノードに分散させることで、耐障害性が向上し、ノード障害時のリカバリが短縮されます。
- コミットレイテンシを最小限に抑える。エージェントは、次のステップに進む前に各プッシュを待機します。
- 各参照に対して順序付けられた変更を記録し、参照間で迅速に線形化できる能力を持つ。遅延した仮想レプリカは、受信リクエストを提供するために必要なデータのみをダウンロードして追いつく必要があります。
- 読み取りと書き込みの比率を考えると一般的なケースである、何も変更されていない場合の読み取りを安価に保つ。
- 各コンポーネントが独立してスケーリングできるようにする。
それに加えて、システムは全体的な信頼性を向上させ、グローバルにスケーリングできる必要がありました。
「Project Rubicon」の紹介
2026年5月、最初のAzure移行の直後に、これらの要求を満たすことができる設計に取り組み始めました。いくつかの方法を試しました。それらのすべてにおいて、パックデータはAzure Blob Storageに保存され、一部では参照状態もそこに保持することを実験しました。トレードオフについては後述しますが、GitHubのようなプラットフォームが必要とする正確性、耐久性、可用性、およびレイテンシ保証を維持しながら、スループットを最大化できると確信した設計に落ち着きました。エージェント開発によって業界が超えた「後戻りできない地点」への敬意を込めて、これを「Rubicon」と名付けました。
Rubiconをトップダウンで見ていきましょう。多くのことは変わりません。Gitクライアント、API、Web UIからのリクエストはSpokesに到着し、Spokesは引き続きルーティングとロードバランシングを処理し、GitHubの他の部分がリポジトリデータを読み書きするために使用する内部APIを提供します。Spokesは、水平スケーリングに必要なパーティショニング、分離、およびテナントの公平性を維持する配置およびバランシングサービスとしての役割も維持します。対照的に、Spokesの下のデータプレーンは新しいものです。
[画像: アーキテクチャ図。Git、API、WebリクエストはSpokesを通過し、リポジトリごとに仮想レプリカのグループにルーティングされます。仮想レプリカはキャッシュされた状態のみを保持します。Ref-CASは参照とパックマニフェストを格納し、Azure Blob Storageはパックデータを格納します。コンパクションノードはBlob Storageでパックをコンパクションおよびガベージ収集し、Ref-CASでパックマニフェストを更新します。]
Spokesは、各リポジトリに割り当てられた少数の仮想レプリカグループのいずれかに各リクエストをルーティングします。仮想レプリカは、読み取り、書き込み、またはその両方を提供する使い捨てコンピューティングの単一フリートです。仮想レプリカのローカルディスクは、いつでも再構築できるキャッシュです。
Spokesは、可能な限りリポジトリのトラフィックを同じ仮想レプリカにルーティングし、キャッシュをウォームに保ち、レイテンシを低くしますが、リクエストが特定の仮想レプリカに着陸することに正確性が依存することはありません。仮想レプリカが消滅した場合、リポジトリの他の仮想レプリカはサービスを提供し続け、トラフィックが到着すると新しいものがキャッシュを埋めます。
リポジトリがビジーになると、仮想レプリカを追加します。別途、コンパクションノードのフリートがバックグラウンドでBlob StorageとRef-CASに対して直接動作し、各リポジトリのパックをマージして到達不能なオブジェクトを削除します。その作業は仮想レプリカでは実行されないため、CPUとメモリはほぼ完全にリクエストの提供に専念できます。
永続的な状態は2つの場所に存在します。
- Gitオブジェクトは、パックと補助的な読み取りアクセラレーションデータの形式で、Azure Blob Storageに存在します。
- 参照とリポジトリを構成するパックのリスト(パックマニフェスト)は、Ref-CAS(「Reference Compare-and-Swap」の略)と呼ばれる別のレプリケートログサービスに存在します。
アーキテクチャを書き込みと読み取りの観点から見ていきましょう。
- 書き込み:仮想レプリカは受信したパックをインデックス化してBlob Storageにアップロードし、次にRef-CASにトランザクションをコミットして、パックをマニフェストにリストし、参照を更新します。その後、コンパクションノードがパックをマージして読み取りを高速化します。
- 読み取り:仮想レプリカはRef-CASで参照バージョンを確認し、Blob Storageから必要なパックをキャッシュにプルし、そこから読み取りを提供します。
仮想レプリカ上のソフトウェアスタックから、トラフィックルーティングやワークロードスケジューリングがさまざまなユースケース(例:多数の小規模リポジトリ対1つの大規模リポジトリ)に合わせてどのように調整されているかまで、新しいアーキテクチャ内には多くの異なるアプローチがあります。2つのコンポーネントを深く掘り下げて、2つの設計原則を中心にそのストーリーを語りましょう。
調整の最小化:高スループットでの参照状態の変更
Azure Blob Storageのようなオブジェクトストレージサービスは、その機能において非常に優れており、ソースコードストレージのような多くのデータ集約型アプリケーションに適しています。耐久性がありレプリケートされており、必要なだけ保存でき、バイトあたりのコストが比較的低く、トラフィックが多くのキーに分散されている限り、非常に高い集約スループットを提供します。その結果、新しいGitストレージアーキテクチャのほぼすべての永続的なデータはAzure Blob Storageに存在します。これには以下が含まれます。
- プッシュ、コンパクション、ガベージコレクションによって書き込まれたパックとインデックス。
- これらのパックからメンテナンスプロセスが構築するさまざまな読み取りアクセラレータ(マルチパックインデックス、到達可能性ビットマップ、一部のカスタムフォーマットなど)。
- コンパクションおよびガベージコレクションが作業を再開または監査できるようにするレコード、およびコンパクションノードが作業を要求するために使用するリース。
- よく繰り返されるフェッチのキャッシュされた応答。
- リポジトリの参照状態のチェックポイント。ただし、リクエストの提供に使用される権威ある状態ではありません。
[画像: Azure Blob Storage内の単一リポジトリのデータ図。Push、Compaction、Maintenance、GC、Ref-CASが書き込み、フェッチはキーごとに読み取ります。パックとインデックス、レコードとリース、マルチパックインデックスや到達可能性ビットマップなどの読み取りアクセラレータ、キャッシュされたフェッチ応答、参照チェックポイントを保持します。]
これらのデータのほとんどは、一度だけ特定のキーに書き込まれ、その後変更されないため、Blob Storageに非常に適しています。
参照は反対です。ビジーなリポジトリの参照は、多くの書き込みが同時に更新を競合する比較的少量の状態です。そのアクセスパターンはBlob Storageから提供するのが難しく、最初のプロトタイプでその理由がわかりました。この種のワークロード向けのオブジェクトストレージは直感的な選択であり、ある程度のスケールまでうまく機能します。条件付き書き込みは、比較・スワップを無料で提供し、データはすでに耐久性がありレプリケートされています。しかし、2つの試みたアプローチで示されるように、困難な選択に直面しました。
最初のアプローチは、リポジトリのすべての参照の書き込み先行ログであり、単一のブロブによってインデックス化されていました。すべての更新は、そのブロブに対する条件付き書き込みです。これは正確であり、読み取りは簡単です。仮想レプリカはブロブが変更されたかどうかを尋ね、変更されていなければ完了です。書き込みは、それが不足しているところです。ブロブに対する条件付き書き込みは一度に1つしかインフライトできません。そのため、書き込み中に到着したすべてのプッシュを次の書き込みにバッチ処理しても、リポジトリの書き込み上限は単一ブロブへの1回の往復あたりの書き込み1件です。実際には、これにより毎秒数百件台の書き込みスループットが得られました。
2番目のアプローチは、参照を複数のセグメントブロブに分割し、各参照がそれらのいずれかにハッシュされるようにしました。異なるセグメントの参照への書き込みは、もはや互いに待機する必要がなくなり、上限が引き上げられました。しかし、読み取りは変更があったかどうかを知るためにすべてのセグメントをチェックする必要があり、最も遅いセグメントと同じくらいしか速くありません。読み取りレイテンシのために書き込みレイテンシを犠牲にしたのです。読み取りは書き込みの何倍も発生します。複数の書き込みと1つの読み取りを並べて表示することで、これを視覚化できます。
[動画]
どちらのアプローチもすべての要件を満たせなかったため、別のものを作成しました。参照専用のレプリケートログサービス、つまりRef-CASサービスです。これは、前回の投稿の最後にまだスタックのどこかにあると言っていた(小さな)コンセンサスグループです。仕組みは次のとおりです。
Ref-CASの各参照更新は、クォーラムによってコミットされる比較・スワップです。トランザクションは、それが触れる参照のみをロックし、各参照がライターが期待する値のままであることを確認し、ログのレプリカの過半数がそれを記録すると可視化されます。エージェントは主に自分のブランチにプッシュするため、ほとんどの更新は競合せず、並列でレプリケートされます。2つのプッシュが同じ参照を更新した場合、1つだけがログに追記されます。
[動画]
Ref-CASへの各コミットにはログシーケンス番号(LSN)が付与され、リポジトリのバージョンはその最新コミットのLSNです。読み取りは、そのバージョンに対するバージョンチェックから始まります。仮想レプリカは、既に保持している参照状態のバージョンを送信します。そのバージョンがまだ最新であれば、応答は参照をまったく含まず、変更されていないリポジトリのコストは単一の比較です。仮想レプリカが遅れている場合、現在の参照とバージョン、およびパックデータへのポインタや、ガベージコレクションサイクルがいつ発生したかなど、知る必要があるその他の情報が返されます。
レガシーアーキテクチャのコンセンサスグループとは異なり、Ref-CASはオブジェクトデータを保持または検証しません。それは単にそれらへのポインタを格納します。仮想レプリカは、参照トランザクションをコミットする前に、プッシュのパックをBlob Storageにアップロードします。参照は、既に永続化されているオブジェクトのみを指します。
これらのアプローチにより、参照状態のみを毎秒数万件の操作でトランザクション処理でき、読み取りが特定の要求に対してダウンロードする必要があるものを決定するのに役立つレプリケーションログへの追加アノテーションのベースラインを提供できます。
しかし、単一のリポジトリで毎秒数千件の書き込みをサポートすることは、第二次の問題、つまり読み書きパフォーマンスの維持を生み出します。そのためには、コンパクションに対する別の方法が必要でした。
ストレージとコンピューティングの分離:毎秒数千件の書き込みでのコンパクション
すべてのプッシュはBlob Storageにパックを追加します。Gitは複数のパックからリポジトリを提供できますが、パック数が増えるたびにルックアップがわずかに高価になるため、パック数は無制限に増やすことはできません。レガシーアーキテクチャでは、コンパクションはトラフィックを提供するのと同じレプリカで実行され、すべてのレプリカで実行する必要があります。新しいアーキテクチャでは、これは完全にバックグラウンドで別のノードで実行され、永続ストレージに対して直接動作し、Ref-CASサービスを使用して仮想レプリカに進化する状態を通知します。
コンパクションノードはそれぞれ、リースを通じてリポジトリデータのサブセットの所有権を主張します。これにより、コンパクションを水平にスケーリングできます。以下は、1つのコンパクションノードがリポジトリパックの単一シャードを処理する仕組みです。
[動画]
ノードはまずBlob Storage内のシャードのリースを取得するため、一度に1つのノードのみがシャードをコンパクトします。その間、仮想レプリカはパックのアップロードを続け、パックマニフェストにリストします。ノードはマニフェストを読み取って新しいパックを見つけ、それらをオープンセグメントに収集します。これは、コンパクションされるデータの保持場所として機能します。セグメントがパック数、サイズ、または年齢に基づいて定義された制限に達すると、セグメントをシールして新しいパックを受け入れなくなり、パックをより少なく、より大きなパックに結合します。コンパクションが発生している間、シャードの次のセグメントは入力パックのキャプチャを開始できます。新しいパックが公開される前に、ノードは新しいパックが入力とまったく同じオブジェクトを含んでいることを確認します。次に、新しいパックをアップロードし、マニフェストに追加します。2番目のトランザクションで入力がマニフェストから削除され、クリーンアップで後で削除されます。
スケールアウトするために、このプロセスは多くのシャードで独立して実行されます。各パックのハッシュはシャードにマッピングされるため、シャードとノードを追加するとコンパクションスループットが増加します。サイズ制限を下回っているコンパクションされたパックは、そのハッシュによってシャードに戻り、再度マージされるため、パックは制限に達するまでティアごとに成長します。結果として、プッシュが増加しても、パック数は実質的にフラットになります。
[動画]
コンパクションはパックをマージしますが、すべてのオブジェクトを保持します。ガベージコレクションは、削除されたブランチや強制プッシュによって残されたオブジェクトなど、参照されていないオブジェクトを削除するステップです。新しいアーキテクチャでは、ガベージコレクションは、コンピューティング負荷の高い部分を処理するために専用のワーカーを使用することで、コンパクションと非常によく似た方法で機能します。また、データを安全に削除するためのいくつかの特別な動作もあり、それらは独自の議論に値します。
これらのアプローチを使用することで、書き込みが増加してもレイテンシ曲線がフラットであることを一貫して実証でき、コンパクションおよびガベージコレクションプロセス自体のパフォーマンスを向上させるための他の計画された最適化もあります。
次に来るもの
最初に設定した2つの原則により、このアーキテクチャの各部分が独自にスケーリングできるようになります。読み取りと書き込みは仮想レプリカを追加することでスケーリングします。Ref-CASはリポジトリ全体ではなく、各参照を独立して順序付けします。メンテナンスはコンパクションノードを追加することでスケーリングします。私たちの目標は、1人の開発者のサイドプロジェクトから最もビジーなモノレポまで、すべてのリポジトリが特別な処理なしで同じアーキテクチャで実行されるようにすることです。リポジトリは小さく留まりません。サイドプロジェクトは一晩でエージェントのフリートを獲得する可能性があり、追いつくために移行や特別な処理を必要としないはずです。1つのアーキテクチャは、私たちが作るすべての改善がすべてのリポジトリに一度に届くことも意味します。
この基盤により、さらに多くのことができます。私たちは次の3つの分野に焦点を当てています。今日の必要性を超えるアーキテクチャのスケーリング、GitHub上で行われる作業におけるエージェントの効率化、そしてエージェント開発のためのGit自体の進化です。
アーキテクチャの改善
このアーキテクチャをさらに進化させる計画があります。これらの改善のそれぞれにより、リポジトリはより多くのアクティビティを処理しながら、各リクエストの背後にある作業を削減できます。
- 必要なものだけを読み取る。パック全体ではなく、Blob Storageから単一のオブジェクトを取得します。
- パックをタイプ別に整理する。コミット、ツリー、ブロブを独自のパックにグループ化します。多くのリクエストは、より少ないデータにアクセスします。
- 部分レプリカ。ローカルディスクにすべてのパックがなくてもリクエストを提供します。
- 具現化されたビュー。準備されたアーカイブ、デフォルトブランチのほぼライブなシャローコピー、各コミットとともに保存されたエージェントセッションコンテキストなど、すぐに使用できる形式でリポジトリデータを保持します。
- グローバルレプリカ。任意のリージョンにレプリカを配置し、それを使用するワークロードに近づけます。読み取りは、最新の書き込みからバウンドされたスタレネス内に留まります。
- ゼロへのスケーリング。実際にアクティブなリポジトリを持つフリートを増減させます。
GitHub上での効率的なエージェント操作
インフラストラクチャのスケーリングは、エージェントをサポートする一方の側面です。もう一方の側面は、各エージェント操作をより安価にすることです。取得するバイト数が少なく、往復回数が少なく、変更を待つ時間が短くなります。これらの高速フォローアップ項目のそれぞれは、この記事のストレージ作業に基づいています。
- クローンなしで作業する。多くの場合、エージェントタスクは完全なワーキングツリーを必要としません。少数のファイルと変更を加えるための十分なコンテキストが必要です。それらは両方ともサーバーから直接読み取ることができます。
- アトミックコミットAPI。複数ファイルの変更を1つの操作でコミットします。変更全体がコミットされるか、競合が発生した場合はクリーンに拒否されます。
- イベントストリーム。エージェントとその周囲のシステムは、リアルタイム情報で最もよく機能します。汎用イベントストリームにより、変更をサブスクライブして発生時に反応できます。
エージェント向けGitの改善
Git自体にも取り組むべきことがあります。GitHubはGitプロジェクトの長年の貢献者であり、これからもやめません。継続したい作業の例をいくつか紹介します。
- 軽量クローン。部分クローン、シャロークローン、スパースチェックアウトは、それぞれエージェントがより少ないデータをフェッチできるようにします。それぞれにギャップがあります。それらは常にうまく連携するわけでもありません。各部分を改善し、それらを適合させたいと考えています。
- 隠れたスケーリングコストの削除。Gitには、リポジトリの成長よりもはるかに速く遅くなるコードパスがまだあります。最近のリリースでいくつか修正されましたが、さらに見つかると予想しています。
- 高速な履歴ウォーク。コミットグラフのようなメタデータは、すべてのコミットをウォークすることなく、マージベースと到達可能性の質問に答えることができます。これは、エージェントのAPI呼び出しがサーバー上のGit作業に変換される場合に最も重要です。
- 一般的な落とし穴の回避。エージェントは開発者と同じ間違いを犯します。Gitは一般的なものの多くをキャッチし、損傷を引き起こす前に警告できます。
エージェントはソフトウェアの構築方法を変えました。GitHubとGitもそれに合わせて変化しています。この新しいアーキテクチャをスタートラインから進めることに興奮しています。現在限定的なプライベートプレビューで利用可能であり、来年初頭にさらに広く展開される予定です。リポジトリで作業するエージェントの数がアイデアによって制限されるときに、チームが何を構築するかを見るのが待ちきれません。お客様やGitコミュニティとともに、GitとGitHubを進化させ続けます。
執筆者
[画像: Karthik Raman]
Distinguished Engineer
[画像: Brian Celenza]
Brianは、GitHubを支えるすべてのリポジトリインタラクションのストレージとコアサービスを担当するPrincipal Software Engineerです。