Description
CreateCanvas reads the graph container under s.mu but assigns c.CanvasInfo after releasing the mutex. GetCanvas reads the same pointer while holding s.mu.RLock, so the write is not synchronized with readers or with another concurrent CreateCanvas call.
Reproduction
On current main (6752ff8da9b1ea85c8e91b27b0a64fef099f22f9), a test that repeatedly calls CreateCanvas and GetCanvas concurrently reports read/write and write/write races:
go test -race -gcflags='all=-N -l' ./internal/service -run '^TestAuditCreateCanvasRace$' -count=1
WARNING: DATA RACE
Read ... container.go:233 / container.go:237
Previous write ... container.go:223
This was reproduced with Linux and Go 1.25.6, matching the repository CI Go version.
Expected behavior
All access to GraphContainer.CanvasInfo should be protected by the container service mutex.
Suggested fix
Acquire s.mu for the cache assignment after building the schema, and add a concurrent regression test that passes under the race detector.
Description
CreateCanvasreads the graph container unders.mubut assignsc.CanvasInfoafter releasing the mutex.GetCanvasreads the same pointer while holdings.mu.RLock, so the write is not synchronized with readers or with another concurrentCreateCanvascall.Reproduction
On current
main(6752ff8da9b1ea85c8e91b27b0a64fef099f22f9), a test that repeatedly callsCreateCanvasandGetCanvasconcurrently reports read/write and write/write races:This was reproduced with Linux and Go 1.25.6, matching the repository CI Go version.
Expected behavior
All access to
GraphContainer.CanvasInfoshould be protected by the container service mutex.Suggested fix
Acquire
s.mufor the cache assignment after building the schema, and add a concurrent regression test that passes under the race detector.