Skip to content

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

Description

@cinar

Summary

backtest.DataReport.Results is a plain map[string][]*DataStrategyResult, mutated by AssetBegin and Write with no synchronization. Backtest.worker runs one goroutine per worker (Backtest.Workers), each calling report.AssetBegin / report.Write / report.AssetEnd concurrently for different assets. With Workers > 1 and DataReport as the report, this is a concurrent map write.

This is the same bug class fixed for HTMLReport in #393 / commit b396ad5 (which added a sync.Mutex to backtest/html_report.go), but DataReport was never touched.

Reproduction

go test -race with a Backtest configured with Workers: 8, backtesting 20+ assets, using NewDataReport() as the report, reports a concurrent map write in DataReport.Write / DataReport.AssetBegin immediately. In a non-race build this is fatal error: concurrent map writes — a process crash, not silent corruption.

Impact

DataReport is the report type mcp/strategy.go builds its backtest results on, so this is reachable from the MCP server's runBacktest tool, not just direct library use.

Root cause: the Report interface (backtest/report.go) has no documented concurrency contract, so nothing signals to an implementer that AssetBegin/Write/AssetEnd can be called concurrently across assets. DataReport made the same mistake HTMLReport did, and any future Report implementation is likely to repeat it.

Proposed fix

Rather than adding a mutex to DataReport alone (which just repeats the HTMLReport fix a second time and leaves the interface trap for the next implementation), centralize serialization in Backtest.worker itself so no Report implementation needs to manage its own locking, and document the contract on the Report interface.

CI's test task also doesn't run go test -race, which is why this wasn't caught automatically.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions