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 }