diff --git a/routing/router.go b/routing/router.go index 0cb0ced8..dc133b9b 100644 --- a/routing/router.go +++ b/routing/router.go @@ -43,6 +43,12 @@ const ( // if a channel should be pruned or not. DefaultChannelPruneExpiry = time.Duration(time.Hour * 24 * 14) + // DefaultFirstTimePruneDelay is the time we'll wait after startup + // before attempting to prune the graph for zombie channels. We don't + // do it immediately after startup to allow lnd to start up without + // getting blocked by this job. + DefaultFirstTimePruneDelay = 30 * time.Second + // defaultStatInterval governs how often the router will log non-empty // stats related to processing new channels, updates, or node // announcements. @@ -306,6 +312,12 @@ type Config struct { // should examine the channel graph to garbage collect zombie channels. GraphPruneInterval time.Duration + // FirstTimePruneDelay is the time we'll wait after startup before + // attempting to prune the graph for zombie channels. We don't do it + // immediately after startup to allow lnd to start up without getting + // blocked by this job. + FirstTimePruneDelay time.Duration + // QueryBandwidth is a method that allows the router to query the lower // link layer to determine the up to date available bandwidth at a // prospective link to be traversed. If the link isn't available, then @@ -485,11 +497,21 @@ func (r *ChannelRouter) Start() error { // If AssumeChannelValid is present, then we won't rely on pruning // channels from the graph based on their spentness, but whether they - // are considered zombies or not. + // are considered zombies or not. We will start zombie pruning after a + // small delay, to avoid slowing down startup of lnd. if r.cfg.AssumeChannelValid { - if err := r.pruneZombieChans(); err != nil { - return err - } + time.AfterFunc(r.cfg.FirstTimePruneDelay, func() { + select { + case <-r.quit: + return + default: + } + + log.Info("Initial zombie prune starting") + if err := r.pruneZombieChans(); err != nil { + log.Errorf("Unable to prune zombies: %v", err) + } + }) } else { // Otherwise, we'll use our filtered chain view to prune // channels as soon as they are detected as spent on-chain. diff --git a/routing/router_test.go b/routing/router_test.go index 19ad1ef3..a7c082e9 100644 --- a/routing/router_test.go +++ b/routing/router_test.go @@ -1541,6 +1541,9 @@ func TestWakeUpOnStaleBranch(t *testing.T) { Control: makeMockControlTower(), ChannelPruneExpiry: time.Hour * 24, GraphPruneInterval: time.Hour * 2, + + // We'll set the delay to zero to prune immediately. + FirstTimePruneDelay: 0, }) if err != nil { t.Fatalf("unable to create router %v", err) @@ -2157,6 +2160,9 @@ func testPruneChannelGraphDoubleDisabled(t *testing.T, assumeValid bool) { if !assumeValid { assertChannelsPruned(t, ctx.graph, testChannels) } else { + // Sleep to allow the pruning to finish. + time.Sleep(200 * time.Millisecond) + prunedChannel := testChannels[len(testChannels)-1].ChannelID assertChannelsPruned(t, ctx.graph, testChannels, prunedChannel) } diff --git a/server.go b/server.go index 7c6681b6..a3240dee 100644 --- a/server.go +++ b/server.go @@ -769,20 +769,21 @@ func newServer(cfg *Config, listenAddrs []net.Addr, s.controlTower = routing.NewControlTower(paymentControl) s.chanRouter, err = routing.New(routing.Config{ - Graph: chanGraph, - Chain: cc.ChainIO, - ChainView: cc.ChainView, - Payer: s.htlcSwitch, - Control: s.controlTower, - MissionControl: s.missionControl, - SessionSource: paymentSessionSource, - ChannelPruneExpiry: routing.DefaultChannelPruneExpiry, - GraphPruneInterval: time.Duration(time.Hour), - QueryBandwidth: queryBandwidth, - AssumeChannelValid: cfg.Routing.AssumeChannelValid, - NextPaymentID: sequencer.NextID, - PathFindingConfig: pathFindingConfig, - Clock: clock.NewDefaultClock(), + Graph: chanGraph, + Chain: cc.ChainIO, + ChainView: cc.ChainView, + Payer: s.htlcSwitch, + Control: s.controlTower, + MissionControl: s.missionControl, + SessionSource: paymentSessionSource, + ChannelPruneExpiry: routing.DefaultChannelPruneExpiry, + GraphPruneInterval: time.Hour, + FirstTimePruneDelay: routing.DefaultFirstTimePruneDelay, + QueryBandwidth: queryBandwidth, + AssumeChannelValid: cfg.Routing.AssumeChannelValid, + NextPaymentID: sequencer.NextID, + PathFindingConfig: pathFindingConfig, + Clock: clock.NewDefaultClock(), }) if err != nil { return nil, fmt.Errorf("can't create router: %v", err)