google.golang.org/grpc@v1.72.2/balancer/pickfirst/pickfirst.go (about) 1 /* 2 * 3 * Copyright 2017 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 // Package pickfirst contains the pick_first load balancing policy. 20 package pickfirst 21 22 import ( 23 "encoding/json" 24 "errors" 25 "fmt" 26 rand "math/rand/v2" 27 28 "google.golang.org/grpc/balancer" 29 "google.golang.org/grpc/balancer/pickfirst/internal" 30 "google.golang.org/grpc/connectivity" 31 "google.golang.org/grpc/grpclog" 32 "google.golang.org/grpc/internal/envconfig" 33 internalgrpclog "google.golang.org/grpc/internal/grpclog" 34 "google.golang.org/grpc/internal/pretty" 35 "google.golang.org/grpc/resolver" 36 "google.golang.org/grpc/serviceconfig" 37 38 _ "google.golang.org/grpc/balancer/pickfirst/pickfirstleaf" // For automatically registering the new pickfirst if required. 39 ) 40 41 func init() { 42 if envconfig.NewPickFirstEnabled { 43 return 44 } 45 balancer.Register(pickfirstBuilder{}) 46 } 47 48 var logger = grpclog.Component("pick-first-lb") 49 50 const ( 51 // Name is the name of the pick_first balancer. 52 Name = "pick_first" 53 logPrefix = "[pick-first-lb %p] " 54 ) 55 56 type pickfirstBuilder struct{} 57 58 func (pickfirstBuilder) Build(cc balancer.ClientConn, _ balancer.BuildOptions) balancer.Balancer { 59 b := &pickfirstBalancer{cc: cc} 60 b.logger = internalgrpclog.NewPrefixLogger(logger, fmt.Sprintf(logPrefix, b)) 61 return b 62 } 63 64 func (pickfirstBuilder) Name() string { 65 return Name 66 } 67 68 type pfConfig struct { 69 serviceconfig.LoadBalancingConfig `json:"-"` 70 71 // If set to true, instructs the LB policy to shuffle the order of the list 72 // of endpoints received from the name resolver before attempting to 73 // connect to them. 74 ShuffleAddressList bool `json:"shuffleAddressList"` 75 } 76 77 func (pickfirstBuilder) ParseConfig(js json.RawMessage) (serviceconfig.LoadBalancingConfig, error) { 78 var cfg pfConfig 79 if err := json.Unmarshal(js, &cfg); err != nil { 80 return nil, fmt.Errorf("pickfirst: unable to unmarshal LB policy config: %s, error: %v", string(js), err) 81 } 82 return cfg, nil 83 } 84 85 type pickfirstBalancer struct { 86 logger *internalgrpclog.PrefixLogger 87 state connectivity.State 88 cc balancer.ClientConn 89 subConn balancer.SubConn 90 } 91 92 func (b *pickfirstBalancer) ResolverError(err error) { 93 if b.logger.V(2) { 94 b.logger.Infof("Received error from the name resolver: %v", err) 95 } 96 if b.subConn == nil { 97 b.state = connectivity.TransientFailure 98 } 99 100 if b.state != connectivity.TransientFailure { 101 // The picker will not change since the balancer does not currently 102 // report an error. 103 return 104 } 105 b.cc.UpdateState(balancer.State{ 106 ConnectivityState: connectivity.TransientFailure, 107 Picker: &picker{err: fmt.Errorf("name resolver error: %v", err)}, 108 }) 109 } 110 111 // Shuffler is an interface for shuffling an address list. 112 type Shuffler interface { 113 ShuffleAddressListForTesting(n int, swap func(i, j int)) 114 } 115 116 // ShuffleAddressListForTesting pseudo-randomizes the order of addresses. n 117 // is the number of elements. swap swaps the elements with indexes i and j. 118 func ShuffleAddressListForTesting(n int, swap func(i, j int)) { rand.Shuffle(n, swap) } 119 120 func (b *pickfirstBalancer) UpdateClientConnState(state balancer.ClientConnState) error { 121 if len(state.ResolverState.Addresses) == 0 && len(state.ResolverState.Endpoints) == 0 { 122 // The resolver reported an empty address list. Treat it like an error by 123 // calling b.ResolverError. 124 if b.subConn != nil { 125 // Shut down the old subConn. All addresses were removed, so it is 126 // no longer valid. 127 b.subConn.Shutdown() 128 b.subConn = nil 129 } 130 b.ResolverError(errors.New("produced zero addresses")) 131 return balancer.ErrBadResolverState 132 } 133 // We don't have to guard this block with the env var because ParseConfig 134 // already does so. 135 cfg, ok := state.BalancerConfig.(pfConfig) 136 if state.BalancerConfig != nil && !ok { 137 return fmt.Errorf("pickfirst: received illegal BalancerConfig (type %T): %v", state.BalancerConfig, state.BalancerConfig) 138 } 139 140 if b.logger.V(2) { 141 b.logger.Infof("Received new config %s, resolver state %s", pretty.ToJSON(cfg), pretty.ToJSON(state.ResolverState)) 142 } 143 144 var addrs []resolver.Address 145 if endpoints := state.ResolverState.Endpoints; len(endpoints) != 0 { 146 // Perform the optional shuffling described in gRFC A62. The shuffling will 147 // change the order of endpoints but not touch the order of the addresses 148 // within each endpoint. - A61 149 if cfg.ShuffleAddressList { 150 endpoints = append([]resolver.Endpoint{}, endpoints...) 151 internal.RandShuffle(len(endpoints), func(i, j int) { endpoints[i], endpoints[j] = endpoints[j], endpoints[i] }) 152 } 153 154 // "Flatten the list by concatenating the ordered list of addresses for each 155 // of the endpoints, in order." - A61 156 for _, endpoint := range endpoints { 157 // "In the flattened list, interleave addresses from the two address 158 // families, as per RFC-8304 section 4." - A61 159 // TODO: support the above language. 160 addrs = append(addrs, endpoint.Addresses...) 161 } 162 } else { 163 // Endpoints not set, process addresses until we migrate resolver 164 // emissions fully to Endpoints. The top channel does wrap emitted 165 // addresses with endpoints, however some balancers such as weighted 166 // target do not forward the corresponding correct endpoints down/split 167 // endpoints properly. Once all balancers correctly forward endpoints 168 // down, can delete this else conditional. 169 addrs = state.ResolverState.Addresses 170 if cfg.ShuffleAddressList { 171 addrs = append([]resolver.Address{}, addrs...) 172 rand.Shuffle(len(addrs), func(i, j int) { addrs[i], addrs[j] = addrs[j], addrs[i] }) 173 } 174 } 175 176 if b.subConn != nil { 177 b.cc.UpdateAddresses(b.subConn, addrs) 178 return nil 179 } 180 181 var subConn balancer.SubConn 182 subConn, err := b.cc.NewSubConn(addrs, balancer.NewSubConnOptions{ 183 StateListener: func(state balancer.SubConnState) { 184 b.updateSubConnState(subConn, state) 185 }, 186 }) 187 if err != nil { 188 if b.logger.V(2) { 189 b.logger.Infof("Failed to create new SubConn: %v", err) 190 } 191 b.state = connectivity.TransientFailure 192 b.cc.UpdateState(balancer.State{ 193 ConnectivityState: connectivity.TransientFailure, 194 Picker: &picker{err: fmt.Errorf("error creating connection: %v", err)}, 195 }) 196 return balancer.ErrBadResolverState 197 } 198 b.subConn = subConn 199 b.state = connectivity.Idle 200 b.cc.UpdateState(balancer.State{ 201 ConnectivityState: connectivity.Connecting, 202 Picker: &picker{err: balancer.ErrNoSubConnAvailable}, 203 }) 204 b.subConn.Connect() 205 return nil 206 } 207 208 // UpdateSubConnState is unused as a StateListener is always registered when 209 // creating SubConns. 210 func (b *pickfirstBalancer) UpdateSubConnState(subConn balancer.SubConn, state balancer.SubConnState) { 211 b.logger.Errorf("UpdateSubConnState(%v, %+v) called unexpectedly", subConn, state) 212 } 213 214 func (b *pickfirstBalancer) updateSubConnState(subConn balancer.SubConn, state balancer.SubConnState) { 215 if b.logger.V(2) { 216 b.logger.Infof("Received SubConn state update: %p, %+v", subConn, state) 217 } 218 if b.subConn != subConn { 219 if b.logger.V(2) { 220 b.logger.Infof("Ignored state change because subConn is not recognized") 221 } 222 return 223 } 224 if state.ConnectivityState == connectivity.Shutdown { 225 b.subConn = nil 226 return 227 } 228 229 switch state.ConnectivityState { 230 case connectivity.Ready: 231 b.cc.UpdateState(balancer.State{ 232 ConnectivityState: state.ConnectivityState, 233 Picker: &picker{result: balancer.PickResult{SubConn: subConn}}, 234 }) 235 case connectivity.Connecting: 236 if b.state == connectivity.TransientFailure { 237 // We stay in TransientFailure until we are Ready. See A62. 238 return 239 } 240 b.cc.UpdateState(balancer.State{ 241 ConnectivityState: state.ConnectivityState, 242 Picker: &picker{err: balancer.ErrNoSubConnAvailable}, 243 }) 244 case connectivity.Idle: 245 if b.state == connectivity.TransientFailure { 246 // We stay in TransientFailure until we are Ready. Also kick the 247 // subConn out of Idle into Connecting. See A62. 248 b.subConn.Connect() 249 return 250 } 251 b.cc.UpdateState(balancer.State{ 252 ConnectivityState: state.ConnectivityState, 253 Picker: &idlePicker{subConn: subConn}, 254 }) 255 case connectivity.TransientFailure: 256 b.cc.UpdateState(balancer.State{ 257 ConnectivityState: state.ConnectivityState, 258 Picker: &picker{err: state.ConnectionError}, 259 }) 260 } 261 b.state = state.ConnectivityState 262 } 263 264 func (b *pickfirstBalancer) Close() { 265 } 266 267 func (b *pickfirstBalancer) ExitIdle() { 268 if b.subConn != nil && b.state == connectivity.Idle { 269 b.subConn.Connect() 270 } 271 } 272 273 type picker struct { 274 result balancer.PickResult 275 err error 276 } 277 278 func (p *picker) Pick(balancer.PickInfo) (balancer.PickResult, error) { 279 return p.result, p.err 280 } 281 282 // idlePicker is used when the SubConn is IDLE and kicks the SubConn into 283 // CONNECTING when Pick is called. 284 type idlePicker struct { 285 subConn balancer.SubConn 286 } 287 288 func (i *idlePicker) Pick(balancer.PickInfo) (balancer.PickResult, error) { 289 i.subConn.Connect() 290 return balancer.PickResult{}, balancer.ErrNoSubConnAvailable 291 }