公開:2025年6月5日
10分で読めます
15年前のGit関数のパフォーマンス上のボトルネックを追跡して修正し、効率性の向上、より強固なバックアップ戦略の導入、リスクの軽減を実現した方法をご紹介します。
リポジトリのバックアップは、いかなる堅牢なディザスタリカバリ戦略においても不可欠な要素です。しかし、リポジトリのサイズが大きくなるにつれて、信頼性の高いバックアップの作成プロセスはますます難しくなります。GitLabでは、Railsリポジトリのバックアップに48時間かかっていました。そのため、バックアップ頻度かシステムパフォーマンスのどちらを優先するかを選ばないといけないという難しい状況にありました。お客様のため、そして社内ユーザーのためにこの問題に取り組むことにしました。
最終的に、15年前に作成したGit関数でO(N²)レベルの複雑なアルゴリズムが使われていたのがこの問題の原因であることを突き止め、アルゴリズムを変更して修正したところ、__バックアップ時間が大幅に短縮__されました。その結果、コストの削減、リスクの軽減とともに、コードベースに合わせて実際にスケールするバックアップ戦略を実施できるようになりました。
これは、大規模なリポジトリのユーザーなら誰にでも起こりうるGitのスケーラビリティの問題であることが判明しました。この問題をどのように追跡して修正したかをご説明します。
まずは、問題について詳しく見ていきましょう。組織においてリポジトリのサイズが大きくなり、バックアップが複雑化するにつれて、以下のような課題が生じる可能性があります。
このような課題によって、バックアップの頻度や完全性を妥協せざるを得なくなる可能性があります。しかしながら、データ保護という観点では妥協すべきではありません。バックアップの実施期間が長くなると、顧客は回避策を取らざるを得なくなることがあります。その場合、外部ツールの導入やバックアップ頻度の削減などが行われますが、結果として、組織全体で一貫したデータ保護戦略を維持できなくなる可能性があります。
では、GitLabがどのような方法でパフォーマンス上のボトルネックを特定し、解決策を見つけ、バックアップ時間を短縮するためにどのように導入したかを詳しく見ていきましょう。
GitLabのリポジトリバックアップ機能では、git bundle create
コマンドを使用しています。このコマンドは、ブランチやタグのようなすべてのオブジェクトと参照を含むリポジトリの完全なスナップショットをキャプチャします。このバンドルは、リポジトリを正確な状態で再作成するための復元ポイントとして機能します。
しかしながら、このコマンドの実装には参照数に関連するスケーラビリティの低さの問題があり、パフォーマンス上のボトルネックとなっていました。リポジトリ内の参照数が増えるにつれて、処理時間が飛躍的に増加していました。当社の一番大きなリポジトリでは数百万個の参照が含まれますが、バックアップ処理に48時間以上かかることもありました。
そこで、このパフォーマンス上のボトルネックの根本原因を特定するために、実行中のコマンドのフレームグラフを分析しました。
フレームグラフは、スタックトレースを使用してコマンドの実行パスを表示します。各バーはコード内の関数に対応しており、バーの幅はその特定の関数内でコマンドの実行に費やされた時間を示します。
10,000個の参照が含まれるリポジトリで実行されたgit bundle create
コマンドのフレームグラフを調べたところ、実行時間の約80%がobject_array_remove_duplicates()
関数に費やされていました。この関数は、コミットb2a6d1c686(バンドル:同じ参照を複数回指定できるように許可、2009年1月17日)でGitに導入されたものです。
この変更内容を理解するには、git bundle create
を使用すると、バンドルに含める参照を指定できることを把握する必要があります。完全なリポジトリバンドルの場合、--all
フラグを指定するとすべての参照がパッケージ化されます。
このコミットは、ユーザーがコマンドラインから重複した参照を指定(例:git bundle create main.bundle main main
)すると、重複したmain参照を適切に処理せずにバンドルが作成されてしまうという問題に対処したものでした。Gitリポジトリでこのバンドルをアンバンドルすると、同じ参照を二度書き込もうとするため、壊れてしまいます。そのため、重複回避を目的として、すべての参照で重複の特定処理が繰り返されるようにネストされたfor
ループを用いたコードが実装されました。このようなO(N²)アルゴリズムは、参照数が多いリポジトリではパフォーマンス上の重大なボトルネックとなり、かなりの処理時間がかかっていました。
GitLabは、明らかになったパフォーマンスの問題を解決するために、ネストされたループをマップデータ構造に置き換えるアップストリーム修正をGitにコントリビュートしました。これにより、各参照がマップに追加され、各参照の単一のコピーのみが処理目的で自動的に保持されるようになりました。
この変更により、git bundle create
のパフォーマンスが劇的に向上し、参照数の多いリポジトリのスケーラビリティが大幅に改善されました。10,000個の参照があるリポジトリでベンチマークテストを行った結果、パフォーマンスが6倍向上することが明らかになりました。
Benchmark 1: bundle (refcount = 100000, revision = master)
Time (mean ± σ): 14.653 s ± 0.203 s [User: 13.940 s, System: 0.762 s]
Range (min … max): 14.237 s … 14.920 s 10 runs
Benchmark 2: bundle (refcount = 100000, revision = HEAD)
Time (mean ± σ): 2.394 s ± 0.023 s [User: 1.684 s, System: 0.798 s]
Range (min … max): 2.364 s … 2.425 s 10 runs
Summary
bundle (refcount = 100000, revision = HEAD) ran
6.12 ± 0.10 times faster than bundle (refcount = 100000, revision = master)
パッチは承認され、アップストリームのGitにマージされました。GitLabでは、次のGitバージョンがリリースされる前にお客様がすぐに恩恵を受けられるように、この修正をバックポートしました。
この改善によるパフォーマンスの向上は、まさにこれまでの状況を一変させるものでした。
gitlab-org/gitlab
)のバックアップを従来のわずか1.4%の時間で作成できるようになりました。GitLabを利用する組織は、この改善によって、リポジトリのバックアップとディザスタリカバリ計画方法に関して、即座に次のような具体的なメリットを得られます。
組織は、パフォーマンスや完全性を犠牲にしなくても、より堅牢なバックアップ戦略を実施できるようになりました。以前は難しいトレードオフに直面していたものの、今では簡単な方法で運用できます。
GitLab 18.0のリリース以降、ライセンスプランに関係なく、GitLabをご利用のお客様は全員、ご紹介した改善点を利用してバックアップ戦略の立案および実施を行えるようになりました。設定を変更する必要はもうありません。
今回の革新的な改善は、スケーラブルでエンタープライズグレードなGitインフラの提供に向けた、当社の継続的な取り組みの一環です。バックアップの作成時間が48時間から41分に短縮されたことは重要なマイルストーンとなりましたが、引き続きスタック全体においてパフォーマンス上のボトルネックを特定し、対処しています。
今回の改善をGitのアップストリームプロジェクトにコントリビュートでき、GitLabユーザーだけでなく、広範なGitコミュニティにメリットをもたらせたことを特に誇りに思っています。このような協調的なアプローチにより、改善に対する徹底的なレビューおよび幅広いテストの実施が行われ、誰もがそのメリットを得られるようになります。
GitLabではパフォーマンスを最適化するために、このような深いレベルでインフラに取り組んでいます。ぜひGitLab 18のオンラインリリースイベントにご参加ください。ほかにどのような根本的な機能強化が行われたかをご紹介します。今すぐご登録ください!