google.golang.org/grpc@v1.72.2/xds/internal/balancer/clusterresolver/resource_resolver_eds.go (about) 1 /* 2 * 3 * Copyright 2023 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 clusterresolver 20 21 import ( 22 "sync" 23 24 "google.golang.org/grpc/internal/grpclog" 25 "google.golang.org/grpc/internal/grpcsync" 26 "google.golang.org/grpc/xds/internal/xdsclient/xdsresource" 27 ) 28 29 type edsDiscoveryMechanism struct { 30 nameToWatch string 31 cancelWatch func() 32 topLevelResolver topLevelResolver 33 stopped *grpcsync.Event 34 logger *grpclog.PrefixLogger 35 36 mu sync.Mutex 37 update *xdsresource.EndpointsUpdate // Nil indicates no update received so far. 38 } 39 40 func (er *edsDiscoveryMechanism) lastUpdate() (any, bool) { 41 er.mu.Lock() 42 defer er.mu.Unlock() 43 44 if er.update == nil { 45 return nil, false 46 } 47 return *er.update, true 48 } 49 50 func (er *edsDiscoveryMechanism) resolveNow() { 51 } 52 53 // The definition of stop() mentions that implementations must not invoke any 54 // methods on the topLevelResolver once the call to `stop()` returns. 55 func (er *edsDiscoveryMechanism) stop() { 56 // Canceling a watch with the xDS client can race with an xDS response 57 // received around the same time, and can result in the watch callback being 58 // invoked after the watch is canceled. Callers need to handle this race, 59 // and we fire the stopped event here to ensure that a watch callback 60 // invocation around the same time becomes a no-op. 61 er.stopped.Fire() 62 er.cancelWatch() 63 } 64 65 // newEDSResolver returns an implementation of the endpointsResolver interface 66 // that uses EDS to resolve the given name to endpoints. 67 func newEDSResolver(nameToWatch string, producer xdsresource.Producer, topLevelResolver topLevelResolver, logger *grpclog.PrefixLogger) *edsDiscoveryMechanism { 68 ret := &edsDiscoveryMechanism{ 69 nameToWatch: nameToWatch, 70 topLevelResolver: topLevelResolver, 71 logger: logger, 72 stopped: grpcsync.NewEvent(), 73 } 74 ret.cancelWatch = xdsresource.WatchEndpoints(producer, nameToWatch, ret) 75 return ret 76 } 77 78 // OnUpdate is invoked to report an update for the resource being watched. 79 func (er *edsDiscoveryMechanism) OnUpdate(update *xdsresource.EndpointsResourceData, onDone xdsresource.OnDoneFunc) { 80 if er.stopped.HasFired() { 81 onDone() 82 return 83 } 84 85 er.mu.Lock() 86 er.update = &update.Resource 87 er.mu.Unlock() 88 89 er.topLevelResolver.onUpdate(onDone) 90 } 91 92 func (er *edsDiscoveryMechanism) OnError(err error, onDone xdsresource.OnDoneFunc) { 93 if er.stopped.HasFired() { 94 onDone() 95 return 96 } 97 98 if er.logger.V(2) { 99 er.logger.Infof("EDS discovery mechanism for resource %q reported error: %v", er.nameToWatch, err) 100 } 101 102 er.mu.Lock() 103 if er.update != nil { 104 // Continue using a previously received good configuration if one 105 // exists. 106 er.mu.Unlock() 107 onDone() 108 return 109 } 110 111 // Else report an empty update that would result in no priority child being 112 // created for this discovery mechanism. This would result in the priority 113 // LB policy reporting TRANSIENT_FAILURE (as there would be no priorities or 114 // localities) if this was the only discovery mechanism, or would result in 115 // the priority LB policy using a lower priority discovery mechanism when 116 // that becomes available. 117 er.update = &xdsresource.EndpointsUpdate{} 118 er.mu.Unlock() 119 120 er.topLevelResolver.onUpdate(onDone) 121 } 122 123 func (er *edsDiscoveryMechanism) OnResourceDoesNotExist(onDone xdsresource.OnDoneFunc) { 124 if er.stopped.HasFired() { 125 onDone() 126 return 127 } 128 129 er.logger.Warningf("EDS discovery mechanism for resource %q reported resource-does-not-exist error", er.nameToWatch) 130 131 // Report an empty update that would result in no priority child being 132 // created for this discovery mechanism. This would result in the priority 133 // LB policy reporting TRANSIENT_FAILURE (as there would be no priorities or 134 // localities) if this was the only discovery mechanism, or would result in 135 // the priority LB policy using a lower priority discovery mechanism when 136 // that becomes available. 137 er.mu.Lock() 138 er.update = &xdsresource.EndpointsUpdate{} 139 er.mu.Unlock() 140 141 er.topLevelResolver.onUpdate(onDone) 142 }