fix: scheduler race condition — guard initial-run goroutines with atomic flag

The "run immediately on start" goroutines in 5 scheduler loops did not
set the idempotency guard (atomic.Bool), allowing the first ticker tick
to spawn a concurrent execution. The race detector caught overlapping
goroutines calling the same service method simultaneously.

Fix: set the Running flag before spawning the initial goroutine and
clear it in the defer, same pattern as ticker-triggered goroutines.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
shankar0123
2026-03-27 23:27:03 -04:00
parent 144bd5fdf9
commit d27cf3545b
+15 -5
View File
@@ -157,10 +157,12 @@ func (s *Scheduler) renewalCheckLoop(ctx context.Context) {
ticker := time.NewTicker(s.renewalCheckInterval)
defer ticker.Stop()
// Run immediately on start
// Run immediately on start (with idempotency guard)
s.renewalCheckRunning.Store(true)
s.wg.Add(1)
go func() {
defer s.wg.Done()
defer s.renewalCheckRunning.Store(false)
s.runRenewalCheck(ctx)
}()
@@ -204,10 +206,12 @@ func (s *Scheduler) jobProcessorLoop(ctx context.Context) {
ticker := time.NewTicker(s.jobProcessorInterval)
defer ticker.Stop()
// Run immediately on start
// Run immediately on start (with idempotency guard)
s.jobProcessorRunning.Store(true)
s.wg.Add(1)
go func() {
defer s.wg.Done()
defer s.jobProcessorRunning.Store(false)
s.runJobProcessor(ctx)
}()
@@ -251,10 +255,12 @@ func (s *Scheduler) agentHealthCheckLoop(ctx context.Context) {
ticker := time.NewTicker(s.agentHealthCheckInterval)
defer ticker.Stop()
// Run immediately on start
// Run immediately on start (with idempotency guard)
s.agentHealthCheckRunning.Store(true)
s.wg.Add(1)
go func() {
defer s.wg.Done()
defer s.agentHealthCheckRunning.Store(false)
s.runAgentHealthCheck(ctx)
}()
@@ -297,10 +303,12 @@ func (s *Scheduler) notificationProcessLoop(ctx context.Context) {
ticker := time.NewTicker(s.notificationProcessInterval)
defer ticker.Stop()
// Run immediately on start
// Run immediately on start (with idempotency guard)
s.notificationProcessRunning.Store(true)
s.wg.Add(1)
go func() {
defer s.wg.Done()
defer s.notificationProcessRunning.Store(false)
s.runNotificationProcess(ctx)
}()
@@ -383,10 +391,12 @@ func (s *Scheduler) networkScanLoop(ctx context.Context) {
ticker := time.NewTicker(s.networkScanInterval)
defer ticker.Stop()
// Run immediately on start
// Run immediately on start (with idempotency guard)
s.networkScanRunning.Store(true)
s.wg.Add(1)
go func() {
defer s.wg.Done()
defer s.networkScanRunning.Store(false)
s.runNetworkScan(ctx)
}()