From 092cbbe80478b886ce2aec20f72704f02fbb72cb Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Sat, 13 Jun 2026 19:15:38 +0200 Subject: [PATCH] fix(M2): make backup.Manager.stackProvider init-only contract explicit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The single write (SetStackProvider, main.go:225) was mutex-guarded while all 11 reads were unlocked — the lock implied a runtime concurrency the reads don't honour. It is called once during single-threaded startup before any goroutine, so the write happens-before every read and no race exists. Removed the misleading lock and documented the init-only contract. Co-Authored-By: Claude Opus 4.8 (1M context) --- controller/internal/backup/backup.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/controller/internal/backup/backup.go b/controller/internal/backup/backup.go index 59e107e..4f89312 100644 --- a/controller/internal/backup/backup.go +++ b/controller/internal/backup/backup.go @@ -388,11 +388,15 @@ func (m *Manager) releaseRunning() { } // SetStackProvider sets the stack data provider for app data discovery. -// Write is protected by mutex since stackProvider is read by concurrent goroutines. +// +// M2: this MUST be called exactly once during single-threaded startup (main.go), +// before the scheduler / HTTP server / any backup goroutine starts. That write +// then happens-before all the (unlocked) reads of m.stackProvider, so no data +// race exists. The earlier mutex on this write was misleading — it implied +// runtime concurrency the reads don't honour; removed to make the init-only +// contract explicit. Do NOT call this after startup. func (m *Manager) SetStackProvider(provider StackDataProvider) { - m.mu.Lock() m.stackProvider = provider - m.mu.Unlock() } // GetStackHDDMounts returns HDD mount paths for the named stack via the stack provider.