Skip to content

Commit d61a2c9

Browse files
committed
fix: eliminate race in Reload — atomic check-and-start under single lock
1 parent ccda97b commit d61a2c9

1 file changed

Lines changed: 21 additions & 6 deletions

File tree

internal/server/email_trigger.go

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,7 @@ func (e *EmailTriggerPoller) Reload(ctx context.Context) error {
9191

9292
// Start pollers for newly added triggers.
9393
for _, t := range triggers {
94-
e.mu.Lock()
95-
_, running := e.pollers[t.ID]
96-
e.mu.Unlock()
97-
if !running {
98-
e.startPoller(ctx, t)
99-
}
94+
e.startPollerIfNotRunning(ctx, t)
10095
}
10196

10297
return nil
@@ -112,10 +107,30 @@ func (e *EmailTriggerPoller) Stop() {
112107

113108
// startPoller starts a single goroutine for the given trigger, subject to the
114109
// maxConnections limit. Excess triggers are logged and skipped.
110+
// startPollerIfNotRunning acquires the lock, checks if the trigger is already
111+
// running, and starts it if not. The check-and-start is atomic under the lock
112+
// to prevent duplicate pollers from concurrent Reload calls.
113+
func (e *EmailTriggerPoller) startPollerIfNotRunning(ctx context.Context, t TriggerRecord) {
114+
e.mu.Lock()
115+
defer e.mu.Unlock()
116+
117+
if _, running := e.pollers[t.ID]; running {
118+
return
119+
}
120+
e.startPollerLocked(ctx, t)
121+
}
122+
123+
// startPoller acquires the lock and starts a poller. Used during Start() where
124+
// no existence check is needed (fresh startup).
115125
func (e *EmailTriggerPoller) startPoller(ctx context.Context, t TriggerRecord) {
116126
e.mu.Lock()
117127
defer e.mu.Unlock()
128+
e.startPollerLocked(ctx, t)
129+
}
118130

131+
// startPollerLocked starts a single goroutine for the given trigger. Must be
132+
// called with e.mu held. Excess triggers beyond maxConnections are skipped.
133+
func (e *EmailTriggerPoller) startPollerLocked(ctx context.Context, t TriggerRecord) {
119134
if len(e.pollers) >= e.maxConnections {
120135
e.server.Logger.Warn("email poller: maxConnections reached, skipping trigger",
121136
"trigger_id", t.ID,

0 commit comments

Comments
 (0)