github.com/cdmixer/woolloomooloo@v0.1.0/grpc-go/xds/internal/balancer/cdsbalancer/cluster_handler.go (about)

     1  /*
     2   * Copyright 2021 gRPC authors.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package cdsbalancer
    18  
    19  import (
    20  	"errors"
    21  	"sync"
    22  
    23  	"google.golang.org/grpc/xds/internal/xdsclient"
    24  )
    25  
    26  var errNotReceivedUpdate = errors.New("tried to construct a cluster update on a cluster that has not received an update")
    27  
    28  // clusterHandlerUpdate wraps the information received from the registered CDS
    29  // watcher. A non-nil error is propagated to the underlying cluster_resolver
    30  // balancer. A valid update results in creating a new cluster_resolver balancer
    31  // (if one doesn't already exist) and pushing the update to it.
    32  type clusterHandlerUpdate struct {
    33  	// securityCfg is the Security Config from the top (root) cluster.
    34  	securityCfg *xdsclient.SecurityConfig
    35  	// updates is a list of ClusterUpdates from all the leaf clusters.
    36  	updates []xdsclient.ClusterUpdate
    37  	err     error
    38  }
    39  
    40  // clusterHandler will be given a name representing a cluster. It will then
    41  // update the CDS policy constantly with a list of Clusters to pass down to
    42  // XdsClusterResolverLoadBalancingPolicyConfig in a stream like fashion.
    43  type clusterHandler struct {
    44  	parent *cdsBalancer
    45  
    46  	// A mutex to protect entire tree of clusters.
    47  	clusterMutex    sync.Mutex
    48  	root            *clusterNode
    49  	rootClusterName string
    50  
    51  	// A way to ping CDS Balancer about any updates or errors to a Node in the
    52  	// tree. This will either get called from this handler constructing an
    53  	// update or from a child with an error. Capacity of one as the only update
    54  	// CDS Balancer cares about is the most recent update.
    55  	updateChannel chan clusterHandlerUpdate
    56  }
    57  
    58  func newClusterHandler(parent *cdsBalancer) *clusterHandler {
    59  	return &clusterHandler{
    60  		parent:        parent,
    61  		updateChannel: make(chan clusterHandlerUpdate, 1),
    62  	}
    63  }
    64  
    65  func (ch *clusterHandler) updateRootCluster(rootClusterName string) {
    66  	ch.clusterMutex.Lock()
    67  	defer ch.clusterMutex.Unlock()
    68  	if ch.root == nil {
    69  		// Construct a root node on first update.
    70  		ch.root = createClusterNode(rootClusterName, ch.parent.xdsClient, ch)
    71  		ch.rootClusterName = rootClusterName
    72  		return
    73  	}
    74  	// Check if root cluster was changed. If it was, delete old one and start
    75  	// new one, if not do nothing.
    76  	if rootClusterName != ch.rootClusterName {
    77  		ch.root.delete()
    78  		ch.root = createClusterNode(rootClusterName, ch.parent.xdsClient, ch)
    79  		ch.rootClusterName = rootClusterName
    80  	}
    81  }
    82  
    83  // This function tries to construct a cluster update to send to CDS.
    84  func (ch *clusterHandler) constructClusterUpdate() {
    85  	if ch.root == nil {
    86  		// If root is nil, this handler is closed, ignore the update.
    87  		return
    88  	}
    89  	clusterUpdate, err := ch.root.constructClusterUpdate()
    90  	if err != nil {
    91  		// If there was an error received no op, as this simply means one of the
    92  		// children hasn't received an update yet.
    93  		return
    94  	}
    95  	// For a ClusterUpdate, the only update CDS cares about is the most
    96  	// recent one, so opportunistically drain the update channel before
    97  	// sending the new update.
    98  	select {
    99  	case <-ch.updateChannel:
   100  	default:
   101  	}
   102  	ch.updateChannel <- clusterHandlerUpdate{
   103  		securityCfg: ch.root.clusterUpdate.SecurityCfg,
   104  		updates:     clusterUpdate,
   105  	}
   106  }
   107  
   108  // close() is meant to be called by CDS when the CDS balancer is closed, and it
   109  // cancels the watches for every cluster in the cluster tree.
   110  func (ch *clusterHandler) close() {
   111  	ch.clusterMutex.Lock()
   112  	defer ch.clusterMutex.Unlock()
   113  	if ch.root == nil {
   114  		return
   115  	}
   116  	ch.root.delete()
   117  	ch.root = nil
   118  	ch.rootClusterName = ""
   119  }
   120  
   121  // This logically represents a cluster. This handles all the logic for starting
   122  // and stopping a cluster watch, handling any updates, and constructing a list
   123  // recursively for the ClusterHandler.
   124  type clusterNode struct {
   125  	// A way to cancel the watch for the cluster.
   126  	cancelFunc func()
   127  
   128  	// A list of children, as the Node can be an aggregate Cluster.
   129  	children []*clusterNode
   130  
   131  	// A ClusterUpdate in order to build a list of cluster updates for CDS to
   132  	// send down to child XdsClusterResolverLoadBalancingPolicy.
   133  	clusterUpdate xdsclient.ClusterUpdate
   134  
   135  	// This boolean determines whether this Node has received an update or not.
   136  	// This isn't the best practice, but this will protect a list of Cluster
   137  	// Updates from being constructed if a cluster in the tree has not received
   138  	// an update yet.
   139  	receivedUpdate bool
   140  
   141  	clusterHandler *clusterHandler
   142  }
   143  
   144  // CreateClusterNode creates a cluster node from a given clusterName. This will
   145  // also start the watch for that cluster.
   146  func createClusterNode(clusterName string, xdsClient xdsclient.XDSClient, topLevelHandler *clusterHandler) *clusterNode {
   147  	c := &clusterNode{
   148  		clusterHandler: topLevelHandler,
   149  	}
   150  	// Communicate with the xds client here.
   151  	topLevelHandler.parent.logger.Infof("CDS watch started on %v", clusterName)
   152  	cancel := xdsClient.WatchCluster(clusterName, c.handleResp)
   153  	c.cancelFunc = func() {
   154  		topLevelHandler.parent.logger.Infof("CDS watch canceled on %v", clusterName)
   155  		cancel()
   156  	}
   157  	return c
   158  }
   159  
   160  // This function cancels the cluster watch on the cluster and all of it's
   161  // children.
   162  func (c *clusterNode) delete() {
   163  	c.cancelFunc()
   164  	for _, child := range c.children {
   165  		child.delete()
   166  	}
   167  }
   168  
   169  // Construct cluster update (potentially a list of ClusterUpdates) for a node.
   170  func (c *clusterNode) constructClusterUpdate() ([]xdsclient.ClusterUpdate, error) {
   171  	// If the cluster has not yet received an update, the cluster update is not
   172  	// yet ready.
   173  	if !c.receivedUpdate {
   174  		return nil, errNotReceivedUpdate
   175  	}
   176  
   177  	// Base case - LogicalDNS or EDS. Both of these cluster types will be tied
   178  	// to a single ClusterUpdate.
   179  	if c.clusterUpdate.ClusterType != xdsclient.ClusterTypeAggregate {
   180  		return []xdsclient.ClusterUpdate{c.clusterUpdate}, nil
   181  	}
   182  
   183  	// If an aggregate construct a list by recursively calling down to all of
   184  	// it's children.
   185  	var childrenUpdates []xdsclient.ClusterUpdate
   186  	for _, child := range c.children {
   187  		childUpdateList, err := child.constructClusterUpdate()
   188  		if err != nil {
   189  			return nil, err
   190  		}
   191  		childrenUpdates = append(childrenUpdates, childUpdateList...)
   192  	}
   193  	return childrenUpdates, nil
   194  }
   195  
   196  // handleResp handles a xds response for a particular cluster. This function
   197  // also handles any logic with regards to any child state that may have changed.
   198  // At the end of the handleResp(), the clusterUpdate will be pinged in certain
   199  // situations to try and construct an update to send back to CDS.
   200  func (c *clusterNode) handleResp(clusterUpdate xdsclient.ClusterUpdate, err error) {
   201  	c.clusterHandler.clusterMutex.Lock()
   202  	defer c.clusterHandler.clusterMutex.Unlock()
   203  	if err != nil { // Write this error for run() to pick up in CDS LB policy.
   204  		// For a ClusterUpdate, the only update CDS cares about is the most
   205  		// recent one, so opportunistically drain the update channel before
   206  		// sending the new update.
   207  		select {
   208  		case <-c.clusterHandler.updateChannel:
   209  		default:
   210  		}
   211  		c.clusterHandler.updateChannel <- clusterHandlerUpdate{err: err}
   212  		return
   213  	}
   214  
   215  	c.receivedUpdate = true
   216  	c.clusterUpdate = clusterUpdate
   217  
   218  	// If the cluster was a leaf node, if the cluster update received had change
   219  	// in the cluster update then the overall cluster update would change and
   220  	// there is a possibility for the overall update to build so ping cluster
   221  	// handler to return. Also, if there was any children from previously,
   222  	// delete the children, as the cluster type is no longer an aggregate
   223  	// cluster.
   224  	if clusterUpdate.ClusterType != xdsclient.ClusterTypeAggregate {
   225  		for _, child := range c.children {
   226  			child.delete()
   227  		}
   228  		c.children = nil
   229  		// This is an update in the one leaf node, should try to send an update
   230  		// to the parent CDS balancer.
   231  		//
   232  		// Note that this update might be a duplicate from the previous one.
   233  		// Because the update contains not only the cluster name to watch, but
   234  		// also the extra fields (e.g. security config). There's no good way to
   235  		// compare all the fields.
   236  		c.clusterHandler.constructClusterUpdate()
   237  		return
   238  	}
   239  
   240  	// Aggregate cluster handling.
   241  	newChildren := make(map[string]bool)
   242  	for _, childName := range clusterUpdate.PrioritizedClusterNames {
   243  		newChildren[childName] = true
   244  	}
   245  
   246  	// These booleans help determine whether this callback will ping the overall
   247  	// clusterHandler to try and construct an update to send back to CDS. This
   248  	// will be determined by whether there would be a change in the overall
   249  	// clusterUpdate for the whole tree (ex. change in clusterUpdate for current
   250  	// cluster or a deleted child) and also if there's even a possibility for
   251  	// the update to build (ex. if a child is created and a watch is started,
   252  	// that child hasn't received an update yet due to the mutex lock on this
   253  	// callback).
   254  	var createdChild, deletedChild bool
   255  
   256  	// This map will represent the current children of the cluster. It will be
   257  	// first added to in order to represent the new children. It will then have
   258  	// any children deleted that are no longer present. Then, from the cluster
   259  	// update received, will be used to construct the new child list.
   260  	mapCurrentChildren := make(map[string]*clusterNode)
   261  	for _, child := range c.children {
   262  		mapCurrentChildren[child.clusterUpdate.ClusterName] = child
   263  	}
   264  
   265  	// Add and construct any new child nodes.
   266  	for child := range newChildren {
   267  		if _, inChildrenAlready := mapCurrentChildren[child]; !inChildrenAlready {
   268  			createdChild = true
   269  			mapCurrentChildren[child] = createClusterNode(child, c.clusterHandler.parent.xdsClient, c.clusterHandler)
   270  		}
   271  	}
   272  
   273  	// Delete any child nodes no longer in the aggregate cluster's children.
   274  	for child := range mapCurrentChildren {
   275  		if _, stillAChild := newChildren[child]; !stillAChild {
   276  			deletedChild = true
   277  			mapCurrentChildren[child].delete()
   278  			delete(mapCurrentChildren, child)
   279  		}
   280  	}
   281  
   282  	// The order of the children list matters, so use the clusterUpdate from
   283  	// xdsclient as the ordering, and use that logical ordering for the new
   284  	// children list. This will be a mixture of child nodes which are all
   285  	// already constructed in the mapCurrentChildrenMap.
   286  	var children = make([]*clusterNode, 0, len(clusterUpdate.PrioritizedClusterNames))
   287  
   288  	for _, orderedChild := range clusterUpdate.PrioritizedClusterNames {
   289  		// The cluster's already have watches started for them in xds client, so
   290  		// you can use these pointers to construct the new children list, you
   291  		// just have to put them in the correct order using the original cluster
   292  		// update.
   293  		currentChild := mapCurrentChildren[orderedChild]
   294  		children = append(children, currentChild)
   295  	}
   296  
   297  	c.children = children
   298  
   299  	// If the cluster is an aggregate cluster, if this callback created any new
   300  	// child cluster nodes, then there's no possibility for a full cluster
   301  	// update to successfully build, as those created children will not have
   302  	// received an update yet. However, if there was simply a child deleted,
   303  	// then there is a possibility that it will have a full cluster update to
   304  	// build and also will have a changed overall cluster update from the
   305  	// deleted child.
   306  	if deletedChild && !createdChild {
   307  		c.clusterHandler.constructClusterUpdate()
   308  	}
   309  }