Wait for both writers in the ETag cache race test

Merged
JE
19d ago
+14-9

The v0.29.0 Go release (run 40359925658) stopped in "Run tests with race detector", before the Go module tag, on a setup check in the cache race test from #1030:

--- FAIL: TestCache_EntryNeverPairsOneResponsesETagWithAnothersBody (0.12s)
    etag_doer_test.go:356: writes during the reads = [0 1], want both writers publishing

The test races two writers on one cache key against a reader and counts reads that pair one response's ETag with another's body. The check that failed is what keeps that count meaningful: if a writer published nothing while the reads ran, zero torn reads proves nothing. But the reads were a fixed 2,000, and 2,000 reads promise no overlap. A writer kept off the CPU for the ~100 ms they take, which a busy runner under the race detector can do, misses all of them.

The reads now continue past 2,000 until each writer has published twice since they began, bounded by the test's existing 10-second deadline. Twice rather than once because a writer's second Set started after the reads did, so one of its Sets began and ended while they ran. The rest holds as before: at least 2,000 reads, none of them torn, and an intact entry left behind.

Verified

Race-enabled test binaries built from main's test and this one, -test.count=200 per setting unless noted:

mainthis PR
Linux amd64, GOMAXPROCS=150/200 fail ([0 0], [0 217], ...)0/200
Linux, GOMAXPROCS=21/200 ([0 387])0/200
Linux, GOMAXPROCS=4 and 320/4000/400
Linux, four copies at once pinned to 4 cores (taskset -c 0-3), like a loaded 4-vCPU runner2/800 ([0 1], [2 0])0/800
macOS arm64, GOMAXPROCS=13/1,200 ([0 1], [0 108])0/1,200
macOS, GOMAXPROCS=2, 4, 18 and 640/8000/800
Writer 0 sleeps 300 ms after its first publish (both OSes, GOMAXPROCS=1, 4, 18)100/100 fail, [0 N]0/100

The last row is the CI failure made deterministic, with a patch that isn't committed.

To check the fix didn't blunt the assertion, a mutant Cache.Set writes the entry in place (truncate, ETag line, body) instead of renaming a temporary file into place. At GOMAXPROCS=4 both tests catch it 200/200, with 865 to 1,268 reads torn. At GOMAXPROCS=1 both miss it now and then (main 5/200, this PR 3/200): with a single P the reader only sees the file as each writer's time slice leaves it. This change doesn't touch that, and CI runs with every core.

go test -race ./... passes on Linux. Only the test changes.