それぞれの変更はテストに通ったのに、二つを取り込むと動かなくなる。ソフトウェアの開発では、変更そのものだけでなく、組み合わせた状態も確かめる必要があります。後から取り込む変更を、古い状態のまま試した結果では、その違いが抜け落ちるためです。
この問題を扱うのが、Zuulのゲーティングです。コードを本流へ取り込む「マージ」の前に、必要な試験を通す仕組みを指します。OCP Korea Tech DayでOpenInfra Korea User GroupのSeongsoo Cho氏が紹介したZuulについて、公式文書を重ね、変更の順番と再試験の関係を見ていきます。[1][2]
簡単にまとめると?
- Zuulは、前の変更も取り込まれると仮定し、後ろの変更をその組み合わせで先にテストします。
- 前の変更が失敗すれば、それを除いた状態で後続を試し直します。ただし、失敗した変更を本当に必要とする変更は、そのまま先へ進めません。
- 関連するリポジトリの範囲、試験内容、先に試す変更の数を合わせて設計します。並列化すれば必ず速くなるわけではありません。
「単独で通る」と「一緒に通る」の間
仕組みを理解するための例として、送信側が一度に8件を送り、受信側は16件まで受け取れる処理を考えます。変更Aで送信を12件に増やしても、現在の受信上限16件なら収まります。別の変更Bで受信上限を10件に下げても、現在の送信件数8件なら収まります。
ところがAとBを合わせると、12件を送る先の上限は10件です。各変更を元の構成に対して試すだけでは、この組み合わせを確かめたことになりません。これは説明用の仮例で、特定製品の試験結果ではありません。
一つ取り込むたびに次の変更を試せば、順番に沿った確認はできます。ただ、試験が長いほど待ち行列が伸びます。Zuulのdependent pipeline managerは、この順序を保ちながら試験を並行させる役割を持ちます。[3]
前の変更が通ると仮定して、先に試す
変更A、B、Cがこの順でキューへ入ったとします。キューは、取り込みを待つ変更の列です。ZuulはAだけ、AとB、AとBとCという組み合わせを作り、利用できる実行資源の範囲で並行して試験します。これがspeculative execution(先行する変更の成功を仮定した実行)です。まだ確かめていない変更を本流へ先に入れる、という意味ではありません。[2][3]
| 対象の変更 | 試験する状態 |
|---|---|
| A | 現在の本流 + A |
| B | 現在の本流 + A + B |
| C | 現在の本流 + A + B + C |
この表は公式文書の説明を三つの変更に整理したものです。Bを試す環境にAが入っているため、Bだけを古い本流に載せた試験とは、結果の意味が違います。後続の試験が早く終わっても、先行する変更の合否を無視してよいわけではありません。[2]
失敗したら、何を除いて試し直すか
先頭のAが失敗すると、Aを含めて試したBとCの結果は、そのまま使えません。単に順番待ちしていた独立した変更なら、Aを列から外し、Bは「現在の本流+B」、Cは「現在の本流+B+C」で試し直します。前提となる組み合わせが変わったからです。[3]
ここで区別したいのが、順番の都合で先行変更を含めることと、機能としてその変更を必要とすることです。例えば、共通ライブラリーへ新しい関数を加える変更Aと、その関数を呼ぶサービス側の変更Bは、Aを除いてBだけ進められません。Zuulでは、変更URLを指定するDepends-Onで、こうしたリポジトリをまたぐ依存を明示できます。依存先が失敗すれば、その依存先とそれに依存する変更はキューから外され、依存先が取り込まれるまでは依存する側も取り込めません。[2]
「失敗したものを飛ばす」と一律に考えると、この違いを見失います。再試験で進められる変更と、前提の修正を待つ変更を分けるためにも、コード上の依存を記録しておく必要があります。
一緒に試す範囲を、リポジトリの境界と照合する
リポジトリは、ソフトウェアや設定の変更履歴を保存する単位です。サービスと共通ライブラリーを別のリポジトリで管理しているなら、それぞれの単体試験だけでは、組み合わせの不整合を見逃すことがあります。
Zuulのproject.queueでは、試験で相互に関わるプロジェクトへ同じ共有キューを指定します。dependent pipelineでは、その列の先行変更を含めて後続を試します。共有キューの設定自体は、試しの変更として置くだけでは有効にならず、設定をマージしておく必要があります。[4]
queue.typeをper-branchとしてブランチ名ごとにキューを分ける場合には、関連プロジェクトのブランチ名をそろえる条件もあります。名前が違えば、一緒に試したい変更が別のキューへ入るためです。また、複数のリポジトリを同じキューへ置いても、複数のマージが一つの不可分な操作になるわけではありません。公式文書は、循環する依存を許可した場合でも、一部だけ取り込まれる失敗があり、人の介入が必要になり得ると説明しています。[5]
共有する範囲は、部署や組織の大きさより、実際に一緒に試す必要のある関係から考えます。無関係な変更まで同じ列へ集めると、ある変更の失敗によって、試し直す範囲も広がり得ます。
待ち時間だけでなく、試し直す量も見る
並行して試す変更を増やせば、すべて通るときは待ち時間を減らせます。一方、先行変更が頻繁に失敗する環境では、後続の試験をやり直す負担が増えます。Zuulのwindowは、キューの先頭から何件の変更について試験を開始できるかを制御します。これは、個々のテストジョブの本数と同じ数ではありません。[3]
導入を考えるなら、まず関連する二つのリポジトリと、それらを組み合わせる試験を一組選びます。通常の変更が通る場合に加え、先行変更が失敗する場合、明示した依存先が失敗する場合に、どの結果が破棄され、どの変更が止まるかを見る。待ち時間の短縮と、再試験に使う時間・実行資源を合わせて比べるための確認です。
ゲーティングが整えるのは、変更を試す状態と取り込みの条件です。用意していない試験の不具合まで自動で見つけたり、本番への配備が安全であると保証したりするものではありません。早い段階で結果を返す試験、取り込みを決める試験、取り込み後の配備を区別したうえで、どの組み合わせを合格とするかを設計することが、Zuulを使う出発点になります。[3]
参考資料
- Seongsoo Cho/OpenInfra Korea User Group「Open Infrastructure: Global Trends」、2026 OCP Korea Tech Day、全36頁。Zuulの紹介はpp.23–25。OCP公式資料一覧。
- Zuul contributors「Project Gating」。並行試験、再試験、共有キュー、明示的な変更依存。
- Zuul contributors「Pipeline」。independent/dependent/serialの役割、pipeline.window。
- Zuul contributors「Project」。project.queueと設定の有効化。
- Zuul contributors「Queue」。ブランチ単位の区分と、循環依存を扱う際の制約。
資料確認日:2026年10月12日。公式文書のlatest版を参照しています。試験環境を動かした検証や、講演に登場する個別企業の改善効果の検証は行っていません。
