github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/module/upstream/upstream_connector.go (about)

     1  package upstream
     2  
     3  import (
     4  	"context"
     5  	"sync"
     6  	"time"
     7  
     8  	"github.com/onflow/flow-go/network/p2p"
     9  
    10  	"github.com/rs/zerolog"
    11  	"github.com/sethvargo/go-retry"
    12  	"go.uber.org/atomic"
    13  
    14  	"github.com/onflow/flow-go/network/p2p/utils"
    15  
    16  	"github.com/onflow/flow-go/model/flow"
    17  	"github.com/onflow/flow-go/module/lifecycle"
    18  )
    19  
    20  // upstreamConnector tries to connect the unstaked AN with atleast one of the configured bootstrap access nodes
    21  type upstreamConnector struct {
    22  	lm                  *lifecycle.LifecycleManager
    23  	bootstrapIdentities flow.IdentitySkeletonList
    24  	logger              zerolog.Logger
    25  	unstakedNode        p2p.LibP2PNode
    26  	cancel              context.CancelFunc
    27  	retryInitialTimeout time.Duration
    28  	maxRetries          uint64
    29  }
    30  
    31  func NewUpstreamConnector(bootstrapIdentities flow.IdentitySkeletonList, unstakedNode p2p.LibP2PNode, logger zerolog.Logger) *upstreamConnector {
    32  	return &upstreamConnector{
    33  		lm:                  lifecycle.NewLifecycleManager(),
    34  		bootstrapIdentities: bootstrapIdentities,
    35  		unstakedNode:        unstakedNode,
    36  		logger:              logger,
    37  		retryInitialTimeout: time.Second,
    38  		maxRetries:          5,
    39  	}
    40  }
    41  func (connector *upstreamConnector) Ready() <-chan struct{} {
    42  	connector.lm.OnStart(func() {
    43  		// eventually, context will be passed in to Start method: https://github.com/dapperlabs/flow-go/issues/5730
    44  		ctx, cancel := context.WithCancel(context.TODO())
    45  		connector.cancel = cancel
    46  
    47  		workerCtx, cancel := context.WithCancel(ctx)
    48  		defer cancel()
    49  
    50  		success := atomic.NewBool(false)
    51  		var wg sync.WaitGroup
    52  
    53  		// spawn a connect worker for each bootstrap node
    54  		for _, b := range connector.bootstrapIdentities {
    55  			id := *b
    56  			wg.Add(1)
    57  			go func() {
    58  				defer wg.Done()
    59  				lg := connector.logger.With().Str("bootstrap_node", id.NodeID.String()).Logger()
    60  
    61  				backoff := retry.NewFibonacci(connector.retryInitialTimeout)
    62  				backoff = retry.WithMaxRetries(connector.maxRetries, backoff)
    63  
    64  				if err := retry.Do(workerCtx, backoff, func(ctx context.Context) error {
    65  					return retry.RetryableError(connector.connect(ctx, id))
    66  				}); err != nil {
    67  					lg.Err(err).Msg("failed to connect")
    68  				} else {
    69  					lg.Info().Msg("successfully connected to bootstrap node")
    70  					success.Store(true)
    71  				}
    72  			}()
    73  		}
    74  
    75  		wg.Wait()
    76  
    77  		if !success.Load() {
    78  			// log fatal as there is no point continuing further, the unstaked AN cannot connect to any of the bootstrap peers
    79  			connector.logger.Fatal().
    80  				Msg("Failed to connect to a bootstrap node. " +
    81  					"Please ensure the network address and public key of the bootstrap access node are correct " +
    82  					"and that the node is running and reachable.")
    83  		}
    84  	})
    85  	return connector.lm.Started()
    86  }
    87  
    88  // connect is run to connect to an boostrap peer
    89  func (connector *upstreamConnector) connect(ctx context.Context, bootstrapPeer flow.IdentitySkeleton) error {
    90  
    91  	select {
    92  	// check for a cancelled/expired context
    93  	case <-ctx.Done():
    94  		return ctx.Err()
    95  	default:
    96  	}
    97  
    98  	peerAddrInfo, err := utils.PeerAddressInfo(bootstrapPeer)
    99  
   100  	if err != nil {
   101  		return err
   102  	}
   103  
   104  	// try and connect to the bootstrap server
   105  	return connector.unstakedNode.ConnectToPeer(ctx, peerAddrInfo)
   106  }
   107  
   108  func (connector *upstreamConnector) Done() <-chan struct{} {
   109  	connector.lm.OnStop(func() {
   110  		// this function will only be executed if connector.lm.OnStart was previously called,
   111  		// in which case connector.cancel != nil
   112  		connector.cancel()
   113  	})
   114  	return connector.lm.Stopped()
   115  }