discovery: add support for PinnedSyncers

A pinned syncer is an ActiveSyncer that is configured to always remain
active for the lifetime of the connection. Pinned syncers do not count
towards the total NumActiveSyncer count, which are rotated periodically.

This features allows nodes to more tightly synchronize their routing
tables by ensuring they are always receiving gossip from distinguished
subset of peers.
This commit is contained in:
Conner Fromknecht
2021-01-29 00:13:38 -08:00
parent 9e932f2a64
commit 2f0d56d539
4 changed files with 114 additions and 8 deletions

View File

@@ -92,6 +92,11 @@ type SyncManagerCfg struct {
// BestHeight returns the latest height known of the chain.
BestHeight func() uint32
// PinnedSyncers is a set of peers that will always transition to
// ActiveSync upon connection. These peers will never transition to
// PassiveSync.
PinnedSyncers PinnedSyncers
}
// SyncManager is a subsystem of the gossiper that manages the gossip syncers
@@ -140,6 +145,12 @@ type SyncManager struct {
// currently receiving new graph updates from.
inactiveSyncers map[route.Vertex]*GossipSyncer
// pinnedActiveSyncers is the set of all syncers which are pinned into
// an active sync. Pinned peers performan an initial historical sync on
// each connection and will continue to receive graph updates for the
// duration of the connection.
pinnedActiveSyncers map[route.Vertex]*GossipSyncer
wg sync.WaitGroup
quit chan struct{}
}
@@ -154,7 +165,10 @@ func newSyncManager(cfg *SyncManagerCfg) *SyncManager {
map[route.Vertex]*GossipSyncer, cfg.NumActiveSyncers,
),
inactiveSyncers: make(map[route.Vertex]*GossipSyncer),
quit: make(chan struct{}),
pinnedActiveSyncers: make(
map[route.Vertex]*GossipSyncer, len(cfg.PinnedSyncers),
),
quit: make(chan struct{}),
}
}
@@ -240,6 +254,8 @@ func (m *SyncManager) syncerHandler() {
s := m.createGossipSyncer(newSyncer.peer)
isPinnedSyncer := m.isPinnedSyncer(s)
// attemptHistoricalSync determines whether we should
// attempt an initial historical sync when a new peer
// connects.
@@ -247,6 +263,12 @@ func (m *SyncManager) syncerHandler() {
m.syncersMu.Lock()
switch {
// For pinned syncers, we will immediately transition
// the peer into an active (pinned) sync state.
case isPinnedSyncer:
s.setSyncType(PinnedSync)
m.pinnedActiveSyncers[s.cfg.peerPub] = s
// Regardless of whether the initial historical sync
// has completed, we'll re-trigger a historical sync if
// we no longer have any syncers. This might be
@@ -416,6 +438,13 @@ func (m *SyncManager) syncerHandler() {
}
}
// isPinnedSyncer returns true if the passed GossipSyncer is one of our pinned
// sync peers.
func (m *SyncManager) isPinnedSyncer(s *GossipSyncer) bool {
_, isPinnedSyncer := m.cfg.PinnedSyncers[s.cfg.peerPub]
return isPinnedSyncer
}
// createGossipSyncer creates the GossipSyncer for a newly connected peer.
func (m *SyncManager) createGossipSyncer(peer lnpeer.Peer) *GossipSyncer {
nodeID := route.Vertex(peer.PubKey())
@@ -474,6 +503,13 @@ func (m *SyncManager) removeGossipSyncer(peer route.Vertex) {
return
}
// If it's a pinned syncer, then we can just exit as this doesn't
// affect our active syncer count.
if _, ok := m.pinnedActiveSyncers[peer]; ok {
delete(m.pinnedActiveSyncers, peer)
return
}
// Otherwise, we'll need find a new one to replace it, if any.
delete(m.activeSyncers, peer)
newActiveSyncer := chooseRandomSyncer(
@@ -676,6 +712,10 @@ func (m *SyncManager) gossipSyncer(peer route.Vertex) (*GossipSyncer, bool) {
if ok {
return syncer, true
}
syncer, ok = m.pinnedActiveSyncers[peer]
if ok {
return syncer, true
}
return nil, false
}