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

     1  /*
     2   *
     3   * Copyright 2020 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 clustermanager implements the cluster manager LB policy for xds.
    20  package clustermanager
    21  
    22  import (
    23  	"encoding/json"
    24  	"fmt"
    25  	"time"
    26  
    27  	"google.golang.org/grpc/balancer"
    28  	"google.golang.org/grpc/balancer/base"
    29  	"google.golang.org/grpc/connectivity"
    30  	"google.golang.org/grpc/grpclog"
    31  	"google.golang.org/grpc/internal/balancergroup"
    32  	internalgrpclog "google.golang.org/grpc/internal/grpclog"
    33  	"google.golang.org/grpc/internal/hierarchy"
    34  	"google.golang.org/grpc/internal/pretty"
    35  	"google.golang.org/grpc/resolver"
    36  	"google.golang.org/grpc/serviceconfig"
    37  )
    38  
    39  const balancerName = "xds_cluster_manager_experimental"
    40  
    41  func init() {
    42  	balancer.Register(bb{})
    43  }
    44  
    45  type bb struct{}
    46  
    47  func (bb) Build(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.Balancer {
    48  	b := &bal{}
    49  	b.logger = prefixLogger(b)
    50  	b.stateAggregator = newBalancerStateAggregator(cc, b.logger)
    51  	b.bg = balancergroup.New(balancergroup.Options{
    52  		CC:                      cc,
    53  		BuildOpts:               opts,
    54  		StateAggregator:         b.stateAggregator,
    55  		Logger:                  b.logger,
    56  		SubBalancerCloseTimeout: time.Duration(0), // Disable caching of removed child policies
    57  	})
    58  	b.logger.Infof("Created")
    59  	return b
    60  }
    61  
    62  func (bb) Name() string {
    63  	return balancerName
    64  }
    65  
    66  func (bb) ParseConfig(c json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
    67  	return parseConfig(c)
    68  }
    69  
    70  type bal struct {
    71  	logger          *internalgrpclog.PrefixLogger
    72  	bg              *balancergroup.BalancerGroup
    73  	stateAggregator *balancerStateAggregator
    74  
    75  	children map[string]childConfig
    76  }
    77  
    78  func (b *bal) setErrorPickerForChild(childName string, err error) {
    79  	b.stateAggregator.UpdateState(childName, balancer.State{
    80  		ConnectivityState: connectivity.TransientFailure,
    81  		Picker:            base.NewErrPicker(err),
    82  	})
    83  }
    84  
    85  func (b *bal) updateChildren(s balancer.ClientConnState, newConfig *lbConfig) error {
    86  	// TODO: Get rid of handling hierarchy in addresses. This LB policy never
    87  	// gets addresses from the resolver.
    88  	addressesSplit := hierarchy.Group(s.ResolverState.Addresses)
    89  	endpointsSplit := hierarchy.GroupEndpoints(s.ResolverState.Endpoints)
    90  
    91  	// Remove sub-balancers that are not in the new list from the aggregator and
    92  	// balancergroup.
    93  	for name := range b.children {
    94  		if _, ok := newConfig.Children[name]; !ok {
    95  			b.stateAggregator.remove(name)
    96  			b.bg.Remove(name)
    97  		}
    98  	}
    99  
   100  	var retErr error
   101  	for childName, childCfg := range newConfig.Children {
   102  		lbCfg := childCfg.ChildPolicy.Config
   103  		if _, ok := b.children[childName]; !ok {
   104  			// Add new sub-balancers to the aggregator and balancergroup.
   105  			b.stateAggregator.add(childName)
   106  			b.bg.Add(childName, balancer.Get(childCfg.ChildPolicy.Name))
   107  		} else {
   108  			// If the child policy type has changed for existing sub-balancers,
   109  			// parse the new config and send down the config update to the
   110  			// balancergroup, which will take care of gracefully switching the
   111  			// child over to the new policy.
   112  			//
   113  			// If we run into errors here, we need to ensure that RPCs to this
   114  			// child fail, while RPCs to other children with good configs
   115  			// continue to succeed.
   116  			newPolicyName, oldPolicyName := childCfg.ChildPolicy.Name, b.children[childName].ChildPolicy.Name
   117  			if newPolicyName != oldPolicyName {
   118  				var err error
   119  				var cfgJSON []byte
   120  				cfgJSON, err = childCfg.ChildPolicy.MarshalJSON()
   121  				if err != nil {
   122  					retErr = fmt.Errorf("failed to JSON marshal load balancing policy for child %q: %v", childName, err)
   123  					b.setErrorPickerForChild(childName, retErr)
   124  					continue
   125  				}
   126  				// This overwrites lbCfg to be in the format expected by the
   127  				// gracefulswitch balancer. So, when this config is pushed to
   128  				// the child (below), it will result in a graceful switch to the
   129  				// new child policy.
   130  				lbCfg, err = balancergroup.ParseConfig(cfgJSON)
   131  				if err != nil {
   132  					retErr = fmt.Errorf("failed to parse load balancing policy for child %q: %v", childName, err)
   133  					b.setErrorPickerForChild(childName, retErr)
   134  					continue
   135  				}
   136  			}
   137  		}
   138  
   139  		if err := b.bg.UpdateClientConnState(childName, balancer.ClientConnState{
   140  			ResolverState: resolver.State{
   141  				Addresses:     addressesSplit[childName],
   142  				Endpoints:     endpointsSplit[childName],
   143  				ServiceConfig: s.ResolverState.ServiceConfig,
   144  				Attributes:    s.ResolverState.Attributes,
   145  			},
   146  			BalancerConfig: lbCfg,
   147  		}); err != nil {
   148  			retErr = fmt.Errorf("failed to push new configuration %v to child %q", childCfg.ChildPolicy.Config, childName)
   149  			b.setErrorPickerForChild(childName, retErr)
   150  		}
   151  
   152  		// Picker update is sent to the parent ClientConn only after the
   153  		// new child policy returns a picker. So, there is no need to
   154  		// set needUpdateStateOnResume to true here.
   155  	}
   156  
   157  	b.children = newConfig.Children
   158  
   159  	// If multiple sub-balancers run into errors, we will return only the last
   160  	// one, which is still good enough, since the grpc channel will anyways
   161  	// return this error as balancer.ErrBadResolver to the name resolver,
   162  	// resulting in re-resolution attempts.
   163  	return retErr
   164  
   165  	// Adding or removing a sub-balancer will result in the
   166  	// needUpdateStateOnResume bit to true which results in a picker update once
   167  	// resumeStateUpdates() is called.
   168  }
   169  
   170  func (b *bal) UpdateClientConnState(s balancer.ClientConnState) error {
   171  	if b.logger.V(2) {
   172  		b.logger.Infof("Received update from resolver, balancer config: %+v", pretty.ToJSON(s.BalancerConfig))
   173  	}
   174  
   175  	newConfig, ok := s.BalancerConfig.(*lbConfig)
   176  	if !ok {
   177  		return fmt.Errorf("unexpected balancer config with type: %T", s.BalancerConfig)
   178  	}
   179  
   180  	b.stateAggregator.pauseStateUpdates()
   181  	defer b.stateAggregator.resumeStateUpdates()
   182  	return b.updateChildren(s, newConfig)
   183  }
   184  
   185  func (b *bal) ResolverError(err error) {
   186  	b.bg.ResolverError(err)
   187  }
   188  
   189  func (b *bal) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) {
   190  	b.logger.Errorf("UpdateSubConnState(%v, %+v) called unexpectedly", sc, state)
   191  }
   192  
   193  func (b *bal) Close() {
   194  	b.stateAggregator.close()
   195  	b.bg.Close()
   196  	b.logger.Infof("Shutdown")
   197  }
   198  
   199  func (b *bal) ExitIdle() {
   200  	b.bg.ExitIdle()
   201  }
   202  
   203  const prefix = "[xds-cluster-manager-lb %p] "
   204  
   205  var logger = grpclog.Component("xds")
   206  
   207  func prefixLogger(p *bal) *internalgrpclog.PrefixLogger {
   208  	return internalgrpclog.NewPrefixLogger(logger, fmt.Sprintf(prefix, p))
   209  }