github.com/status-im/status-go@v1.1.0/metrics/node/subscribe.go (about)

     1  package node
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  
     7  	"github.com/ethereum/go-ethereum/log"
     8  	"github.com/ethereum/go-ethereum/node"
     9  	"github.com/ethereum/go-ethereum/p2p"
    10  )
    11  
    12  // All general log messages in this package should be routed through this logger.
    13  var logger = log.New("package", "status-go/metrics/node")
    14  
    15  // SubscribeServerEvents subscribes to server and listens to
    16  // PeerEventTypeAdd and PeerEventTypeDrop events.
    17  func SubscribeServerEvents(ctx context.Context, node *node.Node) error {
    18  	server := node.Server()
    19  
    20  	if server == nil {
    21  		return errors.New("server is unavailable")
    22  	}
    23  
    24  	ch := make(chan *p2p.PeerEvent, server.MaxPeers)
    25  	subscription := server.SubscribeEvents(ch)
    26  	defer subscription.Unsubscribe()
    27  
    28  	logger.Debug("Subscribed to server events")
    29  
    30  	for {
    31  		select {
    32  		case event := <-ch:
    33  			if isAddDropPeerEvent(event.Type) {
    34  				// We start a goroutine here because updateNodeMetrics
    35  				// is calling a method on the p2p server, which
    36  				// blocks until the server is available:
    37  				// https://github.com/status-im/status-go/blob/e60f425b45d00d3880b42fdd77b460ec465a9f55/vendor/github.com/ethereum/go-ethereum/p2p/server.go#L301
    38  				// https://github.com/status-im/status-go/blob/e60f425b45d00d3880b42fdd77b460ec465a9f55/vendor/github.com/ethereum/go-ethereum/p2p/server.go#L746
    39  				// If there's back-pressure on the peer event feed
    40  				// https://github.com/status-im/status-go/blob/e60f425b45d00d3880b42fdd77b460ec465a9f55/vendor/github.com/ethereum/go-ethereum/p2p/server.go#L783
    41  				// The event channel above might become full while updateNodeMetrics
    42  				// is called, which means is never consumed, the server blocks on publishing on
    43  				// it, and the two will deadlock (server waits for the channel above to be consumed,
    44  				// this code waits for the server to respond to peerCount, which is in the same
    45  				// event loop).
    46  				// Calling it in a different go-routine will allow this code to keep
    47  				// processing peer added events, therefore the server will not lock and
    48  				// keep processing requests.
    49  				go func() {
    50  					if err := updateNodeMetrics(node, event.Type); err != nil {
    51  						logger.Error("failed to update node metrics", "err", err)
    52  					}
    53  				}()
    54  			}
    55  		case err := <-subscription.Err():
    56  			if err != nil {
    57  				logger.Error("Subscription failed", "err", err)
    58  			}
    59  			return err
    60  		case <-ctx.Done():
    61  			return nil
    62  		}
    63  	}
    64  }
    65  
    66  func isAddDropPeerEvent(eventType p2p.PeerEventType) bool {
    67  	return eventType == p2p.PeerEventTypeAdd || eventType == p2p.PeerEventTypeDrop
    68  }