eth/connmanager: monitor sync status

Signed-off-by: Csaba Kiraly <csaba.kiraly@gmail.com>
This commit is contained in:
Csaba Kiraly 2025-03-26 01:44:53 +01:00
parent cb5d672649
commit 301b396939
2 changed files with 61 additions and 29 deletions

View file

@ -405,7 +405,7 @@ func (s *Ethereum) Start() error {
s.handler.Start(s.p2pServer.MaxPeers) s.handler.Start(s.p2pServer.MaxPeers)
// Start the connection manager // Start the connection manager
s.connman.Start(s.p2pServer, s.Synced) s.connman.Start(s.p2pServer, func() bool { return !s.Synced() })
// start log indexer // start log indexer
s.filterMaps.Start() s.filterMaps.Start()

View file

@ -40,15 +40,18 @@ const (
// dropping when no more peers can be added. Larger numbers result in more // dropping when no more peers can be added. Larger numbers result in more
// aggressive drop behavior. // aggressive drop behavior.
peerDropThreshold = 0 peerDropThreshold = 0
// Sync status poll interval (no need to be too reactive here)
syncCheckInterval = 60 * time.Second
) )
// connManager monitors the state of the peer pool and makes changes as follows: // connManager monitors the state of the peer pool and makes changes as follows:
// - if the peer count is close to the limit, it drops peers randomly every // - during sync the Downloader handles peer connections co connManager is disabled
// peerDropInterval to make space for new peers // - if not syncing and the peer count is close to the limit, it drops peers
// randomly every peerDropInterval to make space for new peers
type connManager struct { type connManager struct {
connmanConfig connmanConfig
peersFunc getPeersFunc peersFunc getPeersFunc
syncFunc getSyncFunc syncingFunc getSyncingFunc
// the peerDrop timer introduces churn if we are close to limit capacity // the peerDrop timer introduces churn if we are close to limit capacity
peerDropTimer *mclock.Alarm peerDropTimer *mclock.Alarm
@ -62,8 +65,9 @@ type connManager struct {
// callback type to get the list of connected peers. // callback type to get the list of connected peers.
type getPeersFunc func() []*p2p.Peer type getPeersFunc func() []*p2p.Peer
// callback type to get sync status. // Callback type to get syncing status.
type getSyncFunc func() bool // Returns true while syncing, false when synced.
type getSyncingFunc func() bool
type connmanConfig struct { type connmanConfig struct {
maxDialPeers int // maximum number of dialed peers maxDialPeers int // maximum number of dialed peers
@ -100,15 +104,16 @@ func newConnManager(config *connmanConfig) *connManager {
return cm return cm
} }
func (cm *connManager) Start(srv *p2p.Server, syncFunc getSyncFunc) { // Start the connection manager.
func (cm *connManager) Start(srv *p2p.Server, syncingFunc getSyncingFunc) {
cm.wg.Add(1) cm.wg.Add(1)
cm.peersFunc = srv.Peers cm.peersFunc = srv.Peers
cm.syncFunc = syncFunc cm.syncingFunc = syncingFunc
cm.sub = srv.SubscribeEvents(cm.peerEventCh) cm.sub = srv.SubscribeEvents(cm.peerEventCh)
go cm.loop() go cm.loop()
} }
// stop the connection manager. // Stop the connection manager.
func (cm *connManager) Stop() { func (cm *connManager) Stop() {
cm.sub.Unsubscribe() cm.sub.Unsubscribe()
cm.peerDropTimer.Stop() cm.peerDropTimer.Stop()
@ -149,32 +154,59 @@ func (cm *connManager) dropRandomPeer() bool {
return false return false
} }
// updatePeerDropTimer checks and starts/stops the timer for peer drop.
func (cm *connManager) updatePeerDropTimer(syncing bool) {
numpeers, out, in := cm.numPeers()
cm.log.Trace("ConnManager status", "syncing", syncing,
"peers", numpeers, "out", out, "in", in, "maxout", cm.maxDialPeers)
if !syncing {
// If a drop was already scheduled, Schedule does nothing.
if cm.maxDialPeers-cm.numDialPeers() <= peerDropThreshold {
cm.peerDropTimer.Schedule(cm.clock.Now().Add(peerDropInterval))
} else {
cm.peerDropTimer.Stop()
}
} else {
// Downloader is managing connections while syncing.
cm.peerDropTimer.Stop()
}
}
// loop is the main loop of the connection manager. // loop is the main loop of the connection manager.
func (cm *connManager) loop() { func (cm *connManager) loop() {
defer cm.wg.Done() defer cm.wg.Done()
// Set up periodic timer to pull syncing status.
// We could get syncing status in a few ways:
// - poll the sync status (we use this for now)
// - subscribe to Downloader.mux
// - subscribe to DownloaderAPI (which itself polls the sync status)
syncing := cm.syncingFunc()
cm.log.Trace("Sync status", "syncing", syncing)
syncCheckTimer := mclock.NewAlarm(cm.connmanConfig.clock)
syncCheckTimer.Schedule(cm.clock.Now().Add(syncCheckInterval))
defer syncCheckTimer.Stop()
for { for {
select { select {
case ev := <-cm.peerEventCh: case <-syncCheckTimer.C():
switch ev.Type { // Update info about syncing status, and rearm the timer.
case p2p.PeerEventTypeAdd: syncingNew := cm.syncingFunc()
// check and start timer for peer drop if syncing != syncingNew {
// If a drop was already scheduled, Schedule does nothing. // Syncing status changed, we might need to update the timer.
numpeers, out, in := cm.numPeers() cm.log.Trace("Sync status changed", "syncing", syncingNew)
cm.log.Trace("addPeerCh", "peers", numpeers, "out", out, "in", in, "maxout", cm.maxDialPeers) syncing = syncingNew
if cm.maxDialPeers-cm.numDialPeers() <= peerDropThreshold { cm.updatePeerDropTimer(syncing)
cm.peerDropTimer.Schedule(cm.clock.Now().Add(peerDropInterval)) }
} syncCheckTimer.Schedule(cm.clock.Now().Add(syncCheckInterval))
case ev := <-cm.peerEventCh:
case p2p.PeerEventTypeDrop: if ev.Type == p2p.PeerEventTypeAdd || ev.Type == p2p.PeerEventTypeDrop {
// check and stop timer for peer drop // Number of peers changed, we might need to start the timer.
numpeers, out, in := cm.numPeers() cm.updatePeerDropTimer(syncing)
cm.log.Trace("remPeerCh", "peers", numpeers, "out", out, "in", in, "maxout", cm.maxDialPeers)
if cm.maxDialPeers-cm.numDialPeers() > peerDropThreshold {
cm.peerDropTimer.Stop()
}
} }
case <-cm.peerDropTimer.C(): case <-cm.peerDropTimer.C():
cm.dropRandomPeer() cm.dropRandomPeer()
case <-cm.shutdownCh: case <-cm.shutdownCh: