go.uber.org/yarpc@v1.72.1/transport/tchannel/peer.go (about) 1 // Copyright (c) 2022 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package tchannel 22 23 import ( 24 "context" 25 "time" 26 27 "github.com/uber/tchannel-go" 28 "go.uber.org/yarpc/api/peer" 29 "go.uber.org/yarpc/peer/abstractpeer" 30 "go.uber.org/zap" 31 ) 32 33 type tchannelPeer struct { 34 *abstractpeer.Peer 35 36 transport *Transport 37 ch *tchannel.Channel 38 addr string 39 changed chan struct{} 40 released chan struct{} 41 timer *time.Timer 42 } 43 44 func newPeer(addr string, t *Transport, ch *tchannel.Channel) *tchannelPeer { 45 // Create a defused timer for later use. 46 timer := time.NewTimer(0) 47 if !timer.Stop() { 48 <-timer.C 49 } 50 51 return &tchannelPeer{ 52 addr: addr, 53 ch: ch, 54 Peer: abstractpeer.NewPeer(abstractpeer.PeerIdentifier(addr), t), 55 transport: t, 56 changed: make(chan struct{}, 1), 57 released: make(chan struct{}), 58 timer: timer, 59 } 60 } 61 62 func (p *tchannelPeer) maintainConnection() { 63 cancel := func() {} 64 65 backoff := p.transport.connBackoffStrategy.Backoff() 66 var attempts uint 67 68 // Wait for start (so we can be certain that we have a channel). 69 <-p.transport.once.Started() 70 pl := p.getRootPeers() 71 if pl == nil { 72 return 73 } 74 75 // Attempt to retain an open connection to each peer so long as it is 76 // retained. 77 for { 78 tp := pl.GetOrAdd(p.addr) 79 80 inbound, outbound := tp.NumConnections() 81 if inbound+outbound > 0 { 82 p.setConnectionStatus(peer.Available) 83 // Reset on success 84 attempts = 0 85 if !p.waitForChange() { 86 break 87 } 88 89 } else { 90 p.setConnectionStatus(peer.Connecting) 91 92 // Attempt to connect 93 ctx := context.Background() 94 ctx, cancel = context.WithTimeout(ctx, p.transport.connTimeout) 95 _, err := tp.Connect(ctx) 96 97 if err == nil { 98 p.setConnectionStatus(peer.Available) 99 } else { 100 p.setConnectionStatus(peer.Unavailable) 101 // Back-off on fail 102 if !p.sleep(backoff.Duration(attempts)) { 103 break 104 } 105 attempts++ 106 } 107 108 } 109 } 110 111 p.transport.connectorsGroup.Done() 112 cancel() 113 } 114 115 func (p *tchannelPeer) release() { 116 close(p.released) 117 } 118 119 func (p *tchannelPeer) setConnectionStatus(status peer.ConnectionStatus) { 120 p.transport.logger.Debug( 121 "peer status change", 122 zap.String("status", status.String()), 123 zap.String("peer", p.Peer.Identifier()), 124 zap.String("tchannel", "tchannel"), 125 ) 126 p.Peer.SetStatus(status) 127 p.Peer.NotifyStatusChanged() 128 } 129 130 func (p *tchannelPeer) notifyConnectionStatusChanged() { 131 select { 132 case p.changed <- struct{}{}: 133 default: 134 } 135 } 136 137 // waitForChange waits for the transport to send a peer connection status 138 // change notification, but exits early if the transport releases the peer or 139 // stops. waitForChange returns whether it is resuming due to a connection 140 // status change event. 141 func (p *tchannelPeer) waitForChange() (changed bool) { 142 select { 143 case <-p.changed: 144 return true 145 case <-p.released: 146 return false 147 case <-p.transport.once.Stopping(): 148 return false 149 } 150 } 151 152 // sleep waits for a duration, but exits early if the transport releases the 153 // peer or stops. sleep returns whether it successfully waited the entire 154 // duration. 155 func (p *tchannelPeer) sleep(delay time.Duration) (completed bool) { 156 p.timer.Reset(delay) 157 158 select { 159 case <-p.timer.C: 160 return true 161 case <-p.released: 162 case <-p.transport.once.Stopping(): 163 } 164 165 if !p.timer.Stop() { 166 <-p.timer.C 167 } 168 return false 169 } 170 171 func (p *tchannelPeer) getPeer() *tchannel.Peer { 172 return p.getRootPeers().GetOrAdd(p.HostPort()) 173 } 174 175 func (p *tchannelPeer) getRootPeers() *tchannel.RootPeerList { 176 ch := p.ch 177 if ch == nil { 178 if p.transport.ch == nil { 179 return nil 180 } 181 ch = p.transport.ch 182 } 183 184 return ch.RootPeers() 185 } 186 187 // StartRequest and EndRequest are no-ops now. 188 // They previously aggregated pending request count from all subscibed peer 189 // lists and distributed change notifications. 190 // This was fraught with concurrency hazards so we moved pending request count 191 // tracking into the lists themselves. 192 193 func (p *tchannelPeer) StartRequest() {} 194 195 func (p *tchannelPeer) EndRequest() {}