Navigation Menu

Skip to content
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

cmd/go: invalidate cache entries for test runs using different timeouts #36134

Closed
bcmills opened this issue Dec 13, 2019 · 10 comments
Closed

cmd/go: invalidate cache entries for test runs using different timeouts #36134

bcmills opened this issue Dec 13, 2019 · 10 comments
Labels
FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done.
Milestone

Comments

@bcmills
Copy link
Contributor

bcmills commented Dec 13, 2019

In the resolution to #22633, we started explicitly ignoring the test timeout when computing its cache key:

case "-test.timeout":
// Special case: this is cacheable but ignored during the hash.
// Do not add to cacheArgs.

The use-case there was to be able to run a set of tests with a short timeout, and then increase that timeout in case of failure without re-running all of the tests.

However, that behavior seems incorrect to me: a test can today vary its behavior based on its timeout by using flag.Lookup("test.timeout") or examining os.Args directly. We have a pretty comprehensive test that checks for cache invalidation for all kinds of esoteric-but-observable behaviors, such as accessing files or environment variables — it's very surprising for one particular input to escape that check.

In #34707 (comment), I even suggested a situation in which a test might want to explicitly vary its behavior based on the timeout: a test using randomized inputs and running with a short timeout might want to check only a few cursory inputs, while a very long (or even unlimited!) timeout might imply a much more exhaustive search.

We do have a tracing hook that checks whether the test happens to observe external environment variables. Something like that might be reasonable for os.Args, but we don't have that today.

I propose that we revert the special case for -test.timeout, and if that proves to be too unpleasant we should add explicit tracking for access to the flag package and/or os.Args so that we only ignore -test.timeout for tests whose behavior verifiably does not depend on the timeout.

CC @jayconrod @rogpeppe @matloob

@gopherbot gopherbot added this to the Proposal milestone Dec 13, 2019
@bcmills bcmills changed the title proposal: cmd/go: invalidate test cache entries that use different timeouts proposal: cmd/go: invalidate cache entries for test runs using different timeouts Dec 13, 2019
@rogpeppe
Copy link
Contributor

Have you found any case where this behaviour is an issue currently? I tend to think that it's probably right that adjusting the timeout doesn't invalidate existing cached tests. When tests take a long time, it's frustrating to have them run again needlessly. If a test varies its behaviour based on the timeout, I think it probably deserves what it gets.

@bcmills
Copy link
Contributor Author

bcmills commented Dec 13, 2019

I wrote a bunch of regression tests for #32471 and they failed because the tests were being cached when I didn't expect.

It may be annoying to re-run tests when you change flags, but the general solution to that is to not request those tests when you know you're changing flags...

@rogpeppe
Copy link
Contributor

In the past, I've worked a lot on projects that had very slow tests. Often there would be one or two packages that timed out and required a -test.timeout flag, so when running tests from the top level you'd use that flag. But within other subdirectories (still slow but not slow enough to time out), you wouldn't need that flag.

If this proposal was accepted, you could run all the tests from the top level with go test -timeout 20m ./... but then when running tests in subdirectories with just go test ./..., the cache wouldn't apply and all those tests would need to be run again. Speaking for myself, I think I'd find that quite frustrating.

In #34707 (comment), I even suggested a situation in which a test might want to explicitly vary its behavior based on the timeout: a test using randomized inputs and running with a short timeout might want to check only a few cursory inputs, while a very long (or even unlimited!) timeout might imply a much more exhaustive search.

That doesn't seem like a good idea to me. I'd suggest that it would be better to have an environment variable or a flag that controls that behaviour - I think it would be deeply unintuitive for a test to take longer when the timeout is increased.

For your regression tests, perhaps a better solution might be for a test to be able to explicitly mark itself as non-cacheable.

@bcmills
Copy link
Contributor Author

bcmills commented Dec 17, 2019

Often there would be one or two packages that timed out and required a -test.timeout flag […]. But within other subdirectories (still slow but not slow enough to time out), you wouldn't need that flag.

It seems to me that GOFLAGS already addresses that use-case reasonably well: if you know that many of the tests you will be running require -timeout 20m, you can set GOFLAGS=-timeout=20m to apply that globally. If the behavior of the tests really does not depend on the timeout flag, then they will produce the same results either way.

(And you can always terminate the faster tests earlier by sending them SIGINT or SIGQUIT if you believe they're hung, or run them with an explicitly-shorter timeout while you're iterating on them.)

@bcmills
Copy link
Contributor Author

bcmills commented Dec 17, 2019

For your regression tests, perhaps a better solution might be for a test to be able to explicitly mark itself as non-cacheable.

That is #23799. (But see specifically #23799 (comment).)

@bcmills
Copy link
Contributor Author

bcmills commented Dec 17, 2019

Another problem with ignoring -timeout for tests is that it hides test failures.

If I know that my CI system runs with -timeout=5m, and I run go test -timeout=5m ./... and the tests all report as passing, then I will be very surprised when I commit the change and push it to CI and discover that it consistently times out.

That suggests, at the very least, a much more subtle caching policy: a passing test with a shorter timeout generally implies passing with a longer timeout too, and a passing test with a long timeout should continue pass with any timeout shorter than its observed running time, but a passing result for -timeout=10m should not imply a passing result for -timeout=10s, because the test would not necessarily pass if run in that configuration with a clean cache.

@gopherbot
Copy link

Change https://golang.org/cl/220365 mentions this issue: cmd/go: add a regression test for #36134

@rsc
Copy link
Contributor

rsc commented Feb 26, 2020

@bcmills, what do you think we should do here?

@rsc rsc added this to Active in Proposals (old) Feb 26, 2020
@bcmills
Copy link
Contributor Author

bcmills commented Feb 26, 2020

I think we should revert the special-case for the -test.timeout flag, so that different -timeout values result in fresh runs of the test. That's a simple change to make, and it's easy to adapt it to accommodate environments that occasionally need very long tests by setting GOFLAGS=-timeout=20m or similar.

(Then we can see if there are any remaining cases where the GOFLAGS approach does not suffice, and if so whether they merit the additional complexity of flag tracking and timeout comparisons for more precise cache invalidation.)

@rsc
Copy link
Contributor

rsc commented Mar 4, 2020

This is about a subtle corner case in the test caching implementation.
I don't believe it needs to be a proposal.
I would say make the change now and let's give it the rest of the cycle to soak and find out what breaks or gets noticeably worse.

@rsc rsc changed the title proposal: cmd/go: invalidate cache entries for test runs using different timeouts cmd/go: invalidate cache entries for test runs using different timeouts Mar 4, 2020
@rsc rsc added NeedsFix The path to resolution is known, but the work has not been done. and removed Proposal labels Mar 4, 2020
@rsc rsc modified the milestones: Proposal, Go1.15 Mar 4, 2020
@bcmills bcmills self-assigned this Mar 4, 2020
@bcmills bcmills removed this from Active in Proposals (old) Mar 4, 2020
@golang golang locked and limited conversation to collaborators Mar 4, 2021
@rsc rsc unassigned bcmills Jun 23, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done.
Projects
None yet
Development

No branches or pull requests

4 participants