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 }