fix(track): skip telemetry under testing.Testing() to avoid term race#2137
fix(track): skip telemetry under testing.Testing() to avoid term race#2137edwardrf wants to merge 1 commit into
Conversation
`track.Evt` spawns a background goroutine that calls `tracker.Track`, which goes through the gRPC interceptor (`grpcLogger.logRequest` → `term.Debug`). That goroutine isn't synchronized with the calling test — it routinely outlives a test by milliseconds-to-seconds because it's waiting on an HTTP RPC roundtrip. Meanwhile `term.SetupTestTerm` swaps `term.DefaultTerm` at the start of each test and restores it via `t.Cleanup`. When test N's tracking goroutine is still alive after test N's cleanup runs, test N+1's SetupTestTerm assignment to `DefaultTerm` races the orphan goroutine's `term.Debug` read of the same variable. The race detector caught this on the workspace tests: WARNING: DATA RACE Read at 0x... by goroutine 288: pkg/term.Debug() ← grpcLogger.logRequest ← Track ← track.Evt.func1 Previous write at 0x... by goroutine 295: pkg/term.SetupTestTerm() ← TestWorkspaceListVerboseTable Disable tracking under `testing.Testing()` so the goroutine is never spawned in tests. Production behavior is unchanged — `testing.Testing()` returns false outside `go test` binaries. Available since Go 1.21 (project is on 1.25). This was added specifically by the Go team for non-test code to detect test context safely. The alternatives all have downsides: - Draining `FlushAllTracking` in SetupTestTerm cleanup creates an import cycle (`track` already imports `term`). - Per-test `t.Cleanup(track.FlushAllTracking)` is manual and easy to forget. - Making `term.DefaultTerm` atomic addresses the symptom, not the cause; future readers from the orphan goroutine stay risky. Verified: `go test -race -short -count=3 ./cmd/cli/command/` passes 3×. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThis PR adds a test-context guard to the tracking system. The ChangesTest-context tracking guard
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~5 minutes
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 golangci-lint (2.12.2)level=warning msg="The linter 'gomodguard' is deprecated (since v2.12.0) due to: new major version. Replaced by gomodguard_v2." Comment |
What
Adds a
testing.Testing()short-circuit at the top oftrack.Evtso telemetry is never fired during tests. Stops a data race betweentrack.Evt's background goroutine andterm.SetupTestTerm's mutation ofterm.DefaultTerm.The race
```
WARNING: DATA RACE
Read at 0x... by goroutine 288:
pkg/term.Debug() ← grpcLogger.logRequest ← Track ← track.Evt.func1
Previous write at 0x... by goroutine 295:
pkg/term.SetupTestTerm() ← TestWorkspaceListVerboseTable
```
track.Evtspawns a goroutine that callstracker.Trackthrough the gRPC interceptor, which callsterm.Debug— readingterm.DefaultTerm. The goroutine isn't bound to the calling test's lifetime; it routinely outlives one test by the duration of an HTTP roundtrip.term.SetupTestTermwritesterm.DefaultTermat the start of every test that uses it. When test N's tracking goroutine is still alive when test N+1 starts, the SetupTestTerm write races the orphan'sterm.Debugread.Fix
```go
func Evt(name string, props ...Property) {
if testing.Testing() {
return
}
// ...
}
```
testing.Testing()is the idiomatic Go-1.21+ way to detect test context from non-test code. Project is on Go 1.25.falseoutsidego testbinaries.Why not the alternatives
FlushAllTrackinginSetupTestTermcleanuptrackalready importsterm.t.Cleanup(track.FlushAllTracking)term.DefaultTermDEFANG_DISABLE_ANALYTICS=truein TestMainVerification
```
$ cd src && go test -race -short -count=3 ./cmd/cli/command/
ok github.com/DefangLabs/defang/src/cmd/cli/command 12.675s
```
🤖 Generated with Claude Code
Summary by CodeRabbit