github.com/decred/dcrlnd@v0.7.6/discovery/reliable_sender.go (about)

     1  package discovery
     2  
     3  import (
     4  	"sync"
     5  
     6  	"github.com/decred/dcrlnd/lnpeer"
     7  	"github.com/decred/dcrlnd/lnwire"
     8  )
     9  
    10  // reliableSenderCfg contains all of necessary items for the reliableSender to
    11  // carry out its duties.
    12  type reliableSenderCfg struct {
    13  	// NotifyWhenOnline is a function that allows the gossiper to be
    14  	// notified when a certain peer comes online, allowing it to
    15  	// retry sending a peer message.
    16  	//
    17  	// NOTE: The peerChan channel must be buffered.
    18  	NotifyWhenOnline func(peerPubKey [33]byte, peerChan chan<- lnpeer.Peer)
    19  
    20  	// NotifyWhenOffline is a function that allows the gossiper to be
    21  	// notified when a certain peer disconnects, allowing it to request a
    22  	// notification for when it reconnects.
    23  	NotifyWhenOffline func(peerPubKey [33]byte) <-chan struct{}
    24  
    25  	// MessageStore is a persistent storage of gossip messages which we will
    26  	// use to determine which messages need to be resent for a given peer.
    27  	MessageStore GossipMessageStore
    28  
    29  	// IsMsgStale determines whether a message retrieved from the backing
    30  	// MessageStore is seen as stale by the current graph.
    31  	IsMsgStale func(lnwire.Message) bool
    32  }
    33  
    34  // peerManager contains the set of channels required for the peerHandler to
    35  // properly carry out its duties.
    36  type peerManager struct {
    37  	// msgs is the channel through which messages will be streamed to the
    38  	// handler in order to send the message to the peer while they're
    39  	// online.
    40  	msgs chan lnwire.Message
    41  
    42  	// done is a channel that will be closed to signal that the handler for
    43  	// the given peer has been torn down for whatever reason.
    44  	done chan struct{}
    45  }
    46  
    47  // reliableSender is a small subsystem of the gossiper used to reliably send
    48  // gossip messages to peers.
    49  type reliableSender struct {
    50  	start sync.Once
    51  	stop  sync.Once
    52  
    53  	cfg reliableSenderCfg
    54  
    55  	// activePeers keeps track of whether a peerHandler exists for a given
    56  	// peer. A peerHandler is tasked with handling requests for messages
    57  	// that should be reliably sent to peers while also taking into account
    58  	// the peer's connection lifecycle.
    59  	activePeers    map[[33]byte]peerManager
    60  	activePeersMtx sync.Mutex
    61  
    62  	wg   sync.WaitGroup
    63  	quit chan struct{}
    64  }
    65  
    66  // newReliableSender returns a new reliableSender backed by the given config.
    67  func newReliableSender(cfg *reliableSenderCfg) *reliableSender {
    68  	return &reliableSender{
    69  		cfg:         *cfg,
    70  		activePeers: make(map[[33]byte]peerManager),
    71  		quit:        make(chan struct{}),
    72  	}
    73  }
    74  
    75  // Start spawns message handlers for any peers with pending messages.
    76  func (s *reliableSender) Start() error {
    77  	var err error
    78  	s.start.Do(func() {
    79  		err = s.resendPendingMsgs()
    80  	})
    81  	return err
    82  }
    83  
    84  // Stop halts the reliable sender from sending messages to peers.
    85  func (s *reliableSender) Stop() {
    86  	s.stop.Do(func() {
    87  		close(s.quit)
    88  		s.wg.Wait()
    89  	})
    90  }
    91  
    92  // sendMessage constructs a request to send a message reliably to a peer. In the
    93  // event that the peer is currently offline, this will only write the message to
    94  // disk. Once the peer reconnects, this message, along with any others pending,
    95  // will be sent to the peer.
    96  func (s *reliableSender) sendMessage(msg lnwire.Message, peerPubKey [33]byte) error {
    97  	// We'll start by persisting the message to disk. This allows us to
    98  	// resend the message upon restarts and peer reconnections.
    99  	if err := s.cfg.MessageStore.AddMessage(msg, peerPubKey); err != nil {
   100  		return err
   101  	}
   102  
   103  	// Then, we'll spawn a peerHandler for this peer to handle resending its
   104  	// pending messages while taking into account its connection lifecycle.
   105  spawnHandler:
   106  	msgHandler, ok := s.spawnPeerHandler(peerPubKey)
   107  
   108  	// If the handler wasn't previously active, we can exit now as we know
   109  	// that the message will be sent once the peer online notification is
   110  	// received. This prevents us from potentially sending the message
   111  	// twice.
   112  	if !ok {
   113  		return nil
   114  	}
   115  
   116  	// Otherwise, we'll attempt to stream the message to the handler.
   117  	// There's a subtle race condition where the handler can be torn down
   118  	// due to all of the messages sent being stale, so we'll handle this
   119  	// gracefully by spawning another one to prevent blocking.
   120  	select {
   121  	case msgHandler.msgs <- msg:
   122  	case <-msgHandler.done:
   123  		goto spawnHandler
   124  	case <-s.quit:
   125  		return ErrGossiperShuttingDown
   126  	}
   127  
   128  	return nil
   129  }
   130  
   131  // spawnPeerMsgHandler spawns a peerHandler for the given peer if there isn't
   132  // one already active. The boolean returned signals whether there was already
   133  // one active or not.
   134  func (s *reliableSender) spawnPeerHandler(
   135  	peerPubKey [33]byte) (peerManager, bool) {
   136  
   137  	s.activePeersMtx.Lock()
   138  	msgHandler, ok := s.activePeers[peerPubKey]
   139  	if !ok {
   140  		msgHandler = peerManager{
   141  			msgs: make(chan lnwire.Message),
   142  			done: make(chan struct{}),
   143  		}
   144  		s.activePeers[peerPubKey] = msgHandler
   145  	}
   146  	s.activePeersMtx.Unlock()
   147  
   148  	// If this is a newly initiated peerManager, we will create a
   149  	// peerHandler.
   150  	if !ok {
   151  		s.wg.Add(1)
   152  		go s.peerHandler(msgHandler, peerPubKey)
   153  	}
   154  
   155  	return msgHandler, ok
   156  }
   157  
   158  // peerHandler is responsible for handling our reliable message send requests
   159  // for a given peer while also taking into account the peer's connection
   160  // lifecycle. Any messages that are attempted to be sent while the peer is
   161  // offline will be queued and sent once the peer reconnects.
   162  //
   163  // NOTE: This must be run as a goroutine.
   164  func (s *reliableSender) peerHandler(peerMgr peerManager, peerPubKey [33]byte) {
   165  	defer s.wg.Done()
   166  
   167  	// We'll start by requesting a notification for when the peer
   168  	// reconnects.
   169  	peerChan := make(chan lnpeer.Peer, 1)
   170  
   171  waitUntilOnline:
   172  	log.Debugf("Requesting online notification for peer=%x", peerPubKey)
   173  
   174  	s.cfg.NotifyWhenOnline(peerPubKey, peerChan)
   175  
   176  	var peer lnpeer.Peer
   177  out:
   178  	for {
   179  		select {
   180  		// While we're waiting, we'll also consume any messages that
   181  		// must be sent to prevent blocking the caller. These can be
   182  		// ignored for now since the peer is currently offline. Once
   183  		// they reconnect, the messages will be sent since they should
   184  		// have been persisted to disk.
   185  		case msg := <-peerMgr.msgs:
   186  			// Retrieve the short channel ID for which this message
   187  			// applies for logging purposes. The error can be
   188  			// ignored as the store can only contain messages which
   189  			// have a ShortChannelID field.
   190  			shortChanID, _ := msgShortChanID(msg)
   191  			log.Debugf("Received request to send %v message for "+
   192  				"channel=%v while peer=%x is offline",
   193  				msg.MsgType(), shortChanID, peerPubKey)
   194  
   195  		case peer = <-peerChan:
   196  			break out
   197  
   198  		case <-s.quit:
   199  			return
   200  		}
   201  	}
   202  
   203  	log.Debugf("Peer=%x is now online, proceeding to send pending messages",
   204  		peerPubKey)
   205  
   206  	// Once we detect the peer has reconnected, we'll also request a
   207  	// notification for when they disconnect. We'll use this to make sure
   208  	// they haven't disconnected (in the case of a flappy peer, etc.) by the
   209  	// time we attempt to send them the pending messages.
   210  	log.Debugf("Requesting offline notification for peer=%x", peerPubKey)
   211  
   212  	offlineChan := s.cfg.NotifyWhenOffline(peerPubKey)
   213  
   214  	pendingMsgs, err := s.cfg.MessageStore.MessagesForPeer(peerPubKey)
   215  	if err != nil {
   216  		log.Errorf("Unable to retrieve pending messages for peer %x: %v",
   217  			peerPubKey, err)
   218  		return
   219  	}
   220  
   221  	// With the peer online, we can now proceed to send our pending messages
   222  	// for them.
   223  	for _, msg := range pendingMsgs {
   224  		// Retrieve the short channel ID for which this message applies
   225  		// for logging purposes. The error can be ignored as the store
   226  		// can only contain messages which have a ShortChannelID field.
   227  		shortChanID, _ := msgShortChanID(msg)
   228  
   229  		// Ensure the peer is still online right before sending the
   230  		// message.
   231  		select {
   232  		case <-offlineChan:
   233  			goto waitUntilOnline
   234  		default:
   235  		}
   236  
   237  		if err := peer.SendMessage(false, msg); err != nil {
   238  			log.Errorf("Unable to send %v message for channel=%v "+
   239  				"to %x: %v", msg.MsgType(), shortChanID,
   240  				peerPubKey, err)
   241  			goto waitUntilOnline
   242  		}
   243  
   244  		log.Debugf("Successfully sent %v message for channel=%v with "+
   245  			"peer=%x upon reconnection", msg.MsgType(), shortChanID,
   246  			peerPubKey)
   247  
   248  		// Now that the message has at least been sent once, we can
   249  		// check whether it's stale. This guarantees that
   250  		// AnnounceSignatures are sent at least once if we happen to
   251  		// already have signatures for both parties.
   252  		if s.cfg.IsMsgStale(msg) {
   253  			err := s.cfg.MessageStore.DeleteMessage(msg, peerPubKey)
   254  			if err != nil {
   255  				log.Errorf("Unable to remove stale %v message "+
   256  					"for channel=%v with peer %x: %v",
   257  					msg.MsgType(), shortChanID, peerPubKey,
   258  					err)
   259  				continue
   260  			}
   261  
   262  			log.Debugf("Removed stale %v message for channel=%v "+
   263  				"with peer=%x", msg.MsgType(), shortChanID,
   264  				peerPubKey)
   265  		}
   266  	}
   267  
   268  	// If all of our messages were stale, then there's no need for this
   269  	// handler to continue running, so we can exit now.
   270  	pendingMsgs, err = s.cfg.MessageStore.MessagesForPeer(peerPubKey)
   271  	if err != nil {
   272  		log.Errorf("Unable to retrieve pending messages for peer %x: %v",
   273  			peerPubKey, err)
   274  		return
   275  	}
   276  
   277  	if len(pendingMsgs) == 0 {
   278  		log.Debugf("No pending messages left for peer=%x", peerPubKey)
   279  
   280  		s.activePeersMtx.Lock()
   281  		delete(s.activePeers, peerPubKey)
   282  		s.activePeersMtx.Unlock()
   283  
   284  		close(peerMgr.done)
   285  
   286  		return
   287  	}
   288  
   289  	// Once the pending messages are sent, we can continue to send any
   290  	// future messages while the peer remains connected.
   291  	for {
   292  		select {
   293  		case msg := <-peerMgr.msgs:
   294  			// Retrieve the short channel ID for which this message
   295  			// applies for logging purposes. The error can be
   296  			// ignored as the store can only contain messages which
   297  			// have a ShortChannelID field.
   298  			shortChanID, _ := msgShortChanID(msg)
   299  
   300  			if err := peer.SendMessage(false, msg); err != nil {
   301  				log.Errorf("Unable to send %v message for "+
   302  					"channel=%v to %x: %v", msg.MsgType(),
   303  					shortChanID, peerPubKey, err)
   304  			}
   305  
   306  			log.Debugf("Successfully sent %v message for "+
   307  				"channel=%v with peer=%x", msg.MsgType(),
   308  				shortChanID, peerPubKey)
   309  
   310  		case <-offlineChan:
   311  			goto waitUntilOnline
   312  
   313  		case <-s.quit:
   314  			return
   315  		}
   316  	}
   317  }
   318  
   319  // resendPendingMsgs retrieves and sends all of the messages within the message
   320  // store that should be reliably sent to their respective peers.
   321  func (s *reliableSender) resendPendingMsgs() error {
   322  	// Fetch all of the peers for which we have pending messages for and
   323  	// spawn a peerMsgHandler for each. Once the peer is seen as online, all
   324  	// of the pending messages will be sent.
   325  	peers, err := s.cfg.MessageStore.Peers()
   326  	if err != nil {
   327  		return err
   328  	}
   329  
   330  	for peer := range peers {
   331  		s.spawnPeerHandler(peer)
   332  	}
   333  
   334  	return nil
   335  }