fix(backend): drain reaper + cdc poller when startSession fails

If startSession returned an error, run() returned immediately and the
reaper + cdc poller goroutines kept running while defer store.Close()
fired — a data race against the SQLite handle. Mirror the bottom-of-run
shutdown sequence on the error path (cancel ctx, drain reaper, drain
poller) so both goroutines have exited before the store is closed. The
explicit-not-defer ordering is the same the existing
post-srv.Run shutdown block uses; piling on more defers would hit the
LIFO trap the same comment already warns about.

Reported by Greptile on PR #52.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
harshitsinghbhandari 2026-05-31 20:25:56 +05:30
parent c1b9e7ec1f
commit 7b9a9f5962
1 changed files with 10 additions and 0 deletions

View File

@ -87,6 +87,16 @@ func run() error {
// surfacing — so we hold the SM in a local until it does.
sStack, err := startSession(ctx, cfg, lcStack, log)
if err != nil {
// startSession is the first start* call after this point that can
// realistically fail while the cdc poller and the reaper are already
// running. Mirror the bottom-of-run shutdown sequence so both have
// drained before the deferred store.Close() fires. Defers would hit
// the LIFO trap (see comment after srv.Run), hence explicit.
stop()
lcStack.Stop()
if cdcErr := cdcPipe.Stop(); cdcErr != nil {
log.Error("cdc pipeline shutdown", "err", cdcErr)
}
return err
}
_ = sStack