Skip to content

Fix concurrent map write in DataReport by centralizing report locking - #401

Merged
cinar merged 3 commits into
masterfrom
fix/datareport-concurrent-map-write
Aug 19, 2026
Merged

Fix concurrent map write in DataReport by centralizing report locking#401
cinar merged 3 commits into
masterfrom
fix/datareport-concurrent-map-write

Conversation

@cinar

@cinar cinar commented Aug 19, 2026

Copy link
Copy Markdown
Owner

Fixes #400.

Problem

DataReport.Results is a plain map, mutated by AssetBegin/Write with no synchronization, while Backtest.worker calls those methods concurrently (one goroutine per Backtest.Workers) for different assets. This is the same bug class fixed for HTMLReport in #393, but DataReport never got the equivalent fix, and it's the report type mcp/strategy.go builds its backtest results on.

Fix

Instead of bolting a second mutex onto DataReport (repeating the same fix a third time for the next Report implementation), this centralizes serialization in Backtest.worker:

  • Backtest now owns a reportMu sync.Mutex and locks around every call into report.AssetBegin / report.Write / report.AssetEnd.
  • Report's doc comment now states the contract: implementations don't need their own synchronization, since Backtest serializes calls across workers.
  • HTMLReport's now-redundant mu sync.Mutex is removed, since Backtest already guarantees serialized access.
  • report_factory.go's reportBuilders map (flagged as a lower-stakes instance of the same smell) is now guarded by a sync.RWMutex.

Testing

  • Added TestBacktestAllAssetsAndStrategiesWithDataReport, mirroring the existing HTMLReport Workers = 16 test but for DataReport — this is the exact case that was previously untested.
  • go test -race ./backtest/... and go test ./... pass.
  • go build ./..., go vet ./..., gofmt -l backtest/ all clean.

🤖 Generated with Claude Code

DataReport.Results was mutated from Backtest.worker with no
synchronization, same bug class as the HTMLReport race fixed in #393.
Rather than adding a mutex to DataReport too, centralize serialization
of Report calls in Backtest.worker itself, document the concurrency
contract on the Report interface, and drop HTMLReport's now-redundant
mutex. Also guard the reportBuilders registry map in report_factory.go.

Fixes #400.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@codecov-commenter

codecov-commenter commented Aug 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.06%. Comparing base (6c49f42) to head (bcdc27b).

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #401   +/-   ##
=======================================
  Coverage   92.05%   92.06%           
=======================================
  Files         229      229           
  Lines        7237     7245    +8     
=======================================
+ Hits         6662     6670    +8     
  Misses        488      488           
  Partials       87       87           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

cinar and others added 2 commits August 19, 2026 05:44
Explicit Lock()/Unlock() pairs leave reportMu locked forever if a
Report call panics. Route every call through withReportLock, which
locks and defers the unlock around a closure, so the mutex is always
released even on panic.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@cinar
cinar merged commit 1f1dde1 into master Aug 19, 2026
5 checks passed
@cinar
cinar deleted the fix/datareport-concurrent-map-write branch August 19, 2026 10:04
@github-actions github-actions Bot locked and limited conversation to collaborators Aug 19, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Concurrent map write panic in backtest.DataReport under Backtest.Workers > 1

2 participants