google.golang.org/grpc@v1.72.2/xds/internal/balancer/outlierdetection/balancer.go (about)

     1  /*
     2   *
     3   * Copyright 2022 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 outlierdetection provides an implementation of the outlier detection
    20  // LB policy, as defined in
    21  // https://github.com/grpc/proposal/blob/master/A50-xds-outlier-detection.md.
    22  package outlierdetection
    23  
    24  import (
    25  	"encoding/json"
    26  	"fmt"
    27  	"math"
    28  	rand "math/rand/v2"
    29  	"strings"
    30  	"sync"
    31  	"sync/atomic"
    32  	"time"
    33  
    34  	"google.golang.org/grpc/balancer"
    35  	"google.golang.org/grpc/balancer/pickfirst/pickfirstleaf"
    36  	"google.golang.org/grpc/connectivity"
    37  	"google.golang.org/grpc/internal/balancer/gracefulswitch"
    38  	"google.golang.org/grpc/internal/buffer"
    39  	"google.golang.org/grpc/internal/channelz"
    40  	"google.golang.org/grpc/internal/grpclog"
    41  	"google.golang.org/grpc/internal/grpcsync"
    42  	iserviceconfig "google.golang.org/grpc/internal/serviceconfig"
    43  	"google.golang.org/grpc/resolver"
    44  	"google.golang.org/grpc/serviceconfig"
    45  )
    46  
    47  // Globals to stub out in tests.
    48  var (
    49  	afterFunc = time.AfterFunc
    50  	now       = time.Now
    51  )
    52  
    53  // Name is the name of the outlier detection balancer.
    54  const Name = "outlier_detection_experimental"
    55  
    56  func init() {
    57  	balancer.Register(bb{})
    58  }
    59  
    60  type bb struct{}
    61  
    62  func (bb) Build(cc balancer.ClientConn, bOpts balancer.BuildOptions) balancer.Balancer {
    63  	b := &outlierDetectionBalancer{
    64  		ClientConn:     cc,
    65  		closed:         grpcsync.NewEvent(),
    66  		done:           grpcsync.NewEvent(),
    67  		addrs:          make(map[string]*endpointInfo),
    68  		scUpdateCh:     buffer.NewUnbounded(),
    69  		pickerUpdateCh: buffer.NewUnbounded(),
    70  		channelzParent: bOpts.ChannelzParent,
    71  		endpoints:      resolver.NewEndpointMap[*endpointInfo](),
    72  	}
    73  	b.logger = prefixLogger(b)
    74  	b.logger.Infof("Created")
    75  	b.child = synchronizingBalancerWrapper{lb: gracefulswitch.NewBalancer(b, bOpts)}
    76  	go b.run()
    77  	return b
    78  }
    79  
    80  func (bb) ParseConfig(s json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
    81  	lbCfg := &LBConfig{
    82  		// Default top layer values as documented in A50.
    83  		Interval:           iserviceconfig.Duration(10 * time.Second),
    84  		BaseEjectionTime:   iserviceconfig.Duration(30 * time.Second),
    85  		MaxEjectionTime:    iserviceconfig.Duration(300 * time.Second),
    86  		MaxEjectionPercent: 10,
    87  	}
    88  
    89  	// This unmarshalling handles underlying layers sre and fpe which have their
    90  	// own defaults for their fields if either sre or fpe are present.
    91  	if err := json.Unmarshal(s, lbCfg); err != nil { // Validates child config if present as well.
    92  		return nil, fmt.Errorf("xds: unable to unmarshal LBconfig: %s, error: %v", string(s), err)
    93  	}
    94  
    95  	// Note: in the xds flow, these validations will never fail. The xdsclient
    96  	// performs the same validations as here on the xds Outlier Detection
    97  	// resource before parsing resource into JSON which this function gets
    98  	// called with. A50 defines two separate places for these validations to
    99  	// take place, the xdsclient and this ParseConfig method. "When parsing a
   100  	// config from JSON, if any of these requirements is violated, that should
   101  	// be treated as a parsing error." - A50
   102  	switch {
   103  	// "The google.protobuf.Duration fields interval, base_ejection_time, and
   104  	// max_ejection_time must obey the restrictions in the
   105  	// google.protobuf.Duration documentation and they must have non-negative
   106  	// values." - A50
   107  	// Approximately 290 years is the maximum time that time.Duration (int64)
   108  	// can represent. The restrictions on the protobuf.Duration field are to be
   109  	// within +-10000 years. Thus, just check for negative values.
   110  	case lbCfg.Interval < 0:
   111  		return nil, fmt.Errorf("OutlierDetectionLoadBalancingConfig.interval = %s; must be >= 0", lbCfg.Interval)
   112  	case lbCfg.BaseEjectionTime < 0:
   113  		return nil, fmt.Errorf("OutlierDetectionLoadBalancingConfig.base_ejection_time = %s; must be >= 0", lbCfg.BaseEjectionTime)
   114  	case lbCfg.MaxEjectionTime < 0:
   115  		return nil, fmt.Errorf("OutlierDetectionLoadBalancingConfig.max_ejection_time = %s; must be >= 0", lbCfg.MaxEjectionTime)
   116  
   117  	// "The fields max_ejection_percent,
   118  	// success_rate_ejection.enforcement_percentage,
   119  	// failure_percentage_ejection.threshold, and
   120  	// failure_percentage.enforcement_percentage must have values less than or
   121  	// equal to 100." - A50
   122  	case lbCfg.MaxEjectionPercent > 100:
   123  		return nil, fmt.Errorf("OutlierDetectionLoadBalancingConfig.max_ejection_percent = %v; must be <= 100", lbCfg.MaxEjectionPercent)
   124  	case lbCfg.SuccessRateEjection != nil && lbCfg.SuccessRateEjection.EnforcementPercentage > 100:
   125  		return nil, fmt.Errorf("OutlierDetectionLoadBalancingConfig.SuccessRateEjection.enforcement_percentage = %v; must be <= 100", lbCfg.SuccessRateEjection.EnforcementPercentage)
   126  	case lbCfg.FailurePercentageEjection != nil && lbCfg.FailurePercentageEjection.Threshold > 100:
   127  		return nil, fmt.Errorf("OutlierDetectionLoadBalancingConfig.FailurePercentageEjection.threshold = %v; must be <= 100", lbCfg.FailurePercentageEjection.Threshold)
   128  	case lbCfg.FailurePercentageEjection != nil && lbCfg.FailurePercentageEjection.EnforcementPercentage > 100:
   129  		return nil, fmt.Errorf("OutlierDetectionLoadBalancingConfig.FailurePercentageEjection.enforcement_percentage = %v; must be <= 100", lbCfg.FailurePercentageEjection.EnforcementPercentage)
   130  	}
   131  	return lbCfg, nil
   132  }
   133  
   134  func (bb) Name() string {
   135  	return Name
   136  }
   137  
   138  // scUpdate wraps a subConn update to be sent to the child balancer.
   139  type scUpdate struct {
   140  	scw   *subConnWrapper
   141  	state balancer.SubConnState
   142  }
   143  
   144  type ejectionUpdate struct {
   145  	scw       *subConnWrapper
   146  	isEjected bool // true for ejected, false for unejected
   147  }
   148  
   149  type lbCfgUpdate struct {
   150  	lbCfg *LBConfig
   151  	// to make sure picker is updated synchronously.
   152  	done chan struct{}
   153  }
   154  
   155  type scHealthUpdate struct {
   156  	scw   *subConnWrapper
   157  	state balancer.SubConnState
   158  }
   159  
   160  type outlierDetectionBalancer struct {
   161  	balancer.ClientConn
   162  	// These fields are safe to be accessed without holding any mutex because
   163  	// they are synchronized in run(), which makes these field accesses happen
   164  	// serially.
   165  	//
   166  	// childState is the latest balancer state received from the child.
   167  	childState balancer.State
   168  	// recentPickerNoop represents whether the most recent picker sent upward to
   169  	// the balancer.ClientConn is a noop picker, which doesn't count RPC's. Used
   170  	// to suppress redundant picker updates.
   171  	recentPickerNoop bool
   172  
   173  	closed         *grpcsync.Event
   174  	done           *grpcsync.Event
   175  	logger         *grpclog.PrefixLogger
   176  	channelzParent channelz.Identifier
   177  
   178  	child synchronizingBalancerWrapper
   179  
   180  	// mu guards access to the following fields. It also helps to synchronize
   181  	// behaviors of the following events: config updates, firing of the interval
   182  	// timer, SubConn State updates, SubConn address updates, and child state
   183  	// updates.
   184  	//
   185  	// For example, when we receive a config update in the middle of the
   186  	// interval timer algorithm, which uses knobs present in the config, the
   187  	// balancer will wait for the interval timer algorithm to finish before
   188  	// persisting the new configuration.
   189  	//
   190  	// Another example would be the updating of the endpoints or addrs map, such
   191  	// as from a SubConn address update in the middle of the interval timer
   192  	// algorithm which uses endpoints. This balancer waits for the interval
   193  	// timer algorithm to finish before making the update to the endpoints map.
   194  	//
   195  	// This mutex is never held when calling methods on the child policy
   196  	// (within the context of a single goroutine).
   197  	mu sync.Mutex
   198  	// endpoints stores pointers to endpointInfo objects for each endpoint.
   199  	endpoints *resolver.EndpointMap[*endpointInfo]
   200  	// addrs stores pointers to endpointInfo objects for each address. Addresses
   201  	// belonging to the same endpoint point to the same object.
   202  	addrs                 map[string]*endpointInfo
   203  	cfg                   *LBConfig
   204  	timerStartTime        time.Time
   205  	intervalTimer         *time.Timer
   206  	inhibitPickerUpdates  bool
   207  	updateUnconditionally bool
   208  	numEndpointsEjected   int // For fast calculations of percentage of endpoints ejected
   209  
   210  	scUpdateCh     *buffer.Unbounded
   211  	pickerUpdateCh *buffer.Unbounded
   212  }
   213  
   214  // noopConfig returns whether this balancer is configured with a logical no-op
   215  // configuration or not.
   216  //
   217  // Caller must hold b.mu.
   218  func (b *outlierDetectionBalancer) noopConfig() bool {
   219  	return b.cfg.SuccessRateEjection == nil && b.cfg.FailurePercentageEjection == nil
   220  }
   221  
   222  // onIntervalConfig handles logic required specifically on the receipt of a
   223  // configuration which specifies to count RPC's and periodically perform passive
   224  // health checking based on heuristics defined in configuration every configured
   225  // interval.
   226  //
   227  // Caller must hold b.mu.
   228  func (b *outlierDetectionBalancer) onIntervalConfig() {
   229  	var interval time.Duration
   230  	if b.timerStartTime.IsZero() {
   231  		b.timerStartTime = time.Now()
   232  		for _, epInfo := range b.endpoints.Values() {
   233  			epInfo.callCounter.clear()
   234  		}
   235  		interval = time.Duration(b.cfg.Interval)
   236  	} else {
   237  		interval = time.Duration(b.cfg.Interval) - now().Sub(b.timerStartTime)
   238  		if interval < 0 {
   239  			interval = 0
   240  		}
   241  	}
   242  	b.intervalTimer = afterFunc(interval, b.intervalTimerAlgorithm)
   243  }
   244  
   245  // onNoopConfig handles logic required specifically on the receipt of a
   246  // configuration which specifies the balancer to be a noop.
   247  //
   248  // Caller must hold b.mu.
   249  func (b *outlierDetectionBalancer) onNoopConfig() {
   250  	// "If a config is provided with both the `success_rate_ejection` and
   251  	// `failure_percentage_ejection` fields unset, skip starting the timer and
   252  	// do the following:"
   253  	// "Unset the timer start timestamp."
   254  	b.timerStartTime = time.Time{}
   255  	for _, epInfo := range b.endpoints.Values() {
   256  		// "Uneject all currently ejected endpoints."
   257  		if !epInfo.latestEjectionTimestamp.IsZero() {
   258  			b.unejectEndpoint(epInfo)
   259  		}
   260  		// "Reset each endpoint's ejection time multiplier to 0."
   261  		epInfo.ejectionTimeMultiplier = 0
   262  	}
   263  }
   264  
   265  func (b *outlierDetectionBalancer) UpdateClientConnState(s balancer.ClientConnState) error {
   266  	lbCfg, ok := s.BalancerConfig.(*LBConfig)
   267  	if !ok {
   268  		b.logger.Errorf("received config with unexpected type %T: %v", s.BalancerConfig, s.BalancerConfig)
   269  		return balancer.ErrBadResolverState
   270  	}
   271  
   272  	// Reject whole config if child policy doesn't exist, don't persist it for
   273  	// later.
   274  	bb := balancer.Get(lbCfg.ChildPolicy.Name)
   275  	if bb == nil {
   276  		return fmt.Errorf("outlier detection: child balancer %q not registered", lbCfg.ChildPolicy.Name)
   277  	}
   278  
   279  	// It is safe to read b.cfg here without holding the mutex, as the only
   280  	// write to b.cfg happens later in this function. This function is part of
   281  	// the balancer.Balancer API, so it is guaranteed to be called in a
   282  	// synchronous manner, so it cannot race with this read.
   283  	if b.cfg == nil || b.cfg.ChildPolicy.Name != lbCfg.ChildPolicy.Name {
   284  		if err := b.child.switchTo(bb); err != nil {
   285  			return fmt.Errorf("outlier detection: error switching to child of type %q: %v", lbCfg.ChildPolicy.Name, err)
   286  		}
   287  	}
   288  
   289  	b.mu.Lock()
   290  	// Inhibit child picker updates until this UpdateClientConnState() call
   291  	// completes. If needed, a picker update containing the no-op config bit
   292  	// determined from this config and most recent state from the child will be
   293  	// sent synchronously upward at the end of this UpdateClientConnState()
   294  	// call.
   295  	b.inhibitPickerUpdates = true
   296  	b.updateUnconditionally = false
   297  	b.cfg = lbCfg
   298  
   299  	newEndpoints := resolver.NewEndpointMap[bool]()
   300  	for _, ep := range s.ResolverState.Endpoints {
   301  		newEndpoints.Set(ep, true)
   302  		if _, ok := b.endpoints.Get(ep); !ok {
   303  			b.endpoints.Set(ep, newEndpointInfo())
   304  		}
   305  	}
   306  
   307  	for _, ep := range b.endpoints.Keys() {
   308  		if _, ok := newEndpoints.Get(ep); !ok {
   309  			b.endpoints.Delete(ep)
   310  		}
   311  	}
   312  
   313  	// populate the addrs map.
   314  	b.addrs = map[string]*endpointInfo{}
   315  	for _, ep := range s.ResolverState.Endpoints {
   316  		epInfo, _ := b.endpoints.Get(ep)
   317  		for _, addr := range ep.Addresses {
   318  			if _, ok := b.addrs[addr.Addr]; ok {
   319  				b.logger.Errorf("Endpoints contain duplicate address %q", addr.Addr)
   320  				continue
   321  			}
   322  			b.addrs[addr.Addr] = epInfo
   323  		}
   324  	}
   325  
   326  	if b.intervalTimer != nil {
   327  		b.intervalTimer.Stop()
   328  	}
   329  
   330  	if b.noopConfig() {
   331  		b.onNoopConfig()
   332  	} else {
   333  		b.onIntervalConfig()
   334  	}
   335  	b.mu.Unlock()
   336  
   337  	err := b.child.updateClientConnState(balancer.ClientConnState{
   338  		ResolverState:  s.ResolverState,
   339  		BalancerConfig: b.cfg.ChildPolicy.Config,
   340  	})
   341  
   342  	done := make(chan struct{})
   343  	b.pickerUpdateCh.Put(lbCfgUpdate{
   344  		lbCfg: lbCfg,
   345  		done:  done,
   346  	})
   347  	<-done
   348  
   349  	return err
   350  }
   351  
   352  func (b *outlierDetectionBalancer) ResolverError(err error) {
   353  	b.child.resolverError(err)
   354  }
   355  
   356  func (b *outlierDetectionBalancer) updateSubConnState(scw *subConnWrapper, state balancer.SubConnState) {
   357  	b.mu.Lock()
   358  	defer b.mu.Unlock()
   359  	scw.setLatestConnectivityState(state.ConnectivityState)
   360  	b.scUpdateCh.Put(&scUpdate{
   361  		scw:   scw,
   362  		state: state,
   363  	})
   364  }
   365  
   366  func (b *outlierDetectionBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) {
   367  	b.logger.Errorf("UpdateSubConnState(%v, %+v) called unexpectedly", sc, state)
   368  }
   369  
   370  func (b *outlierDetectionBalancer) Close() {
   371  	b.closed.Fire()
   372  	<-b.done.Done()
   373  	b.child.closeLB()
   374  
   375  	b.scUpdateCh.Close()
   376  	b.pickerUpdateCh.Close()
   377  
   378  	b.mu.Lock()
   379  	defer b.mu.Unlock()
   380  	if b.intervalTimer != nil {
   381  		b.intervalTimer.Stop()
   382  	}
   383  }
   384  
   385  func (b *outlierDetectionBalancer) ExitIdle() {
   386  	b.child.exitIdle()
   387  }
   388  
   389  // wrappedPicker delegates to the child policy's picker, and when the request
   390  // finishes, it increments the corresponding counter in the map entry referenced
   391  // by the subConnWrapper that was picked. If both the `success_rate_ejection`
   392  // and `failure_percentage_ejection` fields are unset in the configuration, this
   393  // picker will not count.
   394  type wrappedPicker struct {
   395  	childPicker balancer.Picker
   396  	noopPicker  bool
   397  }
   398  
   399  func (wp *wrappedPicker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
   400  	pr, err := wp.childPicker.Pick(info)
   401  	if err != nil {
   402  		return balancer.PickResult{}, err
   403  	}
   404  
   405  	done := func(di balancer.DoneInfo) {
   406  		if !wp.noopPicker {
   407  			incrementCounter(pr.SubConn, di)
   408  		}
   409  		if pr.Done != nil {
   410  			pr.Done(di)
   411  		}
   412  	}
   413  	scw, ok := pr.SubConn.(*subConnWrapper)
   414  	if !ok {
   415  		// This can never happen, but check is present for defensive
   416  		// programming.
   417  		logger.Errorf("Picked SubConn from child picker is not a SubConnWrapper")
   418  		return balancer.PickResult{
   419  			SubConn:  pr.SubConn,
   420  			Done:     done,
   421  			Metadata: pr.Metadata,
   422  		}, nil
   423  	}
   424  	return balancer.PickResult{
   425  		SubConn:  scw.SubConn,
   426  		Done:     done,
   427  		Metadata: pr.Metadata,
   428  	}, nil
   429  }
   430  
   431  func incrementCounter(sc balancer.SubConn, info balancer.DoneInfo) {
   432  	scw, ok := sc.(*subConnWrapper)
   433  	if !ok {
   434  		// Shouldn't happen, as comes from child
   435  		return
   436  	}
   437  
   438  	// scw.endpointInfo and callCounter.activeBucket can be written to
   439  	// concurrently (the pointers themselves). Thus, protect the reads here with
   440  	// atomics to prevent data corruption. There exists a race in which you read
   441  	// the endpointInfo or active bucket pointer and then that pointer points to
   442  	// deprecated memory. If this goroutine yields the processor, in between
   443  	// reading the endpointInfo pointer and writing to the active bucket,
   444  	// UpdateAddresses can switch the endpointInfo the scw points to. Writing to
   445  	// an outdated endpoint is a very small race and tolerable. After reading
   446  	// callCounter.activeBucket in this picker a swap call can concurrently
   447  	// change what activeBucket points to. A50 says to swap the pointer, which
   448  	// will cause this race to write to deprecated memory the interval timer
   449  	// algorithm will never read, which makes this race alright.
   450  	epInfo := scw.endpointInfo.Load()
   451  	if epInfo == nil {
   452  		return
   453  	}
   454  	ab := epInfo.callCounter.activeBucket.Load()
   455  
   456  	if info.Err == nil {
   457  		atomic.AddUint32(&ab.numSuccesses, 1)
   458  	} else {
   459  		atomic.AddUint32(&ab.numFailures, 1)
   460  	}
   461  }
   462  
   463  func (b *outlierDetectionBalancer) UpdateState(s balancer.State) {
   464  	b.pickerUpdateCh.Put(s)
   465  }
   466  
   467  func (b *outlierDetectionBalancer) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) {
   468  	oldListener := opts.StateListener
   469  	scw := &subConnWrapper{
   470  		addresses:                  addrs,
   471  		scUpdateCh:                 b.scUpdateCh,
   472  		listener:                   oldListener,
   473  		latestRawConnectivityState: balancer.SubConnState{ConnectivityState: connectivity.Idle},
   474  		latestHealthState:          balancer.SubConnState{ConnectivityState: connectivity.Connecting},
   475  		healthListenerEnabled:      len(addrs) == 1 && pickfirstleaf.IsManagedByPickfirst(addrs[0]),
   476  	}
   477  	opts.StateListener = func(state balancer.SubConnState) { b.updateSubConnState(scw, state) }
   478  	b.mu.Lock()
   479  	defer b.mu.Unlock()
   480  	sc, err := b.ClientConn.NewSubConn(addrs, opts)
   481  	if err != nil {
   482  		return nil, err
   483  	}
   484  	scw.SubConn = sc
   485  	if len(addrs) != 1 {
   486  		return scw, nil
   487  	}
   488  	epInfo, ok := b.addrs[addrs[0].Addr]
   489  	if !ok {
   490  		return scw, nil
   491  	}
   492  	epInfo.sws = append(epInfo.sws, scw)
   493  	scw.endpointInfo.Store(epInfo)
   494  	if !epInfo.latestEjectionTimestamp.IsZero() {
   495  		scw.eject()
   496  	}
   497  	return scw, nil
   498  }
   499  
   500  func (b *outlierDetectionBalancer) RemoveSubConn(sc balancer.SubConn) {
   501  	b.logger.Errorf("RemoveSubConn(%v) called unexpectedly", sc)
   502  }
   503  
   504  // appendIfPresent appends the scw to the endpoint, if the address is present in
   505  // the Outlier Detection balancers address map. Returns nil if not present, and
   506  // the map entry if present.
   507  //
   508  // Caller must hold b.mu.
   509  func (b *outlierDetectionBalancer) appendIfPresent(addr string, scw *subConnWrapper) *endpointInfo {
   510  	epInfo, ok := b.addrs[addr]
   511  	if !ok {
   512  		return nil
   513  	}
   514  
   515  	epInfo.sws = append(epInfo.sws, scw)
   516  	scw.endpointInfo.Store(epInfo)
   517  	return epInfo
   518  }
   519  
   520  // removeSubConnFromEndpointMapEntry removes the scw from its map entry if
   521  // present.
   522  //
   523  // Caller must hold b.mu.
   524  func (b *outlierDetectionBalancer) removeSubConnFromEndpointMapEntry(scw *subConnWrapper) {
   525  	epInfo := scw.endpointInfo.Load()
   526  	if epInfo == nil {
   527  		return
   528  	}
   529  	for i, sw := range epInfo.sws {
   530  		if scw == sw {
   531  			epInfo.sws = append(epInfo.sws[:i], epInfo.sws[i+1:]...)
   532  			return
   533  		}
   534  	}
   535  }
   536  
   537  func (b *outlierDetectionBalancer) UpdateAddresses(sc balancer.SubConn, addrs []resolver.Address) {
   538  	scw, ok := sc.(*subConnWrapper)
   539  	if !ok {
   540  		// Return, shouldn't happen if passed up scw
   541  		return
   542  	}
   543  
   544  	b.ClientConn.UpdateAddresses(scw.SubConn, addrs)
   545  	b.mu.Lock()
   546  	defer b.mu.Unlock()
   547  
   548  	// Note that 0 addresses is a valid update/state for a SubConn to be in.
   549  	// This is correctly handled by this algorithm (handled as part of a non singular
   550  	// old address/new address).
   551  	switch {
   552  	case len(scw.addresses) == 1 && len(addrs) == 1: // single address to single address
   553  		// If the updated address is the same, then there is nothing to do
   554  		// past this point.
   555  		if scw.addresses[0].Addr == addrs[0].Addr {
   556  			return
   557  		}
   558  		b.removeSubConnFromEndpointMapEntry(scw)
   559  		endpointInfo := b.appendIfPresent(addrs[0].Addr, scw)
   560  		if endpointInfo == nil { // uneject unconditionally because could have come from an ejected endpoint
   561  			scw.uneject()
   562  			break
   563  		}
   564  		if endpointInfo.latestEjectionTimestamp.IsZero() { // relay new updated subconn state
   565  			scw.uneject()
   566  		} else {
   567  			scw.eject()
   568  		}
   569  	case len(scw.addresses) == 1: // single address to multiple/no addresses
   570  		b.removeSubConnFromEndpointMapEntry(scw)
   571  		addrInfo := scw.endpointInfo.Load()
   572  		if addrInfo != nil {
   573  			addrInfo.callCounter.clear()
   574  		}
   575  		scw.uneject()
   576  	case len(addrs) == 1: // multiple/no addresses to single address
   577  		endpointInfo := b.appendIfPresent(addrs[0].Addr, scw)
   578  		if endpointInfo != nil && !endpointInfo.latestEjectionTimestamp.IsZero() {
   579  			scw.eject()
   580  		}
   581  	} // otherwise multiple/no addresses to multiple/no addresses; ignore
   582  
   583  	scw.addresses = addrs
   584  }
   585  
   586  // handleSubConnUpdate stores the recent state and forward the update
   587  // if the SubConn is not ejected.
   588  func (b *outlierDetectionBalancer) handleSubConnUpdate(u *scUpdate) {
   589  	scw := u.scw
   590  	scw.clearHealthListener()
   591  	b.child.updateSubConnState(scw, u.state)
   592  }
   593  
   594  func (b *outlierDetectionBalancer) handleSubConnHealthUpdate(u *scHealthUpdate) {
   595  	b.child.updateSubConnHealthState(u.scw, u.state)
   596  }
   597  
   598  // handleEjectedUpdate handles any SubConns that get ejected/unejected, and
   599  // forwards the appropriate corresponding subConnState to the child policy.
   600  func (b *outlierDetectionBalancer) handleEjectedUpdate(u *ejectionUpdate) {
   601  	b.child.handleEjectionUpdate(u)
   602  }
   603  
   604  // handleChildStateUpdate forwards the picker update wrapped in a wrapped picker
   605  // with the noop picker bit present.
   606  func (b *outlierDetectionBalancer) handleChildStateUpdate(u balancer.State) {
   607  	b.childState = u
   608  	b.mu.Lock()
   609  	if b.inhibitPickerUpdates {
   610  		// If a child's state is updated during the suppression of child
   611  		// updates, the synchronous handleLBConfigUpdate function with respect
   612  		// to UpdateClientConnState should return a picker unconditionally.
   613  		b.updateUnconditionally = true
   614  		b.mu.Unlock()
   615  		return
   616  	}
   617  	noopCfg := b.noopConfig()
   618  	b.mu.Unlock()
   619  	b.recentPickerNoop = noopCfg
   620  	b.ClientConn.UpdateState(balancer.State{
   621  		ConnectivityState: b.childState.ConnectivityState,
   622  		Picker: &wrappedPicker{
   623  			childPicker: b.childState.Picker,
   624  			noopPicker:  noopCfg,
   625  		},
   626  	})
   627  }
   628  
   629  // handleLBConfigUpdate compares whether the new config is a noop config or not,
   630  // to the noop bit in the picker if present. It updates the picker if this bit
   631  // changed compared to the picker currently in use.
   632  func (b *outlierDetectionBalancer) handleLBConfigUpdate(u lbCfgUpdate) {
   633  	lbCfg := u.lbCfg
   634  	noopCfg := lbCfg.SuccessRateEjection == nil && lbCfg.FailurePercentageEjection == nil
   635  	// If the child has sent its first update and this config flips the noop
   636  	// bit compared to the most recent picker update sent upward, then a new
   637  	// picker with this updated bit needs to be forwarded upward. If a child
   638  	// update was received during the suppression of child updates within
   639  	// UpdateClientConnState(), then a new picker needs to be forwarded with
   640  	// this updated state, irregardless of whether this new configuration flips
   641  	// the bit.
   642  	if b.childState.Picker != nil && noopCfg != b.recentPickerNoop || b.updateUnconditionally {
   643  		b.recentPickerNoop = noopCfg
   644  		b.ClientConn.UpdateState(balancer.State{
   645  			ConnectivityState: b.childState.ConnectivityState,
   646  			Picker: &wrappedPicker{
   647  				childPicker: b.childState.Picker,
   648  				noopPicker:  noopCfg,
   649  			},
   650  		})
   651  	}
   652  	b.inhibitPickerUpdates = false
   653  	b.updateUnconditionally = false
   654  	close(u.done)
   655  }
   656  
   657  func (b *outlierDetectionBalancer) run() {
   658  	defer b.done.Fire()
   659  	for {
   660  		select {
   661  		case update, ok := <-b.scUpdateCh.Get():
   662  			if !ok {
   663  				return
   664  			}
   665  			b.scUpdateCh.Load()
   666  			if b.closed.HasFired() { // don't send SubConn updates to child after the balancer has been closed
   667  				return
   668  			}
   669  			switch u := update.(type) {
   670  			case *scUpdate:
   671  				b.handleSubConnUpdate(u)
   672  			case *ejectionUpdate:
   673  				b.handleEjectedUpdate(u)
   674  			case *scHealthUpdate:
   675  				b.handleSubConnHealthUpdate(u)
   676  			}
   677  		case update, ok := <-b.pickerUpdateCh.Get():
   678  			if !ok {
   679  				return
   680  			}
   681  			b.pickerUpdateCh.Load()
   682  			if b.closed.HasFired() { // don't send picker updates to grpc after the balancer has been closed
   683  				return
   684  			}
   685  			switch u := update.(type) {
   686  			case balancer.State:
   687  				b.handleChildStateUpdate(u)
   688  			case lbCfgUpdate:
   689  				b.handleLBConfigUpdate(u)
   690  			}
   691  		case <-b.closed.Done():
   692  			return
   693  		}
   694  	}
   695  }
   696  
   697  // intervalTimerAlgorithm ejects and unejects endpoints based on the Outlier
   698  // Detection configuration and data about each endpoint from the previous
   699  // interval.
   700  func (b *outlierDetectionBalancer) intervalTimerAlgorithm() {
   701  	b.mu.Lock()
   702  	defer b.mu.Unlock()
   703  	b.timerStartTime = time.Now()
   704  
   705  	for _, epInfo := range b.endpoints.Values() {
   706  		epInfo.callCounter.swap()
   707  	}
   708  
   709  	if b.cfg.SuccessRateEjection != nil {
   710  		b.successRateAlgorithm()
   711  	}
   712  
   713  	if b.cfg.FailurePercentageEjection != nil {
   714  		b.failurePercentageAlgorithm()
   715  	}
   716  
   717  	for _, epInfo := range b.endpoints.Values() {
   718  		if epInfo.latestEjectionTimestamp.IsZero() && epInfo.ejectionTimeMultiplier > 0 {
   719  			epInfo.ejectionTimeMultiplier--
   720  			continue
   721  		}
   722  		if epInfo.latestEjectionTimestamp.IsZero() {
   723  			// Endpoint is already not ejected, so no need to check for whether
   724  			// to uneject the endpoint below.
   725  			continue
   726  		}
   727  		et := time.Duration(b.cfg.BaseEjectionTime) * time.Duration(epInfo.ejectionTimeMultiplier)
   728  		met := max(time.Duration(b.cfg.BaseEjectionTime), time.Duration(b.cfg.MaxEjectionTime))
   729  		uet := epInfo.latestEjectionTimestamp.Add(min(et, met))
   730  		if now().After(uet) {
   731  			b.unejectEndpoint(epInfo)
   732  		}
   733  	}
   734  
   735  	// This conditional only for testing (since the interval timer algorithm is
   736  	// called manually), will never hit in production.
   737  	if b.intervalTimer != nil {
   738  		b.intervalTimer.Stop()
   739  	}
   740  	b.intervalTimer = afterFunc(time.Duration(b.cfg.Interval), b.intervalTimerAlgorithm)
   741  }
   742  
   743  // endpointsWithAtLeastRequestVolume returns a slice of endpoint information of
   744  // all endpoints with at least request volume passed in.
   745  //
   746  // Caller must hold b.mu.
   747  func (b *outlierDetectionBalancer) endpointsWithAtLeastRequestVolume(requestVolume uint32) []*endpointInfo {
   748  	var endpoints []*endpointInfo
   749  	for _, epInfo := range b.endpoints.Values() {
   750  		bucket1 := epInfo.callCounter.inactiveBucket
   751  		rv := bucket1.numSuccesses + bucket1.numFailures
   752  		if rv >= requestVolume {
   753  			endpoints = append(endpoints, epInfo)
   754  		}
   755  	}
   756  	return endpoints
   757  }
   758  
   759  // meanAndStdDev returns the mean and std dev of the fractions of successful
   760  // requests of the endpoints passed in.
   761  //
   762  // Caller must hold b.mu.
   763  func (b *outlierDetectionBalancer) meanAndStdDev(endpoints []*endpointInfo) (float64, float64) {
   764  	var totalFractionOfSuccessfulRequests float64
   765  	var mean float64
   766  	for _, epInfo := range endpoints {
   767  		bucket := epInfo.callCounter.inactiveBucket
   768  		rv := bucket.numSuccesses + bucket.numFailures
   769  		totalFractionOfSuccessfulRequests += float64(bucket.numSuccesses) / float64(rv)
   770  	}
   771  	mean = totalFractionOfSuccessfulRequests / float64(len(endpoints))
   772  	var sumOfSquares float64
   773  	for _, epInfo := range endpoints {
   774  		bucket := epInfo.callCounter.inactiveBucket
   775  		rv := bucket.numSuccesses + bucket.numFailures
   776  		devFromMean := (float64(bucket.numSuccesses) / float64(rv)) - mean
   777  		sumOfSquares += devFromMean * devFromMean
   778  	}
   779  	variance := sumOfSquares / float64(len(endpoints))
   780  	return mean, math.Sqrt(variance)
   781  }
   782  
   783  // successRateAlgorithm ejects any endpoints where the success rate falls below
   784  // the other endpoints according to mean and standard deviation, and if overall
   785  // applicable from other set heuristics.
   786  //
   787  // Caller must hold b.mu.
   788  func (b *outlierDetectionBalancer) successRateAlgorithm() {
   789  	endpointsToConsider := b.endpointsWithAtLeastRequestVolume(b.cfg.SuccessRateEjection.RequestVolume)
   790  	if len(endpointsToConsider) < int(b.cfg.SuccessRateEjection.MinimumHosts) {
   791  		return
   792  	}
   793  	mean, stddev := b.meanAndStdDev(endpointsToConsider)
   794  	for _, epInfo := range endpointsToConsider {
   795  		bucket := epInfo.callCounter.inactiveBucket
   796  		ejectionCfg := b.cfg.SuccessRateEjection
   797  		if float64(b.numEndpointsEjected)/float64(b.endpoints.Len())*100 >= float64(b.cfg.MaxEjectionPercent) {
   798  			return
   799  		}
   800  		successRate := float64(bucket.numSuccesses) / float64(bucket.numSuccesses+bucket.numFailures)
   801  		requiredSuccessRate := mean - stddev*(float64(ejectionCfg.StdevFactor)/1000)
   802  		if successRate < requiredSuccessRate {
   803  			channelz.Infof(logger, b.channelzParent, "SuccessRate algorithm detected outlier: %s. Parameters: successRate=%f, mean=%f, stddev=%f, requiredSuccessRate=%f", epInfo, successRate, mean, stddev, requiredSuccessRate)
   804  			if uint32(rand.Int32N(100)) < ejectionCfg.EnforcementPercentage {
   805  				b.ejectEndpoint(epInfo)
   806  			}
   807  		}
   808  	}
   809  }
   810  
   811  // failurePercentageAlgorithm ejects any endpoints where the failure percentage
   812  // rate exceeds a set enforcement percentage, if overall applicable from other
   813  // set heuristics.
   814  //
   815  // Caller must hold b.mu.
   816  func (b *outlierDetectionBalancer) failurePercentageAlgorithm() {
   817  	endpointsToConsider := b.endpointsWithAtLeastRequestVolume(b.cfg.FailurePercentageEjection.RequestVolume)
   818  	if len(endpointsToConsider) < int(b.cfg.FailurePercentageEjection.MinimumHosts) {
   819  		return
   820  	}
   821  
   822  	for _, epInfo := range endpointsToConsider {
   823  		bucket := epInfo.callCounter.inactiveBucket
   824  		ejectionCfg := b.cfg.FailurePercentageEjection
   825  		if float64(b.numEndpointsEjected)/float64(b.endpoints.Len())*100 >= float64(b.cfg.MaxEjectionPercent) {
   826  			return
   827  		}
   828  		failurePercentage := (float64(bucket.numFailures) / float64(bucket.numSuccesses+bucket.numFailures)) * 100
   829  		if failurePercentage > float64(b.cfg.FailurePercentageEjection.Threshold) {
   830  			channelz.Infof(logger, b.channelzParent, "FailurePercentage algorithm detected outlier: %s, failurePercentage=%f", epInfo, failurePercentage)
   831  			if uint32(rand.Int32N(100)) < ejectionCfg.EnforcementPercentage {
   832  				b.ejectEndpoint(epInfo)
   833  			}
   834  		}
   835  	}
   836  }
   837  
   838  // Caller must hold b.mu.
   839  func (b *outlierDetectionBalancer) ejectEndpoint(epInfo *endpointInfo) {
   840  	b.numEndpointsEjected++
   841  	epInfo.latestEjectionTimestamp = b.timerStartTime
   842  	epInfo.ejectionTimeMultiplier++
   843  	for _, sbw := range epInfo.sws {
   844  		sbw.eject()
   845  		channelz.Infof(logger, b.channelzParent, "Subchannel ejected: %s", sbw)
   846  	}
   847  
   848  }
   849  
   850  // Caller must hold b.mu.
   851  func (b *outlierDetectionBalancer) unejectEndpoint(epInfo *endpointInfo) {
   852  	b.numEndpointsEjected--
   853  	epInfo.latestEjectionTimestamp = time.Time{}
   854  	for _, sbw := range epInfo.sws {
   855  		sbw.uneject()
   856  		channelz.Infof(logger, b.channelzParent, "Subchannel unejected: %s", sbw)
   857  	}
   858  }
   859  
   860  // synchronizingBalancerWrapper serializes calls into balancer (to uphold the
   861  // balancer.Balancer API guarantee of synchronous calls). It also ensures a
   862  // consistent order of locking mutexes when using SubConn listeners to avoid
   863  // deadlocks.
   864  type synchronizingBalancerWrapper struct {
   865  	// mu should not be used directly from outside this struct, instead use
   866  	// methods defined on the struct.
   867  	mu sync.Mutex
   868  	lb *gracefulswitch.Balancer
   869  }
   870  
   871  func (sbw *synchronizingBalancerWrapper) switchTo(builder balancer.Builder) error {
   872  	sbw.mu.Lock()
   873  	defer sbw.mu.Unlock()
   874  	return sbw.lb.SwitchTo(builder)
   875  }
   876  
   877  func (sbw *synchronizingBalancerWrapper) updateClientConnState(state balancer.ClientConnState) error {
   878  	sbw.mu.Lock()
   879  	defer sbw.mu.Unlock()
   880  	return sbw.lb.UpdateClientConnState(state)
   881  }
   882  
   883  func (sbw *synchronizingBalancerWrapper) resolverError(err error) {
   884  	sbw.mu.Lock()
   885  	defer sbw.mu.Unlock()
   886  	sbw.lb.ResolverError(err)
   887  }
   888  
   889  func (sbw *synchronizingBalancerWrapper) closeLB() {
   890  	sbw.mu.Lock()
   891  	defer sbw.mu.Unlock()
   892  	sbw.lb.Close()
   893  }
   894  
   895  func (sbw *synchronizingBalancerWrapper) exitIdle() {
   896  	sbw.mu.Lock()
   897  	defer sbw.mu.Unlock()
   898  	sbw.lb.ExitIdle()
   899  }
   900  
   901  func (sbw *synchronizingBalancerWrapper) updateSubConnHealthState(scw *subConnWrapper, scs balancer.SubConnState) {
   902  	sbw.mu.Lock()
   903  	defer sbw.mu.Unlock()
   904  	scw.updateSubConnHealthState(scs)
   905  }
   906  
   907  func (sbw *synchronizingBalancerWrapper) updateSubConnState(scw *subConnWrapper, scs balancer.SubConnState) {
   908  	sbw.mu.Lock()
   909  	defer sbw.mu.Unlock()
   910  	scw.updateSubConnConnectivityState(scs)
   911  }
   912  
   913  func (sbw *synchronizingBalancerWrapper) handleEjectionUpdate(u *ejectionUpdate) {
   914  	sbw.mu.Lock()
   915  	defer sbw.mu.Unlock()
   916  	if u.isEjected {
   917  		u.scw.handleEjection()
   918  	} else {
   919  		u.scw.handleUnejection()
   920  	}
   921  }
   922  
   923  // endpointInfo contains the runtime information about an endpoint that pertains
   924  // to Outlier Detection. This struct and all of its fields is protected by
   925  // outlierDetectionBalancer.mu in the case where it is accessed through the
   926  // address or endpoint map. In the case of Picker callbacks, the writes to the
   927  // activeBucket of callCounter are protected by atomically loading and storing
   928  // unsafe.Pointers (see further explanation in incrementCounter()).
   929  type endpointInfo struct {
   930  	// The call result counter object.
   931  	callCounter *callCounter
   932  
   933  	// The latest ejection timestamp, or zero if the endpoint is currently not
   934  	// ejected.
   935  	latestEjectionTimestamp time.Time
   936  
   937  	// The current ejection time multiplier, starting at 0.
   938  	ejectionTimeMultiplier int64
   939  
   940  	// A list of subchannel wrapper objects that correspond to this endpoint.
   941  	sws []*subConnWrapper
   942  }
   943  
   944  func (a *endpointInfo) String() string {
   945  	var res strings.Builder
   946  	res.WriteString("[")
   947  	for _, sw := range a.sws {
   948  		res.WriteString(sw.String())
   949  	}
   950  	res.WriteString("]")
   951  	return res.String()
   952  }
   953  
   954  func newEndpointInfo() *endpointInfo {
   955  	return &endpointInfo{
   956  		callCounter: newCallCounter(),
   957  	}
   958  }