fix(scanner): resolve data race on conf.Server access in getScanner

Captured DevExternalScanner config value in the controller struct at
construction time instead of reading the global conf.Server pointer in
getScanner(). The background goroutine spawned by ScanFolders() was
reading conf.Server.DevExternalScanner concurrently with test cleanup
reassigning the conf.Server pointer, causing a data race detected by
the race detector in the E2E test suite.
This commit is contained in:
Deluan
2026-02-09 16:42:05 -05:00
parent ed79a8897b
commit fd09ca103f
+20 -18
View File
@@ -29,21 +29,22 @@ var (
func New(rootCtx context.Context, ds model.DataStore, cw artwork.CacheWarmer, broker events.Broker, func New(rootCtx context.Context, ds model.DataStore, cw artwork.CacheWarmer, broker events.Broker,
pls core.Playlists, m metrics.Metrics) model.Scanner { pls core.Playlists, m metrics.Metrics) model.Scanner {
c := &controller{ c := &controller{
rootCtx: rootCtx, rootCtx: rootCtx,
ds: ds, ds: ds,
cw: cw, cw: cw,
broker: broker, broker: broker,
pls: pls, pls: pls,
metrics: m, metrics: m,
devExternalScanner: conf.Server.DevExternalScanner,
} }
if !conf.Server.DevExternalScanner { if !c.devExternalScanner {
c.limiter = P(rate.Sometimes{Interval: conf.Server.DevActivityPanelUpdateRate}) c.limiter = P(rate.Sometimes{Interval: conf.Server.DevActivityPanelUpdateRate})
} }
return c return c
} }
func (s *controller) getScanner() scanner { func (s *controller) getScanner() scanner {
if conf.Server.DevExternalScanner { if s.devExternalScanner {
return &scannerExternal{} return &scannerExternal{}
} }
return &scannerImpl{ds: s.ds, cw: s.cw, pls: s.pls} return &scannerImpl{ds: s.ds, cw: s.cw, pls: s.pls}
@@ -92,16 +93,17 @@ type scanner interface {
} }
type controller struct { type controller struct {
rootCtx context.Context rootCtx context.Context
ds model.DataStore ds model.DataStore
cw artwork.CacheWarmer cw artwork.CacheWarmer
broker events.Broker broker events.Broker
metrics metrics.Metrics metrics metrics.Metrics
pls core.Playlists pls core.Playlists
limiter *rate.Sometimes limiter *rate.Sometimes
count atomic.Uint32 devExternalScanner bool
folderCount atomic.Uint32 count atomic.Uint32
changesDetected bool folderCount atomic.Uint32
changesDetected bool
} }
// getLastScanTime returns the most recent scan time across all libraries // getLastScanTime returns the most recent scan time across all libraries