google.golang.org/grpc@v1.72.2/balancer_wrapper.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 grpc 20 21 import ( 22 "context" 23 "fmt" 24 "sync" 25 26 "google.golang.org/grpc/balancer" 27 "google.golang.org/grpc/codes" 28 "google.golang.org/grpc/connectivity" 29 "google.golang.org/grpc/experimental/stats" 30 "google.golang.org/grpc/internal" 31 "google.golang.org/grpc/internal/balancer/gracefulswitch" 32 "google.golang.org/grpc/internal/channelz" 33 "google.golang.org/grpc/internal/grpcsync" 34 "google.golang.org/grpc/resolver" 35 "google.golang.org/grpc/status" 36 ) 37 38 var ( 39 setConnectedAddress = internal.SetConnectedAddress.(func(*balancer.SubConnState, resolver.Address)) 40 // noOpRegisterHealthListenerFn is used when client side health checking is 41 // disabled. It sends a single READY update on the registered listener. 42 noOpRegisterHealthListenerFn = func(_ context.Context, listener func(balancer.SubConnState)) func() { 43 listener(balancer.SubConnState{ConnectivityState: connectivity.Ready}) 44 return func() {} 45 } 46 ) 47 48 // ccBalancerWrapper sits between the ClientConn and the Balancer. 49 // 50 // ccBalancerWrapper implements methods corresponding to the ones on the 51 // balancer.Balancer interface. The ClientConn is free to call these methods 52 // concurrently and the ccBalancerWrapper ensures that calls from the ClientConn 53 // to the Balancer happen in order by performing them in the serializer, without 54 // any mutexes held. 55 // 56 // ccBalancerWrapper also implements the balancer.ClientConn interface and is 57 // passed to the Balancer implementations. It invokes unexported methods on the 58 // ClientConn to handle these calls from the Balancer. 59 // 60 // It uses the gracefulswitch.Balancer internally to ensure that balancer 61 // switches happen in a graceful manner. 62 type ccBalancerWrapper struct { 63 internal.EnforceClientConnEmbedding 64 // The following fields are initialized when the wrapper is created and are 65 // read-only afterwards, and therefore can be accessed without a mutex. 66 cc *ClientConn 67 opts balancer.BuildOptions 68 serializer *grpcsync.CallbackSerializer 69 serializerCancel context.CancelFunc 70 71 // The following fields are only accessed within the serializer or during 72 // initialization. 73 curBalancerName string 74 balancer *gracefulswitch.Balancer 75 76 // The following field is protected by mu. Caller must take cc.mu before 77 // taking mu. 78 mu sync.Mutex 79 closed bool 80 } 81 82 // newCCBalancerWrapper creates a new balancer wrapper in idle state. The 83 // underlying balancer is not created until the updateClientConnState() method 84 // is invoked. 85 func newCCBalancerWrapper(cc *ClientConn) *ccBalancerWrapper { 86 ctx, cancel := context.WithCancel(cc.ctx) 87 ccb := &ccBalancerWrapper{ 88 cc: cc, 89 opts: balancer.BuildOptions{ 90 DialCreds: cc.dopts.copts.TransportCredentials, 91 CredsBundle: cc.dopts.copts.CredsBundle, 92 Dialer: cc.dopts.copts.Dialer, 93 Authority: cc.authority, 94 CustomUserAgent: cc.dopts.copts.UserAgent, 95 ChannelzParent: cc.channelz, 96 Target: cc.parsedTarget, 97 }, 98 serializer: grpcsync.NewCallbackSerializer(ctx), 99 serializerCancel: cancel, 100 } 101 ccb.balancer = gracefulswitch.NewBalancer(ccb, ccb.opts) 102 return ccb 103 } 104 105 func (ccb *ccBalancerWrapper) MetricsRecorder() stats.MetricsRecorder { 106 return ccb.cc.metricsRecorderList 107 } 108 109 // updateClientConnState is invoked by grpc to push a ClientConnState update to 110 // the underlying balancer. This is always executed from the serializer, so 111 // it is safe to call into the balancer here. 112 func (ccb *ccBalancerWrapper) updateClientConnState(ccs *balancer.ClientConnState) error { 113 errCh := make(chan error) 114 uccs := func(ctx context.Context) { 115 defer close(errCh) 116 if ctx.Err() != nil || ccb.balancer == nil { 117 return 118 } 119 name := gracefulswitch.ChildName(ccs.BalancerConfig) 120 if ccb.curBalancerName != name { 121 ccb.curBalancerName = name 122 channelz.Infof(logger, ccb.cc.channelz, "Channel switches to new LB policy %q", name) 123 } 124 err := ccb.balancer.UpdateClientConnState(*ccs) 125 if logger.V(2) && err != nil { 126 logger.Infof("error from balancer.UpdateClientConnState: %v", err) 127 } 128 errCh <- err 129 } 130 onFailure := func() { close(errCh) } 131 132 // UpdateClientConnState can race with Close, and when the latter wins, the 133 // serializer is closed, and the attempt to schedule the callback will fail. 134 // It is acceptable to ignore this failure. But since we want to handle the 135 // state update in a blocking fashion (when we successfully schedule the 136 // callback), we have to use the ScheduleOr method and not the MaybeSchedule 137 // method on the serializer. 138 ccb.serializer.ScheduleOr(uccs, onFailure) 139 return <-errCh 140 } 141 142 // resolverError is invoked by grpc to push a resolver error to the underlying 143 // balancer. The call to the balancer is executed from the serializer. 144 func (ccb *ccBalancerWrapper) resolverError(err error) { 145 ccb.serializer.TrySchedule(func(ctx context.Context) { 146 if ctx.Err() != nil || ccb.balancer == nil { 147 return 148 } 149 ccb.balancer.ResolverError(err) 150 }) 151 } 152 153 // close initiates async shutdown of the wrapper. cc.mu must be held when 154 // calling this function. To determine the wrapper has finished shutting down, 155 // the channel should block on ccb.serializer.Done() without cc.mu held. 156 func (ccb *ccBalancerWrapper) close() { 157 ccb.mu.Lock() 158 ccb.closed = true 159 ccb.mu.Unlock() 160 channelz.Info(logger, ccb.cc.channelz, "ccBalancerWrapper: closing") 161 ccb.serializer.TrySchedule(func(context.Context) { 162 if ccb.balancer == nil { 163 return 164 } 165 ccb.balancer.Close() 166 ccb.balancer = nil 167 }) 168 ccb.serializerCancel() 169 } 170 171 // exitIdle invokes the balancer's exitIdle method in the serializer. 172 func (ccb *ccBalancerWrapper) exitIdle() { 173 ccb.serializer.TrySchedule(func(ctx context.Context) { 174 if ctx.Err() != nil || ccb.balancer == nil { 175 return 176 } 177 ccb.balancer.ExitIdle() 178 }) 179 } 180 181 func (ccb *ccBalancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) { 182 ccb.cc.mu.Lock() 183 defer ccb.cc.mu.Unlock() 184 185 ccb.mu.Lock() 186 if ccb.closed { 187 ccb.mu.Unlock() 188 return nil, fmt.Errorf("balancer is being closed; no new SubConns allowed") 189 } 190 ccb.mu.Unlock() 191 192 if len(addrs) == 0 { 193 return nil, fmt.Errorf("grpc: cannot create SubConn with empty address list") 194 } 195 ac, err := ccb.cc.newAddrConnLocked(addrs, opts) 196 if err != nil { 197 channelz.Warningf(logger, ccb.cc.channelz, "acBalancerWrapper: NewSubConn: failed to newAddrConn: %v", err) 198 return nil, err 199 } 200 acbw := &acBalancerWrapper{ 201 ccb: ccb, 202 ac: ac, 203 producers: make(map[balancer.ProducerBuilder]*refCountedProducer), 204 stateListener: opts.StateListener, 205 healthData: newHealthData(connectivity.Idle), 206 } 207 ac.acbw = acbw 208 return acbw, nil 209 } 210 211 func (ccb *ccBalancerWrapper) RemoveSubConn(balancer.SubConn) { 212 // The graceful switch balancer will never call this. 213 logger.Errorf("ccb RemoveSubConn(%v) called unexpectedly, sc") 214 } 215 216 func (ccb *ccBalancerWrapper) UpdateAddresses(sc balancer.SubConn, addrs []resolver.Address) { 217 acbw, ok := sc.(*acBalancerWrapper) 218 if !ok { 219 return 220 } 221 acbw.UpdateAddresses(addrs) 222 } 223 224 func (ccb *ccBalancerWrapper) UpdateState(s balancer.State) { 225 ccb.cc.mu.Lock() 226 defer ccb.cc.mu.Unlock() 227 if ccb.cc.conns == nil { 228 // The CC has been closed; ignore this update. 229 return 230 } 231 232 ccb.mu.Lock() 233 if ccb.closed { 234 ccb.mu.Unlock() 235 return 236 } 237 ccb.mu.Unlock() 238 // Update picker before updating state. Even though the ordering here does 239 // not matter, it can lead to multiple calls of Pick in the common start-up 240 // case where we wait for ready and then perform an RPC. If the picker is 241 // updated later, we could call the "connecting" picker when the state is 242 // updated, and then call the "ready" picker after the picker gets updated. 243 244 // Note that there is no need to check if the balancer wrapper was closed, 245 // as we know the graceful switch LB policy will not call cc if it has been 246 // closed. 247 ccb.cc.pickerWrapper.updatePicker(s.Picker) 248 ccb.cc.csMgr.updateState(s.ConnectivityState) 249 } 250 251 func (ccb *ccBalancerWrapper) ResolveNow(o resolver.ResolveNowOptions) { 252 ccb.cc.mu.RLock() 253 defer ccb.cc.mu.RUnlock() 254 255 ccb.mu.Lock() 256 if ccb.closed { 257 ccb.mu.Unlock() 258 return 259 } 260 ccb.mu.Unlock() 261 ccb.cc.resolveNowLocked(o) 262 } 263 264 func (ccb *ccBalancerWrapper) Target() string { 265 return ccb.cc.target 266 } 267 268 // acBalancerWrapper is a wrapper on top of ac for balancers. 269 // It implements balancer.SubConn interface. 270 type acBalancerWrapper struct { 271 internal.EnforceSubConnEmbedding 272 ac *addrConn // read-only 273 ccb *ccBalancerWrapper // read-only 274 stateListener func(balancer.SubConnState) 275 276 producersMu sync.Mutex 277 producers map[balancer.ProducerBuilder]*refCountedProducer 278 279 // Access to healthData is protected by healthMu. 280 healthMu sync.Mutex 281 // healthData is stored as a pointer to detect when the health listener is 282 // dropped or updated. This is required as closures can't be compared for 283 // equality. 284 healthData *healthData 285 } 286 287 // healthData holds data related to health state reporting. 288 type healthData struct { 289 // connectivityState stores the most recent connectivity state delivered 290 // to the LB policy. This is stored to avoid sending updates when the 291 // SubConn has already exited connectivity state READY. 292 connectivityState connectivity.State 293 // closeHealthProducer stores function to close the ref counted health 294 // producer. The health producer is automatically closed when the SubConn 295 // state changes. 296 closeHealthProducer func() 297 } 298 299 func newHealthData(s connectivity.State) *healthData { 300 return &healthData{ 301 connectivityState: s, 302 closeHealthProducer: func() {}, 303 } 304 } 305 306 // updateState is invoked by grpc to push a subConn state update to the 307 // underlying balancer. 308 func (acbw *acBalancerWrapper) updateState(s connectivity.State, curAddr resolver.Address, err error) { 309 acbw.ccb.serializer.TrySchedule(func(ctx context.Context) { 310 if ctx.Err() != nil || acbw.ccb.balancer == nil { 311 return 312 } 313 // Invalidate all producers on any state change. 314 acbw.closeProducers() 315 316 // Even though it is optional for balancers, gracefulswitch ensures 317 // opts.StateListener is set, so this cannot ever be nil. 318 // TODO: delete this comment when UpdateSubConnState is removed. 319 scs := balancer.SubConnState{ConnectivityState: s, ConnectionError: err} 320 if s == connectivity.Ready { 321 setConnectedAddress(&scs, curAddr) 322 } 323 // Invalidate the health listener by updating the healthData. 324 acbw.healthMu.Lock() 325 // A race may occur if a health listener is registered soon after the 326 // connectivity state is set but before the stateListener is called. 327 // Two cases may arise: 328 // 1. The new state is not READY: RegisterHealthListener has checks to 329 // ensure no updates are sent when the connectivity state is not 330 // READY. 331 // 2. The new state is READY: This means that the old state wasn't Ready. 332 // The RegisterHealthListener API mentions that a health listener 333 // must not be registered when a SubConn is not ready to avoid such 334 // races. When this happens, the LB policy would get health updates 335 // on the old listener. When the LB policy registers a new listener 336 // on receiving the connectivity update, the health updates will be 337 // sent to the new health listener. 338 acbw.healthData = newHealthData(scs.ConnectivityState) 339 acbw.healthMu.Unlock() 340 341 acbw.stateListener(scs) 342 }) 343 } 344 345 func (acbw *acBalancerWrapper) String() string { 346 return fmt.Sprintf("SubConn(id:%d)", acbw.ac.channelz.ID) 347 } 348 349 func (acbw *acBalancerWrapper) UpdateAddresses(addrs []resolver.Address) { 350 acbw.ac.updateAddrs(addrs) 351 } 352 353 func (acbw *acBalancerWrapper) Connect() { 354 go acbw.ac.connect() 355 } 356 357 func (acbw *acBalancerWrapper) Shutdown() { 358 acbw.closeProducers() 359 acbw.ccb.cc.removeAddrConn(acbw.ac, errConnDrain) 360 } 361 362 // NewStream begins a streaming RPC on the addrConn. If the addrConn is not 363 // ready, blocks until it is or ctx expires. Returns an error when the context 364 // expires or the addrConn is shut down. 365 func (acbw *acBalancerWrapper) NewStream(ctx context.Context, desc *StreamDesc, method string, opts ...CallOption) (ClientStream, error) { 366 transport := acbw.ac.getReadyTransport() 367 if transport == nil { 368 return nil, status.Errorf(codes.Unavailable, "SubConn state is not Ready") 369 370 } 371 return newNonRetryClientStream(ctx, desc, method, transport, acbw.ac, opts...) 372 } 373 374 // Invoke performs a unary RPC. If the addrConn is not ready, returns 375 // errSubConnNotReady. 376 func (acbw *acBalancerWrapper) Invoke(ctx context.Context, method string, args any, reply any, opts ...CallOption) error { 377 cs, err := acbw.NewStream(ctx, unaryStreamDesc, method, opts...) 378 if err != nil { 379 return err 380 } 381 if err := cs.SendMsg(args); err != nil { 382 return err 383 } 384 return cs.RecvMsg(reply) 385 } 386 387 type refCountedProducer struct { 388 producer balancer.Producer 389 refs int // number of current refs to the producer 390 close func() // underlying producer's close function 391 } 392 393 func (acbw *acBalancerWrapper) GetOrBuildProducer(pb balancer.ProducerBuilder) (balancer.Producer, func()) { 394 acbw.producersMu.Lock() 395 defer acbw.producersMu.Unlock() 396 397 // Look up existing producer from this builder. 398 pData := acbw.producers[pb] 399 if pData == nil { 400 // Not found; create a new one and add it to the producers map. 401 p, closeFn := pb.Build(acbw) 402 pData = &refCountedProducer{producer: p, close: closeFn} 403 acbw.producers[pb] = pData 404 } 405 // Account for this new reference. 406 pData.refs++ 407 408 // Return a cleanup function wrapped in a OnceFunc to remove this reference 409 // and delete the refCountedProducer from the map if the total reference 410 // count goes to zero. 411 unref := func() { 412 acbw.producersMu.Lock() 413 // If closeProducers has already closed this producer instance, refs is 414 // set to 0, so the check after decrementing will never pass, and the 415 // producer will not be double-closed. 416 pData.refs-- 417 if pData.refs == 0 { 418 defer pData.close() // Run outside the acbw mutex 419 delete(acbw.producers, pb) 420 } 421 acbw.producersMu.Unlock() 422 } 423 return pData.producer, sync.OnceFunc(unref) 424 } 425 426 func (acbw *acBalancerWrapper) closeProducers() { 427 acbw.producersMu.Lock() 428 defer acbw.producersMu.Unlock() 429 for pb, pData := range acbw.producers { 430 pData.refs = 0 431 pData.close() 432 delete(acbw.producers, pb) 433 } 434 } 435 436 // healthProducerRegisterFn is a type alias for the health producer's function 437 // for registering listeners. 438 type healthProducerRegisterFn = func(context.Context, balancer.SubConn, string, func(balancer.SubConnState)) func() 439 440 // healthListenerRegFn returns a function to register a listener for health 441 // updates. If client side health checks are disabled, the registered listener 442 // will get a single READY (raw connectivity state) update. 443 // 444 // Client side health checking is enabled when all the following 445 // conditions are satisfied: 446 // 1. Health checking is not disabled using the dial option. 447 // 2. The health package is imported. 448 // 3. The health check config is present in the service config. 449 func (acbw *acBalancerWrapper) healthListenerRegFn() func(context.Context, func(balancer.SubConnState)) func() { 450 if acbw.ccb.cc.dopts.disableHealthCheck { 451 return noOpRegisterHealthListenerFn 452 } 453 regHealthLisFn := internal.RegisterClientHealthCheckListener 454 if regHealthLisFn == nil { 455 // The health package is not imported. 456 return noOpRegisterHealthListenerFn 457 } 458 cfg := acbw.ac.cc.healthCheckConfig() 459 if cfg == nil { 460 return noOpRegisterHealthListenerFn 461 } 462 return func(ctx context.Context, listener func(balancer.SubConnState)) func() { 463 return regHealthLisFn.(healthProducerRegisterFn)(ctx, acbw, cfg.ServiceName, listener) 464 } 465 } 466 467 // RegisterHealthListener accepts a health listener from the LB policy. It sends 468 // updates to the health listener as long as the SubConn's connectivity state 469 // doesn't change and a new health listener is not registered. To invalidate 470 // the currently registered health listener, acbw updates the healthData. If a 471 // nil listener is registered, the active health listener is dropped. 472 func (acbw *acBalancerWrapper) RegisterHealthListener(listener func(balancer.SubConnState)) { 473 acbw.healthMu.Lock() 474 defer acbw.healthMu.Unlock() 475 acbw.healthData.closeHealthProducer() 476 // listeners should not be registered when the connectivity state 477 // isn't Ready. This may happen when the balancer registers a listener 478 // after the connectivityState is updated, but before it is notified 479 // of the update. 480 if acbw.healthData.connectivityState != connectivity.Ready { 481 return 482 } 483 // Replace the health data to stop sending updates to any previously 484 // registered health listeners. 485 hd := newHealthData(connectivity.Ready) 486 acbw.healthData = hd 487 if listener == nil { 488 return 489 } 490 491 registerFn := acbw.healthListenerRegFn() 492 acbw.ccb.serializer.TrySchedule(func(ctx context.Context) { 493 if ctx.Err() != nil || acbw.ccb.balancer == nil { 494 return 495 } 496 // Don't send updates if a new listener is registered. 497 acbw.healthMu.Lock() 498 defer acbw.healthMu.Unlock() 499 if acbw.healthData != hd { 500 return 501 } 502 // Serialize the health updates from the health producer with 503 // other calls into the LB policy. 504 listenerWrapper := func(scs balancer.SubConnState) { 505 acbw.ccb.serializer.TrySchedule(func(ctx context.Context) { 506 if ctx.Err() != nil || acbw.ccb.balancer == nil { 507 return 508 } 509 acbw.healthMu.Lock() 510 defer acbw.healthMu.Unlock() 511 if acbw.healthData != hd { 512 return 513 } 514 listener(scs) 515 }) 516 } 517 518 hd.closeHealthProducer = registerFn(ctx, listenerWrapper) 519 }) 520 }