discovery: fix endless loop in gossip syncer on context cancellation

This commit fixes a critical bug where the channelGraphSyncer goroutine
would enter an endless loop when context cancellation or peer disconnect
errors occurred during the syncingChans or queryNewChannels states.

The root cause was that state handler functions (handleSyncingChans and
synchronizeChanIDs) did not return errors to the main goroutine loop.
When these functions encountered fatal errors like context cancellation,
they would log the error and return early without changing the syncer's
state. This caused the main loop to immediately re-enter the same state
handler, encounter the same error, and loop indefinitely while spamming
error logs.

The fix makes error handling explicit by having state handlers return
errors. The main channelGraphSyncer loop now checks these errors and
exits cleanly when fatal errors occur. We return any error (not just
context cancellation) because fatal errors can manifest in multiple
forms: context.Canceled, ErrGossipSyncerExiting from the rate limiter,
lnpeer.ErrPeerExiting from Brontide, or network errors like connection
closed. This approach matches the error handling pattern already used in
other goroutines like replyHandler.
This commit is contained in:
Olaoluwa Osuntokun committed 2025-11-03 10:44:12 -08:00
1 parent f938e40afe
commit 06c7d60452
2 files changed
+45 -12

No files matched your search

+4 -2
View File
@@ -1518,7 +1518,8 @@ func TestGossipSyncerSynchronizeChanIDs(t *testing.T) {
for i := 0; i < chunkSize*2; i += 2 {
// With our set up complete, we'll request a sync of chan ID's.
done := syncer.synchronizeChanIDs(t.Context())
done, err := syncer.synchronizeChanIDs(t.Context())
require.NoError(t, err)
// At this point, we shouldn't yet be done as only 2 items
// should have been queried for.
@@ -1565,7 +1566,8 @@ func TestGossipSyncerSynchronizeChanIDs(t *testing.T) {
}
// If we issue another query, the syncer should tell us that it's done.
done := syncer.synchronizeChanIDs(t.Context())
done, err := syncer.synchronizeChanIDs(t.Context())
require.NoError(t, err)
if done {
t.Fatalf("syncer should be finished!")
}