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 }