google.golang.org/grpc@v1.74.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 // ResourceChanged is invoked to report an update for the resource being watched. 79 func (er *edsDiscoveryMechanism) ResourceChanged(update *xdsresource.EndpointsResourceData, onDone func()) { 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) ResourceError(err error, onDone func()) { 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 resource error: %v", er.nameToWatch, err) 100 } 101 102 // Report an empty update that would result in no priority child being 103 // created for this discovery mechanism. This would result in the priority 104 // LB policy reporting TRANSIENT_FAILURE (as there would be no priorities or 105 // localities) if this was the only discovery mechanism, or would result in 106 // the priority LB policy using a lower priority discovery mechanism when 107 // that becomes available. 108 er.mu.Lock() 109 er.update = &xdsresource.EndpointsUpdate{} 110 er.mu.Unlock() 111 112 er.topLevelResolver.onUpdate(onDone) 113 } 114 115 func (er *edsDiscoveryMechanism) AmbientError(err error, onDone func()) { 116 if er.stopped.HasFired() { 117 onDone() 118 return 119 } 120 121 if er.logger.V(2) { 122 er.logger.Infof("EDS discovery mechanism for resource %q reported ambient error: %v", er.nameToWatch, err) 123 } 124 }