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のキャッシュの仕組みにDeep Diveする
Search
KazukiHayase
December 01, 2025
Technology
220
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
go testのキャッシュの仕組みにDeep Diveする
KazukiHayase
December 01, 2025
More Decks by KazukiHayase
See All by KazukiHayase
なぜ Temporal の大小比較には compare しかないのか / Why Does Temporal Only Have compare() for Comparisons
kazukihayase
1
230
entのPrivacy機能とgo/astを使って、意図しないDBアクセスを防ぐ
kazukihayase
1
460
要件定義・デザインフェーズでもAIを活用して、コミュニケーションの密度を高める
kazukihayase
0
630
CIでのgolangci-lintの実行を約90%削減した話
kazukihayase
0
580
もし今からGraphQLを採用するなら
kazukihayase
13
6.1k
Goでテストをしやすくするためにやったこと
kazukihayase
1
960
GraphQLクライアントの技術選定 2023冬
kazukihayase
9
8k
Introduction and Insights of the Hasura-based Architecture
kazukihayase
0
1.2k
自分だけが頑張るのをやめて、フルスタックなチームを作る
kazukihayase
2
3.8k
Other Decks in Technology
See All in Technology
あけおめLINE 傾向とその対策
nasa9084
0
340
ADKで始める業務改善 - AIエージェント開発時の考えと設計
harappa80
2
300
Making AI Agents Safe and Fast- Jev, Obsidian, and the Meta-Harness
x5gtrn
PRO
0
150
2026-09-18 gotanda.sre Terraformで複数環境作ったり、複数Stateに分割したりそれとTerragrunt / Terraform multi envs and multi states
masasuzu
4
760
Oracle Cloud Infrastructure(OCI):Onboarding Session(はじめてのOCI/Oracle Supportご利⽤ガイド)
oracle4engineer
PRO
3
21k
GoのInterface内部構造から学ぶ!最高パフォーマンスを出すコード設計
yappli_developers
1
300
【Findyテック文化祭ワークショップ】新卒エンジニア&採用担当と作る、 なりたい姿と今やるべき一歩
dip_tech
PRO
0
120
AI Agent入門〜今更聞けないAgentの話〜
hiromimaganuma
0
120
Account Factory for Terraformによる 標準化されたアカウント発行の自動化
pensuke628
0
110
Databricksメトリクスビューはじめてのもくもく会
taka_aki
0
110
Amazon Bedrock Agents ClassicからAmazon Bedrock AgentCoreへ移行した際、ガードレール設定が2箇所に割れた話
matsunobu
0
140
地方移住と都心キャリアの両立は「金・時間・人」のリソースをフル活用すれば実現できる!〜Snowflake女子会 vol.8
snowwmn0824
0
130
Featured
See All Featured
Mobile First: as difficult as doing things right
swwweet
225
10k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
12
1.3k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
2.2k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
28
3.6k
Into the Great Unknown - MozCon
thekraken
41
2.7k
The B2B funnel & how to create a winning content strategy
katarinadahlin
PRO
1
540
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
120
120k
Amusing Abliteration
ianozsvald
1
320
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
16k
Designing Experiences People Love
moore
143
24k
Organizational Design Perspectives: An Ontology of Organizational Design Elements
kimpetersen
PRO
1
830
Building a A Zero-Code AI SEO Workflow
portentint
PRO
0
740
Transcript
go testのキャッシュの仕組みにDeep Diveする 2025.12.03 明日から使えるgo test実践テクニック集 #go_findy ©2025 Dr.'s Prime
,Inc.
自己紹介 @KazukiHayase 早瀬和輝(Kazuki Hayase) 2025年4月ドクターズプライム入社 リードエンジニア Go / TypeScript /
React(Next.js) / GraphQL 葬送のフリーレン2期がとても楽しみです ©2025 Dr.'s Prime ,Inc.
会社概要 ©2025 Dr.'s Prime ,Inc.
会社概要 ©2025 Dr.'s Prime ,Inc.
01 go testのキャッシュの仕組み 02 ソースコードの解説 03 CIでの活用 04 まとめ ©2025
Dr.'s Prime ,Inc.
1. go testのキャッシュの仕組み ©2025 Dr.'s Prime ,Inc.
go testのキャッシュの仕組み go testのキャッシュとは Go 1.10から導入された機能 テスト結果をパッケージ単位でキャッシュ 変更がない場合、テストをスキップして高速化 $ go
test ./... ok example.com/pkg1 0.123s ok example.com/pkg2 (cached) ok example.com/pkg3 0.456s ©2025 Dr.'s Prime ,Inc.
go testのキャッシュの仕組み キャッシュが利用される条件 ソースコード・テストコードが変更されていない コマンドライン引数が同じ 環境変数が同じ値 コード内で参照するファイルに変更がない ©2025 Dr.'s Prime
,Inc.
go testのキャッシュの仕組み キャッシュの利用判定の流れ 1. 前提条件のチェック 2. テスト引数の検証 キャッシュ可能なフラグのみが利用されているか 3. 2段階のキャッシュ検索
第1段階: testID の計算とテストログの取得 第2段階: testInputsID の計算と結果の取得 ©2025 Dr.'s Prime ,Inc.
2. ソースコードの解説 ©2025 Dr.'s Prime ,Inc.
ソースコードの解説 対象コード ソースコード内の cmd/go/internal/test パッケージ tryCacheWithID というメソッドがキャッシュの判定を担当 Goのバージョンは1.25 ©2025 Dr.'s
Prime ,Inc.
ソースコードの解説 注意点 簡単のためコードを一部省略・変更しています 正確なコードはGoの公式リポジトリで確認してください https://github.com/golang/go/blob/release- branch.go1.25/src/cmd/go/internal/test/test.go ©2025 Dr.'s Prime ,Inc.
ソースコードの解説 処理の流れ 1. 前提条件のチェック 2. テスト引数の検証 3. testID の計算 4.
testInputsID の計算 5. テスト結果の取得 ©2025 Dr.'s Prime ,Inc.
ソースコードの解説 1. 前提条件のチェック go test のように引数なしでは無効 func (c *runCache) tryCacheWithID(b
*work.Builder, a *work.Action, id string) bool { if len(pkgArgs) == 0 { // Caching does not apply to "go test", // only to "go test foo" (including "go test ."). if cache.DebugTest { fmt.Fprintf(os.Stderr, "testcache: caching disabled in local directory mode\n") } c.disableCache = true return false } // ...省略 } ©2025 Dr.'s Prime ,Inc.
ソースコードの解説 1. 前提条件のチェック モジュール、 GOPATH 、 GOROOT の配下にある必要がある if a.Package.Root
== "" { // Caching does not apply to tests outside of any module, GOPATH, or GOROOT. if cache.DebugTest { fmt.Fprintf(os.Stderr, "testcache ...\n", a.Package.ImportPath) } c.disableCache = true return false } ©2025 Dr.'s Prime ,Inc.
ソースコードの解説 2. テスト引数の検証 var cacheArgs []string for _, arg :=
range testArgs { i := strings.Index(arg, "=") switch arg[:i] { case "-test.benchtime", "-test.cpu", "-test.list", "-test.parallel", "-test.run", "-test.short", "-test.skip", "-test.timeout", "-test.failfast", "-test.v", "-test.fullpath": cacheArgs = append(cacheArgs, arg) default: c.disableCache = true return false } } ©2025 Dr.'s Prime ,Inc.
ソースコードの解説 2. テスト引数の検証 キャッシュ不可な引数が1つでもあれば無効化 run 、 timeout などはキャッシュ可能 coverprofile 、
outputdir は値が変わっても無効化しない それ以外の引数はキャッシュ不可 ©2025 Dr.'s Prime ,Inc.
ソースコードの解説 3. testIDの計算 testID とはテストバイナリとテスト引数から作られるハッシュ値 → ソースコードや引数が変更されると、この testID も変わる ©2025
Dr.'s Prime ,Inc.
ソースコードの解説 3. testIDの計算 h := cache.NewHash("testResult") // テストバイナリとテスト引数からハッシュ値を計算 fmt.Fprintf(h, "test
binary %s args %q execcmd %q", id, cacheArgs, work.ExecCmd) testID := h.Sum() if c.id1 == (cache.ActionID{}) { c.id1 = testID } else { c.id2 = testID } ©2025 Dr.'s Prime ,Inc.
ソースコードの解説 3. testIDの計算 計算した testID を元にテストログを取得 // Load list of
referenced environment variables and files // from last run of testID, and compute hash of that content. data, entry, err := cache.GetBytes(cache.Default(), testID) if !bytes.HasPrefix(data, testlogMagic) || data[len(data)-1] != '\n' { if cache.DebugTest { if err != nil { fmt.Fprintf(os.Stderr, "testcache: %s: input list not found: %v\n", a.Package.ImportPath, err) } else { fmt.Fprintf(os.Stderr, "testcache: %s: input list malformed\n", a.Package.ImportPath) } } return false } ©2025 Dr.'s Prime ,Inc.
ソースコードの解説 テストログとは テスト実行時に参照した環境変数とファイルの一覧を記録したもの API_KEY と config.json が参照された場合の例: getenv API_KEY open
/home/user/project/testdata/config.json stat /home/user/project/testdata ©2025 Dr.'s Prime ,Inc.
ソースコードの解説 4. testInputsIDの計算 テストログを元に、現在の環境変数とファイルからハッシュ値を計算 // data = テストログ testInputsID, err
:= computeTestInputsID(a, data) if err != nil { return false } ©2025 Dr.'s Prime ,Inc.
ソースコードの解説 4. testInputsIDの計算 func computeTestInputsID(a *work.Action, testlog []byte) (cache.ActionID, error)
{ testlog = bytes.TrimPrefix(testlog, testlogMagic) h := cache.NewHash("testInputs") // The runtime always looks at GODEBUG, without telling us in the testlog. fmt.Fprintf(h, "env GODEBUG %x\n", hashGetenv("GODEBUG")) pwd := a.Package.Dir for _, line := range bytes.Split(testlog, []byte("\n")) { if len(line) == 0 { continue } s := string(line) op, name, found := strings.Cut(s, " ") if !found { return cache.ActionID{}, errBadTestInputs } // テストログの各行を処理、次のスライドで解説 } sum := h.Sum() return sum, nil } ©2025 Dr.'s Prime ,Inc.
ソースコードの解説 4. testInputsIDの計算 switch op { case "getenv": fmt.Fprintf(h, "env
%s %x\n", name, hashGetenv(name)) case "chdir": pwd = name // always absolute fmt.Fprintf(h, "chdir %s %x\n", name, hashStat(name)) case "stat": if !filepath.IsAbs(name) { name = filepath.Join(pwd, name) } if a.Package.Root == "" || search.InDir(name, a.Package.Root) == "" { // Do not recheck files outside the module, GOPATH, or GOROOT root. break } fmt.Fprintf(h, "stat %s %x\n", name, hashStat(name)) case "open": if !filepath.IsAbs(name) { name = filepath.Join(pwd, name) } if a.Package.Root == "" || search.InDir(name, a.Package.Root) == "" { // Do not recheck files outside the module, GOPATH, or GOROOT root. break } fh, err := hashOpen(name) if err != nil { return cache.ActionID{}, err } fmt.Fprintf(h, "open %s %x\n", name, fh) } ©2025 Dr.'s Prime ,Inc.
ソースコードの解説 4. testInputsIDの計算 環境変数は存在しない場合は 0 、存在する場合は 1 +値をハッシュ化 func hashGetenv(name
string) cache.ActionID { h := cache.NewHash("getenv") v, ok := os.LookupEnv(name) if !ok { h.Write([]byte{0}) } else { h.Write([]byte{1}) h.Write([]byte(v)) } return h.Sum() } ©2025 Dr.'s Prime ,Inc.
ソースコードの解説 4. testInputsIDの計算 ファイルのハッシュ計算 func hashOpen(name string) (cache.ActionID, error) {
h := cache.NewHash("open") info, err := os.Stat(name) if err != nil { fmt.Fprintf(h, "err %v\n", err) return h.Sum(), nil } hashWriteStat(h, info) if info.IsDir() { // ...省略 } else if info.Mode().IsRegular() { if time.Since(info.ModTime()) < modTimeCutoff { return cache.ActionID{}, errFileTooNew } } return h.Sum(), nil } func hashWriteStat(h io.Writer, info fs.FileInfo) { fmt.Fprintf(h, "stat %d %x %v %v\n", info.Size(), uint64(info.Mode()), info.ModTime(), info.IsDir()) } ©2025 Dr.'s Prime ,Inc.
ソースコードの解説 4. testInputsIDの計算 ファイルサイズと更新時刻をハッシュ化 ファイルサイズが大きい場合を考慮し、内容全体のハッシュ化は避ける 2秒以内に更新されたファイルはキャッシュ拒否 ©2025 Dr.'s Prime ,Inc.
ソースコードの解説 5. テスト結果の取得 testID と testInputsID を組み合わせた最終キーで結果を取得 キャッシュの有効期限が切れている場合は無効化 // Parse
cached result in preparation for changing run time to "(cached)". // If we can't parse the cached result, don't use it. data, entry, err = cache.GetBytes(cache.Default(), testAndInputKey(testID, testInputsID)) if entry.Time.Before(testCacheExpire) { return false } ©2025 Dr.'s Prime ,Inc.
ソースコードの解説 5. テスト結果の取得 キャッシュがヒットした場合、実行時間を (cached) に書き換えて出力 // Committed to printing.
c.buf = new(bytes.Buffer) c.buf.Write(data[:j]) c.buf.WriteString("(cached)") for j < len(data) && ('0' <= data[j] && data[j] <= '9' || data[j] == '.' || data[j] == 's') { j++ } c.buf.Write(data[j:]) return true ©2025 Dr.'s Prime ,Inc.
ソースコードの解説 処理の流れ 1. 前提条件のチェック 2. テスト引数の検証 3. testID の計算 4.
testInputsID の計算 5. テスト結果の取得 ©2025 Dr.'s Prime ,Inc.
3. CIでの活用 ©2025 Dr.'s Prime ,Inc.
CIでの活用 キャッシュが利用される条件 ソースコード・テストコードが変更されていない コマンドライン引数が同じ 環境変数が同じ値 コード内で参照するファイルに変更がない → CI環境でも同様にキャッシュが利用可能 ©2025 Dr.'s
Prime ,Inc.
CIでの活用 CIでキャッシュを利用する際の注意点 テスト実行時に実際に参照されたファイルのみを変更を検知 ソースコードと参照ファイル以外の変更は検知されない e.g. 設定ファイル、マイグレーションファイル ©2025 Dr.'s Prime ,Inc.
CIでの活用 CIでキャッシュを利用する際の注意点 検知するべき変更を検知できず、偽陰性が発生する可能性がある テストが通るが、実際には問題がある状態 Goのキャッシュ機構で検知できない変更は、別途対策が必要 ©2025 Dr.'s Prime ,Inc.
CIでの活用 条件付きキャッシュクリア Goファイル以外に変更がある場合、キャッシュをクリアする -count=1 で実行すると、その結果はキャッシュされないため注意 ©2025 Dr.'s Prime ,Inc.
CIでの活用 条件付きキャッシュクリア name: Clean Go Cache Conditionally runs: using: "composite"
steps: - name: Check non go file changes id: changes uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 with: predicate-quantifier: 'every' filters: | has-not-go-file: - '!**/*.go' - '!**/*.mod' - '!**/*.sum' - name: Clean test cache if needed shell: bash run: | if [ "${{ steps.changes.outputs.has-not-go-file }}" == "true" ] || [ "${{ github.ref }}" == "refs/heads/main" ]; then echo "Cleaning test cache due to non-go file changes or main branch" go clean -testcache else echo "Skipping cache clean - only go files changed and not on main branch" fi ©2025 Dr.'s Prime ,Inc.
CIでの活用 条件付きキャッシュクリア jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955
# v4.3.0 - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 with: go-version-file: 'go.mod' - name: Clean cache conditionally uses: ./.github/actions/clean-go-cache - name: Run tests run: go test -v ./... ©2025 Dr.'s Prime ,Inc.
CIでの活用 キャッシュ利用によるCIの高速化 CIの実行時間が9分→3.5分に短縮 約60%の時間削減に成功 対応自体もシンプルで、簡単に導入可能 ©2025 Dr.'s Prime ,Inc.
4. まとめ ©2025 Dr.'s Prime ,Inc.
まとめ go test のキャッシュは testID と testInputsID で厳密にチェック テストバイナリだけでなく、参照した環境変数・ファイルの変更も検知 CI環境でもキャッシュを利用することで、手軽にCIの高速化を実現
©2025 Dr.'s Prime ,Inc.
We Are Hiring! ぜひドクターズプライムの魅力を覗きにきてください! ©2025 Dr.'s Prime ,Inc.