Fix panic on peer shutdown during block commit - #5393
Conversation
Wait for deliverPayloads goroutine to complete before closing ledger resources in GossipStateProviderImpl.Stop(), preventing send on closed snapshotMgr channels. Signed-off-by: Ady0333 <adityashinde1525@gmail.com>
|
Hi @yacovm , When you get a chance, please take a look at this PR. |
|
@Ady0333 can you please add unit test? |
|
Thank you for the contribution. We currently have neither a peer node stop nor a peer node shutdown command. Because of this, I’m not sure what this PR is intended to address. Our existing shutdown APIs are only used within tests to close services after a unit test run. |
|
@cendhu My understanding was that snapshotMgr.shutdown() can close channels while snapshot generation goroutines are still active, which could lead to a send-on-closed-channel panic. Even if shutdown is currently only used in tests, this panic could still cause non-deterministic failures in test environments where snapshots are generated concurrently. The intent of the fix was to prevent this race and ensure shutdown happens safely even when snapshot generation is in progress. Please let me know if this scenario is not considered relevant for Fabric, and I can close the PR if needed. |
I agree with @cendhu . |
|
@pfi79 Thankyou for keeping your view on this. I agree with your point and I will add an integration test for this fix shortly. |
Atishyy27
left a comment
There was a problem hiding this comment.
I traced the panic this fixes: Stop() closes stopCh and then immediately s.ledger.Close(), so a deliverPayloads inside commitBlock fails the commit and hits the Panicf at the bottom of the pop loop (state.go L576). Waiting for deliverPayloads before closing the ledger is the right ordering, and the Add(1)-before-go placement is correct.
Two suggestions:
- The comment says "wait for all goroutines … don't send on closed channels", but the WaitGroup only tracks
deliverPayloads. The senders intostateRequestCh/stateResponseChindirectMessage(L394-402) useselect { case ch <- msg: case <-s.stopCh: }, and select chooses randomly when both cases are ready — so a send onto the just-closed channel is still possible afterStop(). Either extending the WaitGroup to those paths or narrowing the comment to what's actually guaranteed would avoid giving future readers false confidence. - A regression test — a mock ledger whose
StoreBlockblocks until released, callStop()concurrently, assert no panic and thatClose()happens after the commit returns — would lock this ordering in.
Type of change
Description
This PR fixes a shutdown race in GossipStateProviderImpl that could cause a peer to panic during block commit.
On shutdown, Stop() closed the ledger immediately after signaling stopCh, while the deliverPayloads() goroutine could still be committing blocks. That commit path sends events to the snapshot manager, whose channels are closed as part of ledger.Close(). If shutdown overlaps with an in-flight commit, this results in a send on closed channel panic.
The fix waits for deliverPayloads() to complete before closing ledger resources and adds a stop check in the payload loop so shutdown exits promptly.
Additional details
The shutdown path is now coordinated with block commit to ensure no snapshot manager channels are closed while still in use.
Tested locally with
go test ./gossip/state/....Release Note
Fixes a peer shutdown race that could cause a panic during block commit when
snapshot manager channels were closed while still in use.