- 多くの パッケージマネージャー は、バージョン管理やコラボレーションの利便性から Gitをデータベースのように利用してきたが、規模が大きくなるにつれて性能と保守の問題に突き当たった
- Cargo、Homebrew、CocoaPods などは、Gitインデックスの肥大化や更新速度の低下、CI環境での非効率により、最終的に HTTPベースのインデックスやCDN へ移行した
- vcpkg は依然としてGitツリーハッシュを基盤に動作しており、浅いクローン(shallow clone)環境では ビルド失敗や複雑な回避策 が発生する
- Goモジュールシステム は GOPROXY と チェックサムデータベース(sumdb) を導入し、Git依存を取り除いてセキュリティと速度を改善した
- Gitはコードの共同作業には優れているが、パッケージメタデータの問い合わせや大規模レジストリ管理には不向き であることが繰り返し示されている
Gitをデータベースとして使う試みの反復的な失敗
- Gitは バージョン履歴、分散構造、無料ホスティング などの利点により魅力的だが、データベースとして使うとスケーラビリティの限界に突き当たる
- 複数のパッケージマネージャーがGitをインデックスとして採用したものの、時間の経過とともに 性能低下とインフラ負荷 が深刻化した
Cargo
- crates.io インデックス はGitリポジトリとして始まり、すべてのクライアントが完全な複製(clone)を行っていた
- リポジトリが大きくなるにつれ、delta resolution の段階で libgit2 の性能ボトルネック が発生した
- CI環境では、ビルドのたびにインデックス全体をダウンロードするため無駄が大きかった
- RFC 2789 により sparse HTTP プロトコル が導入され、必要なメタデータだけを HTTPS で取得する形に改善された
- 2025年4月時点で、リクエストの99%が sparse モードを使用
- Gitインデックスは今も存在するが、大半のユーザーはアクセスしていない
Homebrew
- GitHub はHomebrewに対し 浅いクローンの利用停止 を要請し、更新が「非常に高コストな処理」だと指摘した
- homebrew-core の
.git フォルダは1GB近くに達し、更新時に delta resolution による遅延が発生した
- 2023年2月の Homebrew 4.0.0 で tap 更新を JSON ダウンロード方式 に切り替えた
- Git fetch をなくしたことで更新速度が向上し、自動更新周期も5分から24時間に変更された
CocoaPods
- iOS/macOS向けパッケージマネージャー CocoaPods では、数十万件の podspec からなる Specs リポジトリ が過度に肥大化した
- クローンや更新に数分かかり、CI時間の大半がGit操作に費やされていた
- GitHub は CPU rate limit を適用し、浅いクローンがサーバー負荷の原因だと指摘した
- チームは 自動 fetch の停止、完全クローンへの切り替え、リポジトリのシャーディング などの暫定対応を行った
- 1.8 バージョン以降はCDNベースのHTTP配信 へ移行し、ユーザーのディスク使用量を約1GB削減、インストール速度も大幅に改善した
Nixpkgs
- Nix はクライアント側ですでに tarball ベースのチャネル を使っており、Gitクローンを回避している
- パッケージ式は S3 と CDN から HTTP で配信される
- しかしGitHubのインフラは、83GB規模のリポジトリと20,000個のフォーク により負荷を受けている
- 2025年11月、GitHub は レプリカ間の合意失敗と保守作業エラー を報告した
- ローカルクローンは2.5GBだが、フォークネットワーク全体がGitHubのストレージ容量を圧迫している
vcpkg
- Microsoft のC++パッケージマネージャー vcpkg は Gitツリーハッシュ によってバージョン管理している
builtin-baseline により特定コミット時点のポートを再現するには、履歴全体が必要になる
- 浅いクローン環境(GitHub Actions、DevContainers) ではビルド失敗が発生する
- 回避策として
fetch-depth: 0 の設定が必要で、履歴全体のダウンロードが求められる
- Gitツリーハッシュ構造では コミット追跡が不可能 であり、構造的な制約のため修正できない
- 依然としてGitリポジトリベースのレジストリしかサポートしておらず、HTTP や CDN の代替手段はない
Goモジュールシステム
- Grab のエンジニアリングチームは、モジュールプロキシ導入後に
go get の時間が18分→12秒 に短縮された
- 従来方式では、
go.mod を読むために各依存関係のリポジトリ全体をクローンする必要があった
- Goチームは VCSツールへの依存とセキュリティ脆弱性 を懸念していた
- Go 1.13 以降、GOPROXY がデフォルトとなり、モジュールソースと
go.mod を HTTP で提供する
- sumdb(チェックサムデータベース) により、モジュールの完全性と永続性を保証する
Gitをデータベースとして使う際の一般的な問題
- GitベースのWiki(Gollum) は、大規模リポジトリでディレクトリ探索やページ読み込みが遅くなる
- GitLab はGollumの利用中止を計画している
- GitベースのCMS(Decap) は、GitHub API のリクエスト上限(5,000回)に達する
- 約10,000件以上で性能が低下し、キャッシュが空の新規ユーザーはリクエストが集中する
- GitOpsツール(ArgoCD) は、リポジトリのクローン時にディスク容量超過の問題が発生する
- 単一コミットでキャッシュ全体が無効化され、大規模モノレポでは別途スケーリングが必要になる
Gitがデータベースに不向きな構造的理由
- ディレクトリの限界: ファイル数が増えるほど遅くなる
- CocoaPods では16,000個のディレクトリにより巨大なツリーオブジェクトが生成され、ハッシュベースのシャーディングで対処した
- 大文字・小文字の区別の問題: Gitは区別するが、macOS・Windows は区別しない
- Azure DevOps は衝突防止のため、サーバー側のブロック機能を追加した
- パス長制限: Windows の260文字制限により
git status エラーが発生する
- データベース機能の欠如:
- CHECK/UNIQUE 制約、ロック、インデックス、マイグレーション機能がいずれも存在しない
- 各パッケージマネージャーが独自の検証・索引システムを構築しなければならない
結論
- Gitは ソースコードの共同作業 には優れているが、パッケージメタデータの問い合わせや大規模レジストリ管理 には不向きである
- ほとんどのパッケージマネージャーは最終的に HTTPベースのインデックスやデータベース へ移行した
- Gitの利点(バージョン履歴、PRワークフロー)は魅力的だが、データベースの代替としては失敗 している
- 新しいパッケージマネージャーを設計する際、Gitインデックスが魅力的に見えても、Cargo・Homebrew・CocoaPods・vcpkg・Go の事例と同じ限界に到達する
2件のコメント
コントリビューターからの貢献を受けるためにシステムを別途作るより、git のほうが手軽だから使っているのでしょう。限界だと言いますが、あまり共感できませんし、現実的な問題に対する代案もまったく見えてきません。
Hacker Newsの意見
これは一種の共有地の悲劇のように見える。GitHubは無料で優れた機能が多いので、みんな使いたがる。しかしこうした意思決定は、外部性があると常に起きる。
私が最も重要だと考える外部性はユーザーの時間だ。ほとんどのソフトウェア企業はエンジニアリング時間のコストしか気にせず、ユーザーの時間を無視する。機能開発には熱心だが、ユーザーとのインタラクション時間は最適化しない。たとえば、私がアプリを1秒速くするのに1時間使えば、100万人のユーザーが毎年277時間を節約できる。しかしユーザー時間は外部性なので、この種の最適化はほとんど行われない。
結局、ユーザーは無駄に多くのデータをダウンロードして待たされることになり、開発者はその浪費に責任を負わない
私はC向けのCargo/UVを作っている。良い記事で、強く共感した。
始めたばかりの段階では、レジストリ運用は本当に難しい。コードを書くことやツール品質の確保、コミュニティの拡大だけでなく、全世界のトラフィックに耐えるインフラまで考えなければならない。そういう状況ではgitベースのソリューションは魅力的だ。
しかし問題はsparse checkoutだ。gitでパッケージマニフェストをバージョン管理したいが、任意のコミットを追跡しなければならず非効率だ。結局、2回コミットをpushしなければならない構造なので、現実的には不可能だ。
Conanのアプローチが最も実用的だと思う。完全な再現性の代わりに、条件付きロジックをマニフェストに入れる方式だ。バージョン範囲ごとのマニフェストマッピングも可能だ。完璧ではないが、実用的で有用な折衷案だ。
もちろん本当の解決策はデータベースを使うことだが、誰かがサーバー代や維持費を肩代わりしてくれるわけでもないので、現実的には難しい
資金と独立性が問題なら、P2P方式も可能だ。ただしCIキャッシュが効かないとトラフィックが急増する可能性がある
Debian、Fedora、openSUSEのようなLinuxディストリビューションのミラー構成も参考になる
この記事は二つの問題を混同している。一つはgitをパッケージインデックスのデータベースとして使うこと、もう一つは各パッケージのコードをgitで取得することだ。両者は別問題だ。
インデックスはgit、パッケージはzip/tarという構成もできるし、その逆も可能だ。Goの場合はそもそもインデックス自体がない構造だ
GitHubのバックエンド実装や2万個のフォークの話は本質と無関係だ。gitワーキングツリーがなくても効率的なkey-value参照は可能だ。
「gitの履歴書き換えはDBマイグレーションと同じだ」という主張も妙だ。むしろPostgresを1台動かした方がよくないか?
「動いているうちは簡単な方法を使い、ダメになったら直そう」というアプローチは現実的だ。
Juliaも同じ方式を使っていて、Rustよりパッケージ数が1/7程度なので、まだ問題はない。
最上位の Registry.toml ファイルだけを受け取り、必要なパッケージだけをダウンロードするよう改善できる。大きな問題ではない
「Move fast and break things」文化が生んだ結果が、今の遅くてバグの多いソフトウェアだ
「Gitはパッケージマネージャの出発点として優れたデータベースだ」という結論に同意する
「結果的にはうまくいったじゃないか」という立場だ。初期運用には十分役立ち、スケール問題は後から解決できた
ここには生存者バイアス(survivorship bias)がある。Cargoが成功したからこそgitインデックスが肥大化して問題になったのだ。
ほとんどの小さなプロジェクトは、gitをデータ配布プロトコルとしてうまく使っている。
スケールが不確かな初期段階では、gitとGitHubを活用して核心的な問題に集中するのは合理的だ
HNのトップページで「今あなたがやっていることは間違っている」という記事を見ると、いつも謙虚な気持ちになる。
私も何度かそういう経験がある。今回はPG Notify関連の記事がそうだった。
しかし今は一人で開発していて、プロジェクトが成功するかどうかすら分からないので、gitでプラグイン配布するのがいちばん現実的だ。
それでも後でスケール問題が出たら、この記事を参考にするつもりだ
私は個人的にForgejoでコードをホスティングしている。外部公開せず、mTLSで保護している。
しかしGoモジュールは証明書を要求するため、私のForgejoインスタンスを認識してくれない。
SSHを使ってもHTTPSアクセスが必要だと言われ、結局replace directiveでローカル複製を使っている。かなり面倒だ
.gitを付けて$GOPRIVATEを設定すれば、HTTPSリクエストなしでgitコマンド認証を使える。公式ドキュメント参照パッケージマネージャに限らず、多くの小規模プロジェクトがデータをgitリポジトリにクラウドソーシングしている。
ほとんどは規模が小さいため、技術的限界にはぶつからない。
ただしこうした構造は、非開発者の参加障壁を高める。パッケージマネージャは例外だが、一般的なプロジェクトでは問題になる
私はこうした問題を支援するために、Datatigというオープンソースライブラリを作った。
関連発表資料は こちら にある。今後はこの記事を参考にして、スケーリング関連の内容も追加する予定だ