fix(recovery): guard claim fan-out send on context cancellation to unblock Stop - #2118
Conversation
e925b17 to
d42163c
Compare
📊 Token Validation BenchmarkComparison of this PR against the base branch. 🟢 improvement · 🔴 regression · ➖ within ±1.0% noise.
|
📊 Token Validation BenchmarkComparison of this PR against the base branch. 🟢 improvement · 🔴 regression · ➖ within ±1.0% noise.
|
There was a problem hiding this comment.
hi @AkramBitar , took a careful read since I have been working in this file recently. The deadlock traces through exactly as you describe: worker returns from its own select on ctx.Done() without draining work, work is unbuffered and fanOut is the only sender, so the send parks with no receiver and close(work) is never reached. Guarding the send looks right to me, and keeping close(work) and workerWG.Wait() on the cancellation path seems like the important detail.
Ran the test at -count=3 -race and it passes. I liked that it drives the failure through the real Start/Stop path rather than reaching for the unexported fan-out.
Three observations below, none of them blocking, and some possibly things you already weighed up.
d42163c to
6ee7243
Compare
|
Thanks a lot for the review. I fixed all the comments. Hope now it is ok. Regards, |
8e3f497 to
44290ee
Compare
…block Stop The recovery manager fanned claims out to its worker pool with an unguarded `work <- claim` send. On shutdown the workers return from their own select on `ctx.Done()` without draining `work`, so that send blocked forever: `close(work)` was never reached, the recovery loop's deferred `wg.Done()` never ran, and `Stop()`'s `wg.Wait()` hung while holding `m.mu`, wedging every later `Start()`/`Stop()` call. Extract the fan-out into `Manager.fanOut` and guard the send on `ctx.Done()`, keeping `close(work)` and `workerWG.Wait()` on the cancellation path so no worker goroutine leaks, and surface the cancellation instead of swallowing it. Undispatched claims stay `Pending`, so the next sweep re-claims them once their lease expires and no work is lost. `fanOut` also returns the number of claims it dispatched. The sweep summary counted successes as `len(records)-failures`, which was only correct while the fan-out either completed or hung; a cancelled fan-out leaves a tail that never reaches `errCh`, so a partial sweep reported never-attempted claims as succeeded. Successes are now counted against the dispatched total and a short dispatch warns on its own. A sweep aborted by `Stop()` is an ordinary shutdown, so `recoveryLoop` logs a cancelled sweep at debug level via `logSweepError` and keeps the warning for genuine failures. Signed-off-by: AkramBitar <akram@il.ibm.com>
44290ee to
ae29395
Compare
Fixes #2038
The recovery manager fanned claims out to its worker pool with an unguarded
work <- claimsend. On shutdown the workers return from their ownselectonctx.Done()without drainingwork, so that send blocked forever:close(work)was never reached, the recovery loop's deferredwg.Done()never ran, andStop()'swg.Wait()hung while holdingm.mu— wedging every laterStart()/Stop().Changes
Manager.fanOutand guard the send onctx.Done(), mirroringservices/cleanup/manager.go.close(work)/workerWG.Wait()on the cancellation path so no worker goroutine leaks, and surface the cancellation aserrors.Join(fanOutErr, firstErr)instead of swallowing it.Pending, so the next sweep re-claims them once their lease expires — no work is lost.docs/services/storage/recovery.md: new "Shutdown Behaviour" section documenting the above.Test
TestManager_StopDuringFanOutDoesNotDeadlockreproduces the issue's numbered failure scenario step by step: 64 claims to a single worker parked insideRecover(fan-out blocked on the unbuffered send),Stop()from a goroutine, worker released only after cancellation so both its select arms are ready, then a guard onStop()returning and a finalStart()/Stop()pair proving the manager is not wedged.Verified both directions: it fails against the unguarded send (
Stop() deadlocked while the sweep was fanning out claims) and passes with the fix under-count=3 -race.gofmt,go vet, andgolangci-lintare clean.