go.uber.org/yarpc@v1.72.1/peer/single.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 peer
    22  
    23  import (
    24  	"context"
    25  	"fmt"
    26  
    27  	"go.uber.org/yarpc/api/peer"
    28  	"go.uber.org/yarpc/api/transport"
    29  	"go.uber.org/yarpc/api/x/introspection"
    30  	"go.uber.org/yarpc/pkg/lifecycle"
    31  )
    32  
    33  // Single implements the Chooser interface for a single peer
    34  type Single struct {
    35  	once          *lifecycle.Once
    36  	t             peer.Transport
    37  	pid           peer.Identifier
    38  	p             peer.Peer
    39  	err           error
    40  	boundOnFinish func(error)
    41  }
    42  
    43  // NewSingle creates a static Chooser with a single Peer
    44  func NewSingle(pid peer.Identifier, transport peer.Transport) *Single {
    45  	s := &Single{
    46  		once: lifecycle.NewOnce(),
    47  		pid:  pid,
    48  		t:    transport,
    49  	}
    50  	s.boundOnFinish = s.onFinish
    51  	return s
    52  }
    53  
    54  // Transport returns the transport to which this peer is attached.
    55  func (s *Single) Transport() peer.Transport {
    56  	return s.t
    57  }
    58  
    59  // Choose returns the single peer
    60  func (s *Single) Choose(ctx context.Context, _ *transport.Request) (peer.Peer, func(error), error) {
    61  	if err := s.once.WaitUntilRunning(ctx); err != nil {
    62  		return nil, nil, err
    63  	}
    64  	s.p.StartRequest()
    65  	return s.p, s.boundOnFinish, s.err
    66  }
    67  
    68  func (s *Single) onFinish(_ error) {
    69  	s.p.EndRequest()
    70  }
    71  
    72  // NotifyStatusChanged receives notifications from the transport when the peer
    73  // connects, disconnects, accepts a request, and so on.
    74  func (s *Single) NotifyStatusChanged(_ peer.Identifier) {
    75  }
    76  
    77  // Start is a noop
    78  func (s *Single) Start() error {
    79  	return s.once.Start(s.start)
    80  }
    81  
    82  func (s *Single) start() error {
    83  	p, err := s.t.RetainPeer(s.pid, s)
    84  	s.p = p
    85  	s.err = err
    86  	return err
    87  }
    88  
    89  // Stop is a noop
    90  func (s *Single) Stop() error {
    91  	return s.once.Stop(s.stop)
    92  }
    93  
    94  func (s *Single) stop() error {
    95  	return s.t.ReleasePeer(s.pid, s)
    96  }
    97  
    98  // IsRunning is a noop
    99  func (s *Single) IsRunning() bool {
   100  	return true
   101  }
   102  
   103  // Introspect returns a ChooserStatus with a single PeerStatus.
   104  func (s *Single) Introspect() introspection.ChooserStatus {
   105  	if !s.once.IsRunning() {
   106  		return introspection.ChooserStatus{
   107  			Name: "Single",
   108  			Peers: []introspection.PeerStatus{
   109  				{
   110  					Identifier: s.pid.Identifier(),
   111  					State:      "uninitialized",
   112  				},
   113  			},
   114  		}
   115  	}
   116  
   117  	peerStatus := s.p.Status()
   118  	peer := introspection.PeerStatus{
   119  		Identifier: s.p.Identifier(),
   120  		State: fmt.Sprintf("%s, %d pending request(s)",
   121  			peerStatus.ConnectionStatus.String(),
   122  			peerStatus.PendingRequestCount),
   123  	}
   124  
   125  	return introspection.ChooserStatus{
   126  		Name:  "Single",
   127  		Peers: []introspection.PeerStatus{peer},
   128  	}
   129  }