go.uber.org/yarpc@v1.72.1/peer/bind.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  
    26  	"go.uber.org/multierr"
    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  // Bind couples a peer list with a peer list updater.
    34  // Bind accepts a peer list and passes that peer list to a binder.
    35  // The binder must return a peer list updater bound to the peer list.
    36  // The peer list updater must implement Lifecycle so the bound peer list
    37  // can start and stop updates.
    38  func Bind(chooserList peer.ChooserList, bind peer.Binder) *BoundChooser {
    39  	return &BoundChooser{
    40  		once:        lifecycle.NewOnce(),
    41  		chooserList: chooserList,
    42  		updater:     bind(chooserList),
    43  	}
    44  }
    45  
    46  // BoundChooser is a peer chooser that couples a peer list and a peer list
    47  // updater for the duration of its lifecycle.
    48  type BoundChooser struct {
    49  	once        *lifecycle.Once
    50  	updater     transport.Lifecycle // the peer list updater, which to us is just a lifecycle
    51  	chooserList peer.ChooserList    // the peer list/chooser, also a lifecycle
    52  }
    53  
    54  // Updater returns the bound peer list updater.
    55  func (c *BoundChooser) Updater() transport.Lifecycle {
    56  	return c.updater
    57  }
    58  
    59  // ChooserList returns the bound peer list.
    60  func (c *BoundChooser) ChooserList() peer.ChooserList {
    61  	return c.chooserList
    62  }
    63  
    64  // Choose returns a peer from the bound peer list.
    65  func (c *BoundChooser) Choose(ctx context.Context, treq *transport.Request) (peer peer.Peer, onFinish func(error), err error) {
    66  	return c.chooserList.Choose(ctx, treq)
    67  }
    68  
    69  // Start starts the peer list and the peer list updater.
    70  func (c *BoundChooser) Start() error {
    71  	return c.once.Start(c.start)
    72  }
    73  
    74  func (c *BoundChooser) start() error {
    75  
    76  	if err := c.chooserList.Start(); err != nil {
    77  		return err
    78  	}
    79  
    80  	var errs error
    81  	if err := c.updater.Start(); err != nil {
    82  		errs = multierr.Append(errs, err)
    83  
    84  		// Abort the peer chooser if the updater failed to start.
    85  		if err := c.chooserList.Stop(); err != nil {
    86  			errs = multierr.Append(errs, err)
    87  		}
    88  	}
    89  
    90  	return errs
    91  }
    92  
    93  // Stop stops the peer list and the peer list updater.
    94  func (c *BoundChooser) Stop() error {
    95  	return c.once.Stop(c.stop)
    96  }
    97  
    98  func (c *BoundChooser) stop() error {
    99  	var errs error
   100  
   101  	if err := c.updater.Stop(); err != nil {
   102  		errs = multierr.Append(errs, err)
   103  	}
   104  
   105  	if err := c.chooserList.Stop(); err != nil {
   106  		errs = multierr.Append(errs, err)
   107  	}
   108  
   109  	return errs
   110  }
   111  
   112  // IsRunning returns whether the peer list and its peer list updater are both
   113  // running, regardless of whether they should be running according to the bound
   114  // chooser's lifecycle.
   115  func (c *BoundChooser) IsRunning() bool {
   116  	return c.chooserList.IsRunning() && c.updater.IsRunning()
   117  }
   118  
   119  // Introspect introspects the bound chooser.
   120  func (c *BoundChooser) Introspect() introspection.ChooserStatus {
   121  	if ic, ok := c.chooserList.(introspection.IntrospectableChooser); ok {
   122  		return ic.Introspect()
   123  	}
   124  	return introspection.ChooserStatus{}
   125  }
   126  
   127  // BindPeers returns a binder (suitable as an argument to peer.Bind) that
   128  // binds a peer list to a static list of peers for the duration of its
   129  // lifecycle.
   130  func BindPeers(ids []peer.Identifier) peer.Binder {
   131  	return func(pl peer.List) transport.Lifecycle {
   132  		return &PeersUpdater{
   133  			once: lifecycle.NewOnce(),
   134  			pl:   pl,
   135  			ids:  ids,
   136  		}
   137  	}
   138  }
   139  
   140  // PeersUpdater binds a fixed list of peers to a peer list.
   141  type PeersUpdater struct {
   142  	once *lifecycle.Once
   143  	pl   peer.List
   144  	ids  []peer.Identifier
   145  }
   146  
   147  // Start adds a list of fixed peers to a peer list.
   148  func (s *PeersUpdater) Start() error {
   149  	return s.once.Start(s.start)
   150  }
   151  
   152  func (s *PeersUpdater) start() error {
   153  	return s.pl.Update(peer.ListUpdates{
   154  		Additions: s.ids,
   155  	})
   156  }
   157  
   158  // Stop removes a list of fixed peers from a peer list.
   159  func (s *PeersUpdater) Stop() error {
   160  	return s.once.Stop(s.stop)
   161  }
   162  
   163  func (s *PeersUpdater) stop() error {
   164  	return s.pl.Update(peer.ListUpdates{
   165  		Removals: s.ids,
   166  	})
   167  }
   168  
   169  // IsRunning returns whether the peers have been added and not removed.
   170  func (s *PeersUpdater) IsRunning() bool {
   171  	return s.once.IsRunning()
   172  }