mirror of https://github.com/BTCPrivate/lnd.git
routing: implement 2-week zombie channel pruning
This commit implements 2-week zombie channel pruning. This means that every GraphPruneInterval (currently set to one hour), we’ll scan the channel graph, marking any channels which haven’t had *both* edges updated in 2 weeks as a “zombie”. During the second pass, all “zombie” channel are removed from the channel graph all together. Adding this functionality means we’ll ensure that we maintain a “healthy” network view, which will cut down on the number of failed HTLC routing attempts, and also reflect an active portion of the graph.
This commit is contained in:
parent
e81689057a
commit
3b7855e449
|
@ -110,6 +110,16 @@ type Config struct {
|
||||||
// payment was unsuccessful.
|
// payment was unsuccessful.
|
||||||
SendToSwitch func(firstHop *btcec.PublicKey, htlcAdd *lnwire.UpdateAddHTLC,
|
SendToSwitch func(firstHop *btcec.PublicKey, htlcAdd *lnwire.UpdateAddHTLC,
|
||||||
circuit *sphinx.Circuit) ([sha256.Size]byte, error)
|
circuit *sphinx.Circuit) ([sha256.Size]byte, error)
|
||||||
|
|
||||||
|
// ChannelPruneExpiry is the duration used to determine if a channel
|
||||||
|
// should be pruned or not. If the delta between now and when the
|
||||||
|
// channel was last updated is greater than ChannelPruneExpiry, then
|
||||||
|
// the channel is marked as a zombie channel eligible for pruning.
|
||||||
|
ChannelPruneExpiry time.Duration
|
||||||
|
|
||||||
|
// GraphPruneInterval is used as an interval to determine how often we
|
||||||
|
// should examine the channel graph to garbage collect zombie channels.
|
||||||
|
GraphPruneInterval time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// routeTuple is an entry within the ChannelRouter's route cache. We cache
|
// routeTuple is an entry within the ChannelRouter's route cache. We cache
|
||||||
|
@ -382,7 +392,8 @@ func (r *ChannelRouter) syncGraphWithChain() error {
|
||||||
func (r *ChannelRouter) networkHandler() {
|
func (r *ChannelRouter) networkHandler() {
|
||||||
defer r.wg.Done()
|
defer r.wg.Done()
|
||||||
|
|
||||||
// TODO(roasbeef): ticker to check if should prune in two weeks or not
|
graphPruneTicker := time.NewTicker(r.cfg.GraphPruneInterval)
|
||||||
|
defer graphPruneTicker.Stop()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
@ -508,6 +519,75 @@ func (r *ChannelRouter) networkHandler() {
|
||||||
exit: make(chan struct{}),
|
exit: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The graph prune ticker has ticked, so we'll examine the
|
||||||
|
// state of the known graph to filter out any zombie channels
|
||||||
|
// for pruning.
|
||||||
|
case <-graphPruneTicker.C:
|
||||||
|
|
||||||
|
var chansToPrune []wire.OutPoint
|
||||||
|
chanExpiry := r.cfg.ChannelPruneExpiry
|
||||||
|
|
||||||
|
log.Infof("Examining Channel Graph for zombie channels")
|
||||||
|
|
||||||
|
// First, we'll collect all the channels which are
|
||||||
|
// eligible for garbage collection due to being
|
||||||
|
// zombies.
|
||||||
|
filterPruneChans := func(info *channeldb.ChannelEdgeInfo,
|
||||||
|
e1, e2 *channeldb.ChannelEdgePolicy) error {
|
||||||
|
|
||||||
|
// If *both* edges haven't been updated for a
|
||||||
|
// period of chanExpiry, then we'll mark the
|
||||||
|
// channel itself as eligible for graph
|
||||||
|
// pruning.
|
||||||
|
e1Zombie, e2Zombie := true, true
|
||||||
|
if e1 != nil {
|
||||||
|
e1Zombie = time.Since(e1.LastUpdate) >= chanExpiry
|
||||||
|
log.Tracef("Edge #1 of ChannelPoint(%v) "+
|
||||||
|
"last update: %v",
|
||||||
|
info.ChannelPoint, e1.LastUpdate)
|
||||||
|
}
|
||||||
|
if e2 != nil {
|
||||||
|
e2Zombie = time.Since(e2.LastUpdate) >= chanExpiry
|
||||||
|
log.Tracef("Edge #2 of ChannelPoint(%v) "+
|
||||||
|
"last update: %v",
|
||||||
|
info.ChannelPoint, e2.LastUpdate)
|
||||||
|
}
|
||||||
|
if e1Zombie && e2Zombie {
|
||||||
|
log.Infof("ChannelPoint(%v) is a "+
|
||||||
|
"zombie, collecting to prune",
|
||||||
|
info.ChannelPoint)
|
||||||
|
|
||||||
|
// TODO(roasbeef): add ability to
|
||||||
|
// delete single directional edge
|
||||||
|
chansToPrune = append(chansToPrune,
|
||||||
|
info.ChannelPoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := r.cfg.Graph.ForEachChannel(filterPruneChans)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Unable to local zombie chans: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("Pruning %v Zombie Channels", len(chansToPrune))
|
||||||
|
|
||||||
|
// With the set zombie-like channels obtained, we'll do
|
||||||
|
// another pass to delete al zombie channels from the
|
||||||
|
// channel graph.
|
||||||
|
for _, chanToPrune := range chansToPrune {
|
||||||
|
log.Tracef("Pruning zombie chan ChannelPoint(%v)",
|
||||||
|
chanToPrune)
|
||||||
|
|
||||||
|
err := r.cfg.Graph.DeleteChannelEdge(&chanToPrune)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Unable to prune zombie "+
|
||||||
|
"chans: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The router has been signalled to exit, to we exit our main
|
// The router has been signalled to exit, to we exit our main
|
||||||
// loop so the wait group can be decremented.
|
// loop so the wait group can be decremented.
|
||||||
case <-r.quit:
|
case <-r.quit:
|
||||||
|
|
|
@ -84,6 +84,8 @@ func createTestCtx(startingHeight uint32, testGraph ...string) (*testCtx, func()
|
||||||
_ *lnwire.UpdateAddHTLC, _ *sphinx.Circuit) ([32]byte, error) {
|
_ *lnwire.UpdateAddHTLC, _ *sphinx.Circuit) ([32]byte, error) {
|
||||||
return [32]byte{}, nil
|
return [32]byte{}, nil
|
||||||
},
|
},
|
||||||
|
ChannelPruneExpiry: time.Hour * 24,
|
||||||
|
GraphPruneInterval: time.Hour * 2,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("unable to create router %v", err)
|
return nil, nil, fmt.Errorf("unable to create router %v", err)
|
||||||
|
|
|
@ -266,6 +266,8 @@ func newServer(listenAddrs []string, chanDB *channeldb.DB, cc *chainControl,
|
||||||
|
|
||||||
return s.htlcSwitch.SendHTLC(firstHopPub, htlcAdd, errorDecryptor)
|
return s.htlcSwitch.SendHTLC(firstHopPub, htlcAdd, errorDecryptor)
|
||||||
},
|
},
|
||||||
|
ChannelPruneExpiry: time.Duration(time.Hour * 24 * 14),
|
||||||
|
GraphPruneInterval: time.Duration(time.Hour),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't create router: %v", err)
|
return nil, fmt.Errorf("can't create router: %v", err)
|
||||||
|
|
Loading…
Reference in New Issue