Repository navigation
Replies: 2 comments
TriageVerdict: A reproducible bug in Is this a misuse of humus?No. The end user owns their Service's The end user can't defend against it. The race is inside Is it a feature request?No. It contradicts these promises:
A fix restores these. It changes no exported name, signature, field, default or documented guarantee, so it isn't an API change. Is it reproducible?Yes. At 4ddf294, the tip of The reported test fails at the reporter's settings, with the reporter's error. Three other root tests fail the same way:
Their failures read To take the timing out, the triage's tests deliver the signal from the App's own logger, as Run logs "serving" in step 7. The logger holds Run there with
SIGTERM as the App starts serving: the status loses the signal. One Service, which returns nil once Serve's context is done.
A Drain that waits on a load balancer:
Cancelling Run's context as the App starts serving does the same: This corrects the report in four places:
Why
#961 is filed to fix it:
|
|
Fixed in #965, merged as 25b0a30. The order in which a signal and a
The triage's reproductions are now tests in |
Uh oh!
There was an error while loading. Please reload this page.
What happens
TestApp_Run_telemetry_a_failed_shutdown_is_joined_into_the_causefails now and then. The App was stopped by SIGTERM, but the exitStatus's cause holds only the telemetry shutdown error, not the signal:That run was the Build job of run 39160233524, attempt 1 on #935 (go1.27.2, linux/amd64). It passed on a re-run. The change in #935 was only to
kube.It reproduces locally on
main(2f45ce1):It failed once in 3,000 runs at
-cpu=2, once in 3,000 at-cpu=4, and never in 5,000 at-cpu=1.Why
This looks like a race in
App.run, not just a flaky test. It hasn't been triaged yet: the explanation below comes from reading the code.fakeServicewith noservefunc. ItsServewaits for its context and returnsnil(app_test.go#L48-L54).runserves underserveCtx, a child ofsigCtx(app.go#L389-L394). SIGTERM cancelssigCtx, and with itserveCtx, so the Service returns at once.runthen selects between<-sigCtx.Done()and<-served(app.go#L401-L410). If the Service'snilreachesservedbeforerun's goroutine is scheduled, both cases are ready and Go picks one at random.served,signalledstays false andrunreturns0, nil(app.go#L443-L446). The signal is gone, and only the telemetry error is joined into the cause (app.go#L301-L307).The same choice affects more than this test:
Status: an App whose Services return as soon as they're cancelled can exit with no cause after a SIGTERM, instead ofcontext.Canceled.ErrServiceReturnedas itsShutdownCause, instead of the signal (app.go#L415-L419). A Drain that acts on why the App is stopping then takes the wrong branch.Expected
When the signal context is done by the time
runhandles a Service's return, it is a signal shutdown:Status.Cause()holds the signal's cause, and any telemetry shutdown error is joined to it;ShutdownCauseis the signal's cause, notErrServiceReturned.The test passes every time under
-race -countat any-cpu.References
All reactions