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() {}