Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
go test を速くする
Search
sivchari
August 24, 2026
1.5k
4
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
go test を速くする
sivchari
August 24, 2026
More Decks by sivchari
See All by sivchari
govalid ~ Type-safe validation tool ~
sivchari
0
180
Go1.25 リリースパーティ ~ nil pointer bug ~
sivchari
0
170
Google Developer Group - DevFest Tokyo 2025
sivchari
0
180
Who tests the Tests ?
sivchari
0
220
Go 1.26 リリースパーティ
sivchari
0
260
静的解析 x Kubernetes API Conventions = Kube API Linter ~ ベストプラクティスに準拠したカスタムリソースの作り方と運用 ~
sivchari
0
270
What's GOCACHEPROG ?
sivchari
1
620
gh_extensionsによる快適なOSS生活.pdf
sivchari
0
210
Visualization Go scheduler by gosched-simulator
sivchari
1
710
Featured
See All Featured
We Analyzed 250 Million AI Search Results: Here's What I Found
joshbly
1
1.9k
Leadership Guide Workshop - DevTernity 2021
reverentgeek
1
370
The Art of Programming - Codeland 2020
erikaheidi
57
14k
Paper Plane (Part 1)
katiecoart
PRO
1
11k
Navigating Weather and Climate Data
rabernat
0
510
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
120
120k
Have SEOs Ruined the Internet? - User Awareness of SEO in 2025
akashhashmi
0
490
Being A Developer After 40
akosma
91
590k
Bash Introduction
62gerente
615
220k
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
9
1.5k
svc-hook: hooking system calls on ARM64 by binary rewriting
retrage
2
560
YesSQL, Process and Tooling at Scale
rocio
174
15k
Transcript
go test を速くする 2026-08-24 layerx.go #5 @sivchari
whoami @sivchari LayerX バクラク事業部 (2026 -) CI/CD・AI Agent 基盤などの開発基盤を担当 Go
Conference 主催 / golang.tokyo 運営 OSS kumo Kubernetes maintainer ArgoCD Envoy Gateway Kubernetes Contributor Award 2025 受賞 © LayerX Inc.
前提 LayerX の Go monorepo バクラクを支える巨大な Go monorepo 数千の package、多数のサービスが同居
PR のたびに全 package を go test するのは現実的ではない そこで affected package testing 変更されたファイルから「影響を受ける package」だけを算出してテストする 2023 年から存在する仕組み (by @izumin5210) © LayerX Inc. 3
前提 affected package testing の仕組み PR の変更ファイル一覧 git diff /
GitHub API ファイル → 所属 package 起点 package 集合 import の逆依存を辿る 影響を受ける package 集合 対象ディレクトリ配下だけに絞る go test $packages 影響のない package は実行しない © LayerX Inc. 4
入社時点のボトルネック 検出のために go list が走っていた 旧実装は golang.org/x/tools/go/packages (= go list
のラッパー) NeedImports は 3rd party module まで含めて依存を解決する go mod download か module cache の restore が事実上必須 「テストを減らすための検出」のために module 解決を待つのは本末転倒 © LayerX Inc. 5
観察 本当に必要な情報はなにか 欲しいのはリポジトリ内の import グラフだけ 3rd party の変更は go.mod /
go.sum の変更として別途扱えばよい グラフ構築時にリポジトリ外の import は捨てるだけ 型チェックも、ビルドも、module 解決も要らない Go の import 関係は ソースコードの import 宣言だけで決まる import 宣言は AST の入り口にある。そこまで読めば止まっていい © LayerX Inc. 6
解決 1 | AST 時点で落とす
解決 1 | AST 時点で落とす go/build.ImportDir だけで import を集める 各
.go を go/parser の parser.ImportsOnly モードでパースする package 節と import 宣言を読んだら、本文を読まずに即 return 型チェックも、コンパイルも、module 解決もネットワークも一切起きない 標準ライブラリだけで完結。 go list の起動すら不要 © LayerX Inc. 8
解決 1 | AST 時点で落とす 実装: ディレクトリを歩いて import を集める 本体の
import もテストの import も、区別せず同じグラフに入れる テストだけが使う package の変更も、同じ仕組みで拾える あとは変更ファイルの package から逆依存 map を辿るだけ © LayerX Inc. 9
解決 1 | AST 時点で落とす 結果: 検出は数十秒で終わる sparse checkout +
affected 検出まで含めて数十秒で完了 go mod download も module cache の restore も不要 「対象外 package のビルド (型チェック) すらしたくない」を実現 コンパイルの検証は別 workflow (go-build) が全量で担保する役割分担 検出側は import 解析だけに専念できる © LayerX Inc. 10
それでも test job は毎回数分かかっていた
解決 2 | パッケージ構成で依存を切る
解決 2 | パッケージ構成で依存を切る 前提: 全 service 入りの universal バイナリ
バクラクは複数 service からなる分散構成 service ごとにバイナリをビルド・デプロイするのは手間が大きい そこで全 service を 1 つに束ねた universal バイナリを採用 エントリポイントの server package が全 service を import する 起動時の指定で「どの service として動くか」を切り替えられる 詳細: connect-go による複数サービスの開発とユニバーサルバイナリによる改善 © LayerX Inc. 13
解決 2 | パッケージ構成で依存を切る server package の中身: 自動生成された「対応表」 server package
の中にある自動生成ファイル (コード上の名前は registry) 名前から初期化関数を引く表 → そのために全 service を import する 中身は import と map リテラルだけで、分岐もロジックも無い © LayerX Inc. 14
解決 2 | パッケージ構成で依存を切る 原因: 全 service を import する「対応表」
server package に同居していたもの 全 service を import する対応表 (自動生成) server 起動の logic テスト 逆依存グラフのハブになっていた どの service を 1 つ変えても affected に入る 全 service を埋め込むテストバイナリの link に数十秒 かかり、 しかもキャッシュが効かない © LayerX Inc. svc A svc B … svc N server package 対応表 + logic + テスト どの service を変えてもここが affected 15
解決 2 | パッケージ構成で依存を切る 対応表を、テストを持たない子 package に切り出す 対応表にテストしたいロジックは無い。問題はテストと同居していたこと Before server
package = 対応表 + logic + テスト → テストが全 service を link する After 対応表はテストを持たない子 package へ server package は logic のみ (service import ゼロ) テストは fake の対応表を注入して数秒に © LayerX Inc. ↓ 16
解決 2 | パッケージ構成で依存を切る package を分けただけで、link が消えた affected 検出のコードは 1
行も変えていない テストを持つ package が、全 service を import しなくなった 全 service を link するテストバイナリが、そもそも作られない テストを持たない package は compile されるだけ (build cache が効く) 効果 全 service の link 数十秒 → compile のみ数秒 test job の中央値が分単位で短縮 © LayerX Inc. 17
解決 3 | テストの無いビルドを消す
解決 3 | テストの無いビルドを消す テストの有無は、import を集めた時点で分かる go/build.Package はファイルと import を役割ごとに別フィールドで返す
フィールド GoFiles / Imports TestGoFiles / TestImports XTestGoFiles / XTestImports ImportDir © LayerX Inc. 中身 package 本体 package foo のテスト (internal test) package foo_test (external test) の戻り値に「テストを持つか」の情報が最初から含まれている 19
解決 3 | テストの無いビルドを消す テストが無い package はテスト対象にしない 追加の走査ゼロ・ go list
実行ゼロで判定できる 対応表は全 service を import したままなので、分離後も必ず affected に入る ただしテストを持たないので、入っても「コンパイルするだけ」 affected から除外し、コンパイル検証は go-build workflow に任せる テストを持たない package だけの run は test job を丸ごとスキップ 大型 runner + MySQL/Postgres/Redis 起動込みの数分が消える © LayerX Inc. 20
解決 4 | キャッシュの取り方を変える
解決 4 | キャッシュの取り方を変える tar を展開し終わるまで、テストは始まらない affected を絞っても、残ったテストは build cache
を復元してから始まる actions/cache 方式 = 数 GB の tar を実行前に全部展開する 実測 100s の内訳は、ダウンロード 4s に対して展開が 95s GOCACHEPROG なら、必要なものだけをその場で取れる © LayerX Inc. 22
解決 4 | キャッシュの取り方を変える リクエストは並行に飛んでくる GOCACHEPROG に渡すのは、自分で書いたプログラム go build は多数の
goroutine から並行に get / put を投げてくる リクエストには ID が振られており、レスポンスは順不同で返してよい (protocol の仕様) なので、リクエストごとに goroutine を起こして並行に処理できる © LayerX Inc. 23
解決 4 | キャッシュの取り方を変える 常駐プロセスを 1 つ立てて、S3 を集約する GOCACHEPROG は
go コマンド 1 回につき 1 プロセスだけ起動される テストは go run で wrapper を起動し、その中から go test を呼ぶ構成 1 ステップの中で go が何度も起動する。そのままだと毎回 S3 に繋ぎ直すことになる そこで役割を 2 つに分けた は CI job に 1 つだけ常駐し、S3 と手元の cache を持つ。起動直後から先読みする connect は GOCACHEPROG として起動され、socket で serve に取り次ぐだけ serve serve は go test の前のステップで起動する。そこから先読みが始まる warm job build cache を 64 個のチャンクに分ける © LayerX Inc. → S3 manifest + チャンク (tar.zst) × 64 → serve (常駐 1 つ) S3 と手元の cache を集約 → connect × 多数 go test の各プロセス 24
解決 4 | キャッシュの取り方を変える 分割で転送量は減らない。効いたのは待ち時間 Go は build cache を
action ID の先頭 1 バイトごとのディレクトリに置く (00〜ff の 256 個) warm job はこれを 4 個ずつまとめ、64 個のチャンクにして S3 に置く action ID は SHA-256 なので、どのディレクトリに入るかは一様に分布する 数千件のエントリを引けば 256 個すべてに当たる → ほぼ全部のチャンクが要る つまり転送量は tar 方式と変わらない 効いたのは、ダウンロード (ネットワーク) と展開 (CPU) をパイプライン化できること チャンクがその単位。細かすぎると 1 つあたりの overhead、粗いと並行度が落ちる 実測で 64 に決めた (32 では 293s、64 で 270s) © LayerX Inc. 25
効果 test job の実行時間 (P50 / P90) 解決策のリリース以降、Job ごとの P50
/ P90 がともに短縮 © LayerX Inc. 26
まとめ go test を速くする = やらないことを増やす 検出を速くする: 汎用ツールの便利さの代償を知る 欲しいのが import
グラフだけなら、AST の入り口だけ読めばいい グラフを小さくする: 逆依存のハブはパッケージ構成そのものを直して潰す 対象を減らす: 言語が構造として持つ情報を使い切る Test / XTest が分かれているので「テストの無い package」を追加コストなしで判定できる 全部展開しない: キャッシュも要る分だけその場で取る 1〜2 割の PR では test job 自体が走らない。走る分も同条件の実測で 373s → 270s © LayerX Inc. 27