Skip to content

Commit ff3ea11

Browse files
author
Maciej Mucha
committed
Fix client state not being reset after failed Start()
When Start() fails due to a real error (e.g., database connection failure), the client's internal state was left in a running state, preventing subsequent Start() calls from succeeding. Add StartFailed() method to BaseStartStop that properly resets internal state after a startup failure. This is separate from Stop() handling - when Stop() cancels the context (ErrStop), Stop() itself handles cleanup via finalizeStop(). Fixes the issue where a client could not be restarted after a transient startup failure.
1 parent f6feb51 commit ff3ea11

2 files changed

Lines changed: 28 additions & 6 deletions

File tree

client.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,15 +1071,15 @@ func (c *Client[TTx]) Start(ctx context.Context) error {
10711071

10721072
return nil
10731073
}(); err != nil {
1074-
defer func() {
1075-
stopped()
1076-
// Reset the client's internal state (isRunning flag) so that Start
1077-
// can be called again after a startup failure.
1078-
c.baseStartStop.Stop()
1079-
}()
1074+
// If context was cancelled due to Stop(), just close the stopped channel
1075+
// and return. Stop() will handle cleanup via finalizeStop().
10801076
if errors.Is(context.Cause(fetchCtx), startstop.ErrStop) {
1077+
stopped()
10811078
return nil
10821079
}
1080+
1081+
// For real startup failures, reset state so Start() can be called again.
1082+
c.baseStartStop.StartFailed(stopped)
10831083
return err
10841084
}
10851085

rivershared/startstop/start_stop.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,28 @@ func (s *BaseStartStop) StartInit(ctx context.Context) (context.Context, bool, f
136136
}
137137
}
138138

139+
// StartFailed should be called when a service fails to start after StartInit.
140+
// It closes the stopped channel and resets internal state so Start can be
141+
// called again.
142+
//
143+
// This should not be used when a Stop is already in progress (ErrStop), because
144+
// Stop will handle cleanup via finalizeStop.
145+
func (s *BaseStartStop) StartFailed(stopped func()) {
146+
if s.cancelFunc != nil {
147+
s.cancelFunc(ErrStop)
148+
}
149+
stopped()
150+
151+
s.mu.Lock()
152+
defer s.mu.Unlock()
153+
if !s.isRunning {
154+
return
155+
}
156+
s.isRunning = false
157+
s.started = nil
158+
s.stopped = nil
159+
}
160+
139161
// Started returns a channel that's closed when a service finishes starting, or
140162
// if failed to start and is stopped instead. It can be used in conjunction with
141163
// WaitAllStarted to verify startup of a constellation of services.

0 commit comments

Comments
 (0)