perf(ci): stabilize file mtimes for Go test cache#19395
Conversation
|
Skipping CI for Draft Pull Request. |
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- Consider restricting the
git ls-filesfilter (e.g., to*.goand known testdata paths) to avoid unnecessarily touching large/binary/irrelevant tracked files and potentially interfering with tools that rely on mtimes for non-code assets. - It may be safer to make the fixed timestamp explicitly UTC-aware (e.g.,
TZ=UTC touch -t 200101010000ortouch -d '2001-01-01T00:00:00Z') to avoid any subtle differences across runners with different default timezones.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Consider restricting the `git ls-files` filter (e.g., to `*.go` and known testdata paths) to avoid unnecessarily touching large/binary/irrelevant tracked files and potentially interfering with tools that rely on mtimes for non-code assets.
- It may be safer to make the fixed timestamp explicitly UTC-aware (e.g., `TZ=UTC touch -t 200101010000` or `touch -d '2001-01-01T00:00:00Z'`) to avoid any subtle differences across runners with different default timezones.
## Individual Comments
### Comment 1
<location path=".github/actions/cache-go-dependencies/action.yaml" line_range="68" />
<code_context>
+ # Risk: if a non-.go testdata file changes content but not size between
+ # commits, the test cache could return a stale result. This is extremely
+ # rare and Go's own test cache has the same inherent limitation.
+ git ls-files -z | xargs -0 touch -t 200101010000
+ shell: bash
</code_context>
<issue_to_address>
**issue (bug_risk):** The fixed timestamp may still differ between runners due to local timezone interpretation.
`touch -t 200101010000` uses the runner’s local timezone. If different runners use different timezones (e.g., self‑hosted vs GitHub‑hosted), the resulting mtimes/epoch values will differ and the Go test cache may still miss across runners. To keep mtimes stable, force UTC (e.g., `TZ=UTC git ls-files -z | xargs -0 touch -t 200101010000`) or use a UTC `-d '2001-01-01T00:00:00Z'` style argument where available.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
|
Images are ready for the commit at 2087aec. To use with deploy scripts, first |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #19395 +/- ##
==========================================
- Coverage 49.67% 49.66% -0.01%
==========================================
Files 2748 2748
Lines 207354 207354
==========================================
- Hits 102996 102986 -10
- Misses 96704 96712 +8
- Partials 7654 7656 +2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
05f5ad5 to
21752e5
Compare
21752e5 to
f13f6a2
Compare
f13f6a2 to
38d32df
Compare
Go's test cache computes a test binary ID that includes -X ldflags. Since -X MainVersion and -X GitShortSha change on every commit, the test binary ID changes too, causing 100% test cache misses (Phase 1). Fix: generate pkg/version/internal/zversion.go with an init() function instead of passing version data via -X ldflags. For tests, use only the base tag (e.g. "4.11.x") which is stable across commits. For builds, use the full git-describe version. This eliminates -X ldflags entirely from the link step, stabilizing the link ActionID. Result: test binary IDs are constant across commits (fixing Phase 1), and build cache hits improve from ~0% to ~99.9% (only 2/4403 packages recompile — version packages whose output is byte-identical anyway). Also adds -buildvcs=false to disable Go 1.18+'s unused VCS stamping, which otherwise adds git state to every binary. This is the companion to PR #19395 (mtime fix for Phase 2). Together they enable 4.5-52x test speedup. Addresses concerns from PR #15609: -coverprofile caching is fixed in Go 1.25 (we're on 1.25.5), and GOTAGS=release shares 93% of cache entries with default — separation is counterproductive. Partially generated by AI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Go's test cache computes a test binary ID that includes -X ldflags. Since -X MainVersion and -X GitShortSha change on every commit, the test binary ID changes too, causing 100% test cache misses (Phase 1). Fix: generate pkg/version/internal/zversion.go with an init() function instead of passing version data via -X ldflags. For tests, use only the base tag (e.g. "4.11.x") which is stable across commits. For builds, use the full git-describe version. This eliminates -X ldflags entirely from the link step, stabilizing the link ActionID. Result: test binary IDs are constant across commits (fixing Phase 1), and build cache hits improve from ~0% to ~99.9% (only 2/4403 packages recompile — version packages whose output is byte-identical anyway). Also adds -buildvcs=false to disable Go 1.18+'s unused VCS stamping, which otherwise adds git state to every binary. This is the companion to PR #19395 (mtime fix for Phase 2). Together they enable 4.5-52x test speedup. Addresses concerns from PR #15609: -coverprofile caching is fixed in Go 1.25 (we're on 1.25.5), and GOTAGS=release shares 93% of cache entries with default — separation is counterproductive. Partially generated by AI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Go's test cache computes a test binary ID that includes -X ldflags. Since -X MainVersion and -X GitShortSha change on every commit, the test binary ID changes too, causing 100% test cache misses (Phase 1). Fix: generate pkg/version/internal/zversion.go with an init() function instead of passing version data via -X ldflags. For tests, use only the base tag (e.g. "4.11.x") which is stable across commits. For builds, use the full git-describe version. This eliminates -X ldflags entirely from the link step, stabilizing the link ActionID. Result: test binary IDs are constant across commits (fixing Phase 1), and build cache hits improve from ~0% to ~99.9% (only 2/4403 packages recompile — version packages whose output is byte-identical anyway). This is the companion to PR #19395 (mtime fix for Phase 2). Together they enable 4.5-52x test speedup. Addresses concerns from PR #15609: -coverprofile caching is fixed in Go 1.25 (we're on 1.25.5), and GOTAGS=release shares 93% of cache entries with default — separation is counterproductive. Partially generated by AI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@sourcery-ai review |
There was a problem hiding this comment.
Hey - I've left some high level feedback:
- Consider constraining the
git ls-filesscope (e.g., excluding large generated/vendor directories if not needed for tests) to avoid unnecessarytouchoperations and minimize overhead on large repos. - If tests rely on untracked/generated files (e.g., testdata produced at build time), note that these files will still have non-deterministic mtimes and bypass the cache; you may want to either document this explicitly in the comment block or extend the approach to cover those paths if relevant.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Consider constraining the `git ls-files` scope (e.g., excluding large generated/vendor directories if not needed for tests) to avoid unnecessary `touch` operations and minimize overhead on large repos.
- If tests rely on untracked/generated files (e.g., testdata produced at build time), note that these files will still have non-deterministic mtimes and bypass the cache; you may want to either document this explicitly in the comment block or extend the approach to cover those paths if relevant.Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Go's test cache computes a test binary ID that includes -X ldflags. Since -X MainVersion and -X GitShortSha change on every commit, the test binary ID changes too, causing 100% test cache misses (Phase 1). Fix: generate pkg/version/internal/zversion.go with an init() function instead of passing version data via -X ldflags. For tests, use only the base tag (e.g. "4.11.x") which is stable across commits. For builds, use the full git-describe version. This eliminates -X ldflags entirely from the link step, stabilizing the link ActionID. Result: test binary IDs are constant across commits (fixing Phase 1), and build cache hits improve from ~0% to ~99.9% (only 2/4403 packages recompile — version packages whose output is byte-identical anyway). This is the companion to PR #19395 (mtime fix for Phase 2). Together they enable 4.5-52x test speedup. Addresses concerns from PR #15609: -coverprofile caching is fixed in Go 1.25 (we're on 1.25.5), and GOTAGS=release shares 93% of cache entries with default — separation is counterproductive. Partially generated by AI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Go's test cache computes a test binary ID that includes -X ldflags. Since -X MainVersion and -X GitShortSha change on every commit, the test binary ID changes too, causing 100% test cache misses (Phase 1). Fix: generate pkg/version/internal/zversion.go with an init() function instead of passing version data via -X ldflags. For tests, use only the base tag (e.g. "4.11.x") which is stable across commits. For builds, use the full git-describe version. This eliminates -X ldflags entirely from the link step, stabilizing the link ActionID. Result: test binary IDs are constant across commits (fixing Phase 1), and build cache hits improve from ~0% to ~99.9% (only 2/4403 packages recompile — version packages whose output is byte-identical anyway). This is the companion to PR #19395 (mtime fix for Phase 2). Together they enable 4.5-52x test speedup. Addresses concerns from PR #15609: -coverprofile caching is fixed in Go 1.25 (we're on 1.25.5), and GOTAGS=release shares 93% of cache entries with default — separation is counterproductive. Partially generated by AI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Go's test cache computes a test binary ID that includes -X ldflags. Since -X MainVersion and -X GitShortSha change on every commit, the test binary ID changes too, causing 100% test cache misses (Phase 1). Fix: generate pkg/version/internal/zversion.go with an init() function instead of passing version data via -X ldflags. For tests, use only the base tag (e.g. "4.11.x") which is stable across commits. For builds, use the full git-describe version. This eliminates -X ldflags entirely from the link step, stabilizing the link ActionID. Result: test binary IDs are constant across commits (fixing Phase 1), and build cache hits improve from ~0% to ~99.9% (only 2/4403 packages recompile — version packages whose output is byte-identical anyway). This is the companion to PR #19395 (mtime fix for Phase 2). Together they enable 4.5-52x test speedup. Addresses concerns from PR #15609: -coverprofile caching is fixed in Go 1.25 (we're on 1.25.5), and GOTAGS=release shares 93% of cache entries with default — separation is counterproductive. Partially generated by AI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Go's test cache computes a test binary ID that includes -X ldflags. Since -X MainVersion and -X GitShortSha change on every commit, the test binary ID changes too, causing 100% test cache misses (Phase 1). Fix: generate pkg/version/internal/zversion.go with an init() function instead of passing version data via -X ldflags. For tests, use only the base tag (e.g. "4.11.x") which is stable across commits. For builds, use the full git-describe version. This eliminates -X ldflags entirely from the link step, stabilizing the link ActionID. Result: test binary IDs are constant across commits (fixing Phase 1), and build cache hits improve from ~0% to ~99.9% (only 2/4403 packages recompile — version packages whose output is byte-identical anyway). This is the companion to PR #19395 (mtime fix for Phase 2). Together they enable 4.5-52x test speedup. Addresses concerns from PR #15609: -coverprofile caching is fixed in Go 1.25 (we're on 1.25.5), and GOTAGS=release shares 93% of cache entries with default — separation is counterproductive. Partially generated by AI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tests don't need real version info. Use fixed version strings (0.0.0-test) instead of git-describe values for test ldflags. This makes link ActionIDs stable across commits, enabling Go's test result cache to hit. Also adds -buildvcs=false to stop Go 1.18+'s VCS stamping which independently changes link ActionIDs on every commit. Builds keep the current XDef/status.sh mechanism unchanged. Verified locally: tests cache across commits with fixed ldflags. Combined with the mtime fix (#19395), this enables full warm test caching in CI (2.5-8.2x faster test jobs). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
8bae563 to
db06047
Compare
Tests don't need real version info. Use fixed version strings (0.0.0-test) instead of git-describe values for test ldflags. This makes link ActionIDs stable across commits, enabling Go's test result cache to hit. Also adds -buildvcs=false to stop Go 1.18+'s VCS stamping which independently changes link ActionIDs on every commit. Builds keep the current XDef/status.sh mechanism unchanged. Verified locally: tests cache across commits with fixed ldflags. Combined with the mtime fix (#19395), this enables full warm test caching in CI (2.5-8.2x faster test jobs). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tests don't need real version info. Use fixed version strings (0.0.0-test) instead of git-describe values for test ldflags. This makes link ActionIDs stable across commits, enabling Go's test result cache to hit. Also adds -buildvcs=false to stop Go 1.18+'s VCS stamping which independently changes link ActionIDs on every commit. Builds keep the current XDef/status.sh mechanism unchanged. Verified locally: tests cache across commits with fixed ldflags. Combined with the mtime fix (#19395), this enables full warm test caching in CI (2.5-8.2x faster test jobs). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tests don't need real version info. Use fixed version strings (0.0.0-test) instead of git-describe values for test ldflags. This makes link ActionIDs stable across commits, enabling Go's test result cache to hit. Also adds -buildvcs=false to stop Go 1.18+'s VCS stamping which independently changes link ActionIDs on every commit. Builds keep the current XDef/status.sh mechanism unchanged. Verified locally: tests cache across commits with fixed ldflags. Combined with the mtime fix (#19395), this enables full warm test caching in CI (2.5-8.2x faster test jobs). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tests don't need real version info. Use fixed version strings (0.0.0-test) instead of git-describe values for test ldflags. This makes link ActionIDs stable across commits, enabling Go's test result cache to hit. Also adds -buildvcs=false to stop Go 1.18+'s VCS stamping which independently changes link ActionIDs on every commit. Builds keep the current XDef/status.sh mechanism unchanged. Verified locally: tests cache across commits with fixed ldflags. Combined with the mtime fix (#19395), this enables full warm test caching in CI (2.5-8.2x faster test jobs). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
db06047 to
2ba77eb
Compare
📝 WalkthroughSummary by CodeRabbit
WalkthroughA new composite-action step was added to stabilize Go test cache by setting deterministic file modification times (UTC timestamp 200101010000) for tracked repository files and directories, replacing nondeterministic timestamps from git checkout operations to improve test cache hits across runs. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
.github/actions/cache-go-dependencies/action.yaml (1)
108-112: Consider using null-delimited output withxargs -0for robustness with special filenames.While the repository currently has no files with spaces or special characters, using
git ls-files -z | xargs -0is a defensive best practice that prevents potential issues if such files are added in the future. Additionally, the firstTZ=UTCon line 108 is unnecessary sincegit ls-filesdoesn't use the timezone variable.♻️ Optional improvement for null-delimited safety
- TZ=UTC git ls-files | xargs TZ=UTC touch -t 200101010000 + git ls-files -z | TZ=UTC xargs -0 touch -t 200101010000 # Also stabilize directory mtimes — tests using os.ReadDir or # filepath.Walk check directory mtimes too. git ls-files only # lists files, not directories. - git ls-files | sed 's|/[^/]*$||' | sort -u | xargs TZ=UTC touch -t 200101010000 + git ls-files -z | sed -z 's|/[^/]*$||' | sort -zu | TZ=UTC xargs -0 touch -t 200101010000🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/actions/cache-go-dependencies/action.yaml around lines 108 - 112, Replace the two pipelines that call git ls-files and xargs with null-delimited variants and drop the unnecessary TZ=UTC before git ls-files; specifically, use git ls-files -z piped into xargs -0 when touching file mtimes, and for directories derive unique parent dirs from the null-delimited list (e.g. using dirname/perl/awk on the -z stream) then sort uniquely and feed to xargs -0 touch -t to set mtimes — update the lines that currently invoke "TZ=UTC git ls-files | xargs TZ=UTC touch -t 200101010000" and "git ls-files | sed 's|/[^/]*$||' | sort -u | xargs TZ=UTC touch -t 200101010000" to use the null-delimited, xargs -0 approach and remove the extraneous TZ=UTC before git ls-files.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In @.github/actions/cache-go-dependencies/action.yaml:
- Around line 108-112: Replace the two pipelines that call git ls-files and
xargs with null-delimited variants and drop the unnecessary TZ=UTC before git
ls-files; specifically, use git ls-files -z piped into xargs -0 when touching
file mtimes, and for directories derive unique parent dirs from the
null-delimited list (e.g. using dirname/perl/awk on the -z stream) then sort
uniquely and feed to xargs -0 touch -t to set mtimes — update the lines that
currently invoke "TZ=UTC git ls-files | xargs TZ=UTC touch -t 200101010000" and
"git ls-files | sed 's|/[^/]*$||' | sort -u | xargs TZ=UTC touch -t
200101010000" to use the null-delimited, xargs -0 approach and remove the
extraneous TZ=UTC before git ls-files.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Central YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro
Run ID: 7a0df54c-8fc3-4710-ba58-e36db977dfac
📒 Files selected for processing (1)
.github/actions/cache-go-dependencies/action.yaml
Tests don't need real version info. Use fixed version strings (0.0.0-test) instead of git-describe values for test ldflags. This makes link ActionIDs stable across commits, enabling Go's test result cache to hit. Also adds -buildvcs=false to stop Go 1.18+'s VCS stamping which independently changes link ActionIDs on every commit. Builds keep the current XDef/status.sh mechanism unchanged. Verified locally: tests cache across commits with fixed ldflags. Combined with the mtime fix (#19395), this enables full warm test caching in CI (2.5-8.2x faster test jobs). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Go's test cache validates inputs by (path, size, mtime) — not content. git checkout sets all file mtimes to the current time, which differs between CI runs, causing test results to never be cached. Fix: set all tracked file mtimes to a fixed date (2001-01-01) after checkout and cache restore. This makes the test cache hit across runs when file content hasn't changed. Measured impact (from PR #19201): - go unit tests: 35m → 8m (4.5x) - go-postgres: 31m → 36s (52x) - sensor-integration: 26m → 2.5m (11x) - Test cache hit rate: 97.6% (681/698 packages) Partially generated by AI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
git ls-files only outputs files, not directories. Tests that read directories at runtime (os.ReadDir, filepath.Walk) check directory mtimes for cache validation. Without stable dir mtimes, these tests always miss the cache. The helm chart tests (pkg/helm/charts/tests/*) use testdata directories and were the largest uncached tests at 712s combined. This fix caches them, dropping the Go Unit Tests step from 486s to 85s. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ensures consistent epoch values regardless of runner timezone. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ba5f8d2 to
b313cbf
Compare
TZ=UTC on git ls-files was causing shell issues. All GHA runners are UTC by default, so TZ=UTC on touch is unnecessary. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…-mtime Resolve conflict: keep mtime stabilization step, drop removed 'Download Go modules' step (removed by #19688 GOMODCACHE PR). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Description
Go's test cache validates inputs by (path, size, mtime) — not content.
git checkoutsets all mtimes to "now", so test results are never cached across CI runs.Fix: set tracked file and directory mtimes to a fixed date after checkout:
Directory mtimes matter because tests using
os.ReadDir/filepath.Walk(e.g. helm chart tests) check them too.git ls-filesonly lists files.Combined with #19617 (stable test ldflags), this enables full test caching on current master baseline: go 33m -> 7.2m (4.6x), go-postgres 31m -> 3.6m (8.6x). See #19618 for combined demo.
Prior art
#15609 by @janisz applied this with
git-restore-mtime. It was closed due to two blockers at that time:key-suffix).-coverprofiledisabled test caching in Go <1.25. Now fixed in Go 1.25.Risk
If a testdata file changes content but not size between commits, the test cache could return a stale result. This matches Go's own documented limitation.
User-facing documentation
Testing and quality
Automated testing
How I validated my change
Partially generated by AI.