Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

GitHubの改善の核心は、Kafkaを導入したことだけではありません。プッシュ後に動く処理を、依存関係や順序性、リトライ要件、所有者に応じて分け、独立したジョブへ振り分けたことです。GitHub公式ブログによると、この変更で「意図した処理がすべて失敗なく完了したプッシュ」の割合は、旧方式の約99.897%から、新方式の最悪ケース推定で99.999%になりました。これはGitHub全体の稼働率やSLAではなく、記事で定義された独自指標です。

GitHubで「プッシュ処理」と呼ばれるもの

Gitへのプッシュは、リモートの参照を更新するだけでは終わりません。受け入れ後には、プルリクエストの同期、プッシュWebhookの送信、GitHub Actionsワークフローの起動、GitHub Pagesの公開、Codespaces設定の更新など、さまざまな後続処理が発生します。GitHubの2024年6月14日付の公式記事によると、プッシュに直接反応するロジックは60以上あり、20の異なるサービスにまたがっていました。

ここで扱うのは、Gitオブジェクトを受け入れる仕組みそのものではなく、受け入れ後の派生処理をどう調整し、確実に実行するかです。

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

旧方式では、処理がひとつの長いジョブに集中していた

RepositoryPushJobが逐次処理を担う

従来、多くのプッシュ後処理はRuby on Railsモノリス内の単一のバックグラウンドジョブ、RepositoryPushJobにまとめられていました。処理が長い逐次チェーンになっていたため、ある作業が遅れると後続の作業も待つことになります。GitHubの記事では、プルリクエスト同期などのユーザー向け処理に、1秒以上の不要な待ち時間が生じる場合があったと説明しています。

ひとつのリトライが無関係な処理まで巻き戻す

ジョブ全体を再試行すると、失敗した処理だけでなく、それ以前に済んだ処理も再実行されます。しかし処理ごとに、望ましい再試行の仕方は異なります。たとえば、Pushレコードのデータベース書き込みは遅れて再試行できる場合がある一方、時間が経ったWebhookの再送は望ましくないことがあります。再実行による重複副作用にも備えなければなりません。

エラー捕捉が失敗を見えにくくする

一つの処理でジョブ全体が止まらないよう、広い範囲でエラーを捕捉する設計になっていました。その結果、個別処理が失敗してもジョブ自体は完了したように見え、必要な後続処理が抜けるおそれがありました。

処理間に暗黙のデータベース依存があった

RepositoryPushJobは早い段階でPushes MySQLクラスタに書き込んでいました。そのため、後続処理もそのクラスタの状態に影響されました。GitHubの記事では、プルリクエスト同期自体は同クラスタへの明示的な接続を必要としないにもかかわらず、クラスタ障害で同期が失敗したインシデントが紹介されています。ひとつのジョブ内に並んでいるだけの処理が、実際には不要な依存関係まで共有していた例です。

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

分割の基準は機能名ではなく、運用上の境界

GitHubは、処理を単純に一機能一サービスへ切り分けたわけではありません。記事の説明から読み取れる設計上のポイントは、所有者、依存関係、順序性、再試行の性質を踏まえて、運用上まとまりのある処理をグループ化したことです。

  • 依存先:同じデータベースや外部APIに依存したままなら、ジョブを分けても障害の影響範囲は残ります。
  • 順序性:先行処理の完了を必要とする作業と、独立して動かせる作業を区別します。
  • リトライと冪等性:重複実行しても安全な書き込み、一時障害を再試行したい呼び出し、再送時期に注意が必要なWebhookを同じ方針にしないことが重要です。
  • 所有者:コードの担当と障害対応の担当が明確につながるようにします。
  • 障害半径:一つの処理が失敗したときに、どの機能まで止まるかを見ます。

Kafkaイベントから独立したジョブへ振り分ける

新方式では、プッシュごとにKafkaイベントを発行し、複数の独立したコンシューマーがそれを受け取って、対応するバックグラウンドジョブをエンキューします。Kafkaは一つの長い処理を順番に流す場所ではなく、プッシュという事実を複数の独立した処理へ配信するイベント基盤として使われています。

Git push
   ↓
Push event
   ↓
Kafka topic
   ├─ Consumer A → Pull request sync job
   ├─ Consumer B → Webhook dispatch job
   ├─ Consumer C → Actions trigger job
   ├─ Consumer D → Pages publishing job
   └─ Consumer E → Other push-processing jobs

一つのコンシューマーやジョブが遅れても、他のコンシューマーの処理を直接止めにくくなります。また、ある作業の再試行が、無関係な作業を最初からやり直す必要もなくなります。これはRailsモノリス全体を捨てたという話ではなく、モノリス内に集中していたプッシュ後処理の流れを、イベントと独立ジョブを中心に再構成したものです。

Kafka以外にも必要だった基盤

イベント発行の信頼性

プッシュを受け入れたのにKafkaイベントが発行されなければ、後続処理が始まりません。イベント発行の信頼性は、ファンアウト方式の前提です。GitHubの記事は具体的な実装方式を明らかにしていないため、Transactional OutboxやCDCを採用したとは断定できません。自社で設計する場合は、プッシュの確定とイベント発行の食い違いをどう検出・回復するかを決める必要があります。

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

専用ワーカープール

処理を分割すると、ジョブの数や並列度が増えます。GitHubは新しいジョブキュー向けに専用のワーカープールを用意しました。キューを分けても、ワーカー資源を他の処理と奪い合えば、遅延や障害の分離が不十分になることがあります。

ジョブ単位の可観測性

小さなジョブに分けることで、処理のどこで問題が起きたかを個別に監視しやすくなります。自社で同様の構成を作るなら、イベント発行、コンシューマーの遅延、エンキュー、ジョブの成功・再試行・最終失敗、プッシュから完了までの時間を追跡するのが実務上の出発点です。これらは運用のための提案であり、GitHubが公開した具体的なメトリクス名や閾値ではありません。

イベント単位の機能フラグ

GitHubは新旧パイプライン間で、イベント単位の一貫した機能フラグを使い、段階的なロールアウトと必要時のロールバックを可能にしました。イベント駆動処理では、同じイベントを新旧両方に送れば重複実行が起き、どちらにも送らなければ処理が欠けるおそれがあります。切り替えの単位をイベントごとに保つことが、この移行リスクを抑えるうえで重要です。

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

GitHubが報告した成果と、その数値の読み方

GitHub公式記事は、新パイプラインで1日約3億件のプッシュ処理オペレーションを実行し、所有権を1チームから15以上の適切なサービス所有者へ分散したと報告しています。約3億件は処理オペレーションの数であり、単純なGit pushの件数と同じとは限りません。記事はまた、直近30日間に850万人のユーザーから約5億プッシュがあった規模を示しています。

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
指標 旧方式 新方式
GitHub独自の「意図した処理がすべて失敗なく完了したプッシュ」の割合 約99.897% 最悪ケースの推定で99.999%

この定義に基づけば、未完了の割合は約0.103%から約0.001%へ下がり、概算で約103分の1です。これは記事にある数値からの計算であり、一般的な可用性SLA、GitHub全体の稼働率、Webhook配信保証、Actionsの成功率を示すものではありません。

同じ考え方を自社のジョブ処理へ適用する

  1. 巨大ジョブの中身を棚卸しする。各処理の目的、所有者、依存先、副作用、完了条件を書き出します。
  2. 依存関係と順序を図にする。本当に先行処理を待つ必要があるものと、同じジョブに置かれているだけのものを分けます。
  3. 再試行方針を処理ごとに決める。一時障害、恒久的エラー、時間制約のある送信を区別し、重複実行に耐える冪等キーや一意制約を検討します。
  4. イベントの欠落・重複・順序逆転を扱う。イベント発行の整合性を確かめ、同一リポジトリやブランチで古いイベントが新しい状態を上書きしないか検証します。GitHub記事は順序依存性を設計上の観点として挙げていますが、具体的な順序保証方式は公開していません。
  5. 部分的な成功を記録できるようにする。複数の派生処理を一括ロールバックするのではなく、各処理の状態を追跡し、失敗したものだけを安全に再試行できるようにします。
  6. 切り替え前に監視とロールバック条件を定める。コンシューマーラグ、失敗率、完了時間、重複や欠落の兆候を観測し、イベント単位で新旧経路を切り替えられるようにします。
  7. 小さな処理から移行する。影響を確認しながら対象を広げ、障害時にどの経路を止め、どの処理を再実行するかを明確にします。

分割が向くケースと、過剰設計になるケース

一つのジョブが多数の責務を抱え、失敗した処理だけを再実行できず、遅延や暗黙の依存関係がユーザー向け機能へ波及しているなら、処理境界の見直しは有効です。一方、後続処理が少なく、イベント量も低く、失敗時に安全に全体をやり直せる小規模システムでは、Kafkaの運用、スキーマ管理、分散トレーシング、再処理設計が追加負担になり得ます。その場合は、既存ジョブの内部分割や単純なキュー、データベースOutboxから始める選択肢もあります。

イベントを分けても、全ジョブが同じデータベースや外部サービスに依存すれば障害半径は縮まりません。また、パーティション数、保持期間、レプリケーション係数、Exactly-once設定など、GitHubのKafka構成の詳細は記事に記載されていません。自社で採用する際は、製品選定より先に、処理ごとの責務、失敗時の振る舞い、順序要件、復旧手順を設計する必要があります。

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.