オープンソース
Walgit – オブジェクトストアの前に単一バイナリを置いたGitサーバー
Walgit – a Git server that is one binary in front of an object store (github.com)
要約
Walgitは、データベースや永続的なローカル状態を持たないGitサーバーです。単一のバイナリを実行し、S3やGCSのようなオブジェクトストアをバックエンドとして指定するだけで、スマートHTTP(v0/v2)のfetch/push、バンドルURIクローン、Git LFS、Web UI、JSON API、プッシュポリシー、Webフックなどを提供します。これにより、リポジトリサイズのマシンサイズ制限を超えたスケーリングが可能になります。
全文翻訳
walgit — データベース、リーダー、ローカル状態を持たないGitサーバー
walgitは、データベース、リーダー、そして重要なローカル状態を持たずにGitリポジトリをホストします。単一のバイナリを実行し、S3またはGCSバケットを指し示すだけで、以下を提供します:スマートHTTP(v0/v2)のfetchとpush、静的ファイルとして提供されるバンドルURIクローン、Git LFS、ブラウジング可能なWeb UI、SDK付きJSON API、リポジトリごとのプッシュポリシー、Webフック — そして、それが実行されているマシンよりも大きなリポジトリにスケーリングできるサーバーです。walgitを実行するすべてのマシンは使い捨てのキャッシュであり、バケットがリポジトリとなります。
# 1. バケット(S3互換ストアまたはGCS)と設定
cat > walgit.toml <<'EOF'
[server]
listen = "0.0.0.0:8080"
public_url = "https://git.example.com"
auto_create_on_push = true
[server.auth]
mode = "token"
anonymous_read = false
tokens = [{"principal" = "me", "token_env" = "WALGIT_TOKEN_ME", "write" = true}]
[store]
backend = "s3"
bucket = "my-walgit"
[store.s3]
endpoint = "https://s3.us-east-1.amazonaws.com"
region = "us-east-1"
EOF
# 2. 実行
WALGIT_TOKEN_ME=$(openssl rand -hex 24)
walgit serve --config walgit.toml
# 3. 使用
— 新しい名前へのプッシュがリポジトリを作成します
git -c http.extraHeader="Authorization: Bearer $WALGIT_TOKEN_ME" push https://git.example.com/acme/app.git main
これがデプロイのすべてです。同じバケットを指す他のマシンを追加すると、それらは同じリポジトリを一貫して提供し、調整するものは何もありません。それらをすべて破壊しても、暖かさ以外は何も失われません。
これは、Git at any scale(彼らがContinuityと呼ぶシステム)で説明されているアーキテクチャのRust実装であり、リポジトリよりも小さいマシンで実行するために必要な変更が加えられています。その投稿は最初に読む価値があります。それはdocs/reference/cursor-git-at-any-scale.mdにそのまま保持されています。
なぜこの形状なのか
Gitは分散型であり、それがホスティングを一つの理由で苦痛にしています:packfilesです。リポジトリ内のすべては大きなバイナリパックに圧縮され、小さくなるように配置されており、順序通りに読み取られるように配置されていません。すべてのgit操作は数ギガバイトのランダムウォークです。ファイルがページキャッシュにあるラップトップでは問題ありませんが、ネットワークファイルシステム上では壊滅的です。そのため、「リポジトリをNFSに置くだけ」は、試みたすべての大規模ホストで失敗しました。生き残った設計(GitHubのSpokes)は、ローカルNVMeに実際のレポジトリを保持し、厳密な一貫性でパックファイルレベルでレプリケートします — これは、固定レプリカセット全体での3相コミット、各リポジトリをマシンにマッピングするデータベース、および多数のペットによって支払われます。Continuityの洞察は経済性を変えます:オブジェクトストレージにライトアヘッドログを真実の源とし、オンディスクリポジトリをキャッシュとします。プッシュはバケットに不変のオブジェクトとして保存され、小さなマニフェストがコンペア・アンド・スワップで書き換えられたときにのみ表示されます。そのCASがコンセンサスです — 選挙なし、クォーラムなし、プライマリなし。どのインスタンスもプッシュを受け入れることができます。競合する2つのインスタンスが両方とも勝つことはできません。リポジトリを一度も見たことのないレプリカはログを読み取り、それを取得します。読み取りは調整なしで一貫性があります。なぜなら、すべての読み取りは最初にストアに何か変更があったかどうかを尋ねるからです(条件付きGET、通常は304)。コンパクションはリースを保持している誰かによって一度実行され、ログに公開されるため、レプリカは再パックする代わりにコンパクションされたパックをダウンロードします。そしてWALが真実であるため、完全な来歴があります:プッシュと再パックのすべてが、任意の時点まで再生可能です。
walgitはそれをそのまま取り込み、小さなマシン上のモノレポに必要なものを追加します:参照とWebページを提供します(リポジトリのパックはインスタンスに収まりません)(HTTPレンジリクエスト経由のリモートリーダー)、コミットとツリーをローカルに保持し、ブロブはバケットに残します(履歴パック)、クローンバイトをサーバーから完全に移動させます(バンドルURI:新しいクローンとキャッチアップはバケットまたはCDNが配布する静的ファイルです)。
何をするか
gitスマートHTTP v0/v2:ls-refs(プレフィックス付き)、fetch(filter/shallow/deepen/sideband-all)、receive-pack(アトミック、削除、タグ、pushオプション、report-status-v2)、<owner>/<repo>名前空間、sha1およびsha256リポジトリ。アップストリームgitはupload-pack/repack/bundleを実行します。walgitはreceive-pack、WAL、および配管を実行します。
バンドルURI
カレンダースロット(毎週フル、デイリーチェーン、アワーリー)でカットされたバンドルは、WALの純粋な関数です:新しいクローンは最新のフルとそれに続くチェーンをバケットからダウンロードし、サーバーには残りの部分のみを要求します。キャッチアップは、それが逃したスロットのみをダウンロードします。リポジトリあたり2つのリスト:クローン用のbundles/list、フェッチ用のbundles/catchup。ブロブなしファミリー(--filter=blob:none)。
LFS
Batch API + 基本転送、バケット内のオブジェクト、インポートされたリポジトリ用のアップストリームLFSサーバーからのオプションの読み取りスルー。
Web UI + API
React UI(ツリー、ブロブ、コミット、差分、WAL自身のヘルスページ)は、/{owner}/{repo}/api/*の下の読み取り専用JSON API上にあります。SHAアドレス指定された回答は不変で、どこでもキャッシュされます。長い回答はSSEとして進捗をストリームします。repos.jsはページ、エージェント、スクリプト用の依存関係のないSDKです。
ポリシー
リポジトリごとのプッシュルール(policy.json):保護された参照、グループ、ファストフォワードのみ、バイパスリスト。docs/POLICY.md。
設定
リポジトリごとの設定(バンドルスケジュール、コンパクション、アップストリームフォロー)は、履歴とともにWALに公開されます。
イベント
小さなブリッジがWALをテールし、refイベントをWebフックにPOSTします。1つの(repo, seq, ref)あたり正確に1回、耐久性のあるカーソルで。docs/EVENTS.md。
メンテナンス
チェックポイント、バンドルビルド、幾何学的コンパクション、ベース再構築、接続監査と修復 — 設定とWALから望ましい状態を各パスで計算し、最も重要な不足している作業の1つの制限された単位を実行する1つのループ。構築による自己修復:障害は穴を残しません。削除されたアーティファクトは「紛失」し、同一に再構築されます。
認証
なし(ループバック)、トークン(静的トークン)、oidc(任意のOpenID Connect発行者:ブラウザサインイン、IDトークン、git用のwalgit発行アクセストークン)。/services/public/install.shは、開発者のマシンを1つの冪等なコマンドでセットアップします。
ストア
S3およびS3互換(AWS、MinIO、rustfs、R2、Cephなど)およびGCSを第一級として。テスト用のインメモリストア。
仕組み(概要)
リポジトリはバケット内のWALです。repos/<owner>/<repo>/の下にあります:manifest.pb(小さく、CASで書き換え済み:ヘッドシーケンス、ライブパックセット、チェックポイントポインタ、設定 — 線形化ポイント)、log/<seq>.pb(不変のエントリ:PUSH、COMPACT、CHECKPOINT、SETTINGS)、wal/<checksum>.pack|.idx|.rev|.bitmap|.commit-graph(不変のコンテンツアドレス指定されたパックとそのサイドファイル)、checkpoints/<seq>/(折りたたまれた参照スナップショット+パックインベントリなのでコールドスタートはスナップショット+テール)、bundles/、leases/(TTL付きCAS — 唯一のクロスインスタンスミューテックス)、policy.json、lfs/objects/、events/cursor.json。
プッシュ:receive-packはパックをインデックス化し(git index-pack --fix-thin --rev-index をスクラッチディレクトリで実行)、接続性とポリシーをチェックし、pack || idx || logエントリをアップロードし、マニフェストをCASします。412エラーの場合、再読み取りし、すべての参照の古い値を再検証して再試行します。1つのインスタンスでの1つのリポジトリへの同時プッシュは、1つのCASにグループコミットされます。クライアントは、バケットが確認した後でのみOKを見ます。
読み取り:マニフェストの1つの条件付きGET。304 → ローカルコピーから提供、200 → 新しいエントリを適用します。「適用」とは、リクエストが必要とするものによって異なります:参照(スナップショット+ログ → packed-refs、パックなし:広告、API、バンドルリスト)、serve(このマシンが保持できるパックセット:小さなパックと履歴パックをローカルに、大きすぎるベースはレンジで読み取る)、full(すべてローカル、再パック用)、objects(リモートリーダー、収まらないリポジトリのUI用)。パックダウンロードは独自のランタイムで実行され、参照リクエストをブロックしません。
配置は設定です。[placement] serve / maintain globsは、ホストがどのリポジトリでオブジェクト作業を行うかを指定します。参照レベルの読み取りはどこでも機能します。1つのボックス:デフォルトのままにします。複数:モノレポをSSDを持つホスト(cache.mode = "disk")に配置し、他はすべて小さいものに配置し、/<owner>/<repo>でルーティングします。何も静かに待つことはありません。何でもs