-
Notifications
You must be signed in to change notification settings - Fork 4.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cache unit test results [NET-4965] #18333
Conversation
Could you point to one or two examples in some tests to support this statement? |
All of the tests depend on the In Then later in each test job, a few seconds downloading it. Compare this to the build time, which is 33s. Not much savings to be had. |
Noticing some cache misses due to
|
3dcb67b
to
3285fcc
Compare
It may also be worth saving the Update: it saved about 20s between this and this re-run. The cost is a 612MB. |
Hmm, I've discovered a surprising behavior. For example
To be fair, I think it says this on the tin, just surprising Update: this totally precludes us from combining the Go test cache with |
5ca2187
to
4f3aea1
Compare
I rebased onto this branch which moves retries inside the function in question. However, I think it was probably too blunt, because those flaky tests still flaked. We may need to capture |
c656dbf
to
a3d8b22
Compare
In light of #18365 (comment), I've gone back to just skipping the flaky tests and basing on |
This pull request has been automatically flagged for inactivity because it has not been acted upon in the last 60 days. It will be closed if no new activity occurs in the next 30 days. Please feel free to re-open to resurrect the change if you feel this has happened by mistake. Thank you for your contributions. |
8025b79
to
c479af2
Compare
This pull request has been automatically flagged for inactivity because it has not been acted upon in the last 60 days. It will be closed if no new activity occurs in the next 30 days. Please feel free to re-open to resurrect the change if you feel this has happened by mistake. Thank you for your contributions. |
Closing due to inactivity. If you feel this was a mistake or you wish to re-open at any time in the future, please leave a comment and it will be re-surfaced for the maintainers to review. |
I was curious and started looking into
setup-go
's new on-by-defaultcache
flag, and it turns out it probably doesn't work very well unfortunately. TLDR: it not only cachesGOMODCACHE
, but alsoGOCACHE
; it isn't specific enough about input hashing and ends up caching a bunch of junk that actually slows the build down.As part of repro, I experimented with this repo and ended up getting about halfway to a working implementation of
GOCACHE
including test caching, so I did a bunch more experimenting, tidied it up, and here we are.There are some kind of big changes here to the CI strategy, so I'll justify them with data from my experimental PR.
Test result and build object caching
The main course. In Go, test results and build objects are stored in the same cache.
I made some attempts to keep them separate (e.g.
go test -c
to pre-compile tests), but to no avail. It would have been nice, since the build cache is 100s of MBs, and the test cache is 10s.Pros
A run from an empty cache takes ~ 9 minutes, which is already better than main, which takes ~ 11 min. A second run takes ~ xxx min.
Cons
Cache blobs are scoped to branches (GHA policy). The way I've designed this, we will have a cache blob for each (Go version, GOARCH, module) tuple (at least), which at current count is 8. These caches are 100s of MBs, so I can imagine them getting out of hand. There is a 10GB repo-level cache size limit, but it apparently isn't enforced since we are currently sitting at about 50GB.
Cache blobs mostly only grow, until they are reaped by GHA's recency-based garbage collector. In this implementation, I do no GC inside the cache blob. Go itself does trim cache objects, but it is based on mtime and not disk usage. There's no way to set a maximum disk usage limit.
Cache blobs are relatively fast to restore/save. IME it's on the order of 15s for a typical 500 MB blob, either way.
For some reason, cache blobs are not overwriteable. So I worked around this by deleting and then pushing the amended blob. If you're doing something non-linear like rerunning an old commit while the current commit is running, last write wins, and there's no locking AFAICT, so you might end up writing a stale cache.
No shared dev build
I deleted the
dev-build
step. Now the tests just each build it.dev-build
builds aconsul
binary and shares it as an artifact to all the tests. It's a well-intentioned idea, but unfortunately the binary is quite big 100s of MB) and GHA artifact upload/download is for some mysterious reason about 10x slower than cache save/restore.Also, after push, the tests all need to wait for
dev-build
and can do no useful work until then.I'd be willing to bet the
consul
binary shares some cache objects with the unit tests. So on subsequent builds, it should be 0s to build it.No test splitting
Mostly I removed this because caching would be hard. We need per-runner caches because there's no way to do concurrent writes to the same cache (safely). So with test splitting by package into N buckets, we'd have to do some sort of consistent hashing/sharding/etc that is just not the sort of thing I want to do in Bash.
But also, I found that by fixing flakiness and adding test/build caching, module-level splitting (like we have in this PR) is plenty fast.
Explicitly pass the Go version
I declare the Go versions we use (N and N-1) at the top and explicitly feed them to every unit test. Caches are sensitive to any change in Go version, so we must keep this controlled. Previously, we would just specify
1.20.x
tosetup-go
and it may or may not download the latest.No
gotestsum
retriesA test results cache is basically like retries. IMO
gotestsum
retries are a hack. IMO if we consistently see a test flaking, we should disable it and fix it later. (I made a PR here. Probably bears further discussion.)Additionally, I've discovered that the
go test -run
flag is not specially handled;go test -run '.*' .
is a different cache key thango test .
, even though they should be equivalent.gotestsum
's retry mechanism uses-run
, so we can't use them together effectively. When the first run (without-run
) fails, none of its PASSes are cached. (This is maybe a bug with Go, maybe withgotestsum
, IDK.)Sidequest:
freeport
retriesI added a new function to
freeport
:RetryMustGetN
, which is likeGetN
but retries. This was a big source of flakiness.Sidequest: fix test results artifacts
Our invocations of
reusable-unit{,split}.yml
actually saved their test results over top of one another. The artifact namespace is shared for all jobs in the workflow (even called workflows).PR Checklist