github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/internal/xdsclient/pubsub/watch.go (about) 1 /* 2 * 3 * Copyright 2021 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 package pubsub 19 20 import ( 21 "fmt" 22 "sync" 23 "time" 24 25 "github.com/hxx258456/ccgo/grpc/internal/pretty" 26 "github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient/xdsresource" 27 ) 28 29 type watchInfoState int 30 31 const ( 32 watchInfoStateStarted watchInfoState = iota 33 watchInfoStateRespReceived 34 watchInfoStateTimeout 35 watchInfoStateCanceled 36 ) 37 38 // watchInfo holds all the information from a watch() call. 39 type watchInfo struct { 40 c *Pubsub 41 rType xdsresource.ResourceType 42 target string 43 44 ldsCallback func(xdsresource.ListenerUpdate, error) 45 rdsCallback func(xdsresource.RouteConfigUpdate, error) 46 cdsCallback func(xdsresource.ClusterUpdate, error) 47 edsCallback func(xdsresource.EndpointsUpdate, error) 48 49 expiryTimer *time.Timer 50 51 // mu protects state, and c.scheduleCallback(). 52 // - No callback should be scheduled after watchInfo is canceled. 53 // - No timeout error should be scheduled after watchInfo is resp received. 54 mu sync.Mutex 55 state watchInfoState 56 } 57 58 func (wi *watchInfo) newUpdate(update interface{}) { 59 wi.mu.Lock() 60 defer wi.mu.Unlock() 61 if wi.state == watchInfoStateCanceled { 62 return 63 } 64 wi.state = watchInfoStateRespReceived 65 wi.expiryTimer.Stop() 66 wi.c.scheduleCallback(wi, update, nil) 67 } 68 69 func (wi *watchInfo) newError(err error) { 70 wi.mu.Lock() 71 defer wi.mu.Unlock() 72 if wi.state == watchInfoStateCanceled { 73 return 74 } 75 wi.state = watchInfoStateRespReceived 76 wi.expiryTimer.Stop() 77 wi.sendErrorLocked(err) 78 } 79 80 func (wi *watchInfo) resourceNotFound() { 81 wi.mu.Lock() 82 defer wi.mu.Unlock() 83 if wi.state == watchInfoStateCanceled { 84 return 85 } 86 wi.state = watchInfoStateRespReceived 87 wi.expiryTimer.Stop() 88 wi.sendErrorLocked(xdsresource.NewErrorf(xdsresource.ErrorTypeResourceNotFound, "xds: %v target %s not found in received response", wi.rType, wi.target)) 89 } 90 91 func (wi *watchInfo) timeout() { 92 wi.mu.Lock() 93 defer wi.mu.Unlock() 94 if wi.state == watchInfoStateCanceled || wi.state == watchInfoStateRespReceived { 95 return 96 } 97 wi.state = watchInfoStateTimeout 98 wi.sendErrorLocked(fmt.Errorf("xds: %v target %s not found, watcher timeout", wi.rType, wi.target)) 99 } 100 101 // Caller must hold wi.mu. 102 func (wi *watchInfo) sendErrorLocked(err error) { 103 var ( 104 u interface{} 105 ) 106 switch wi.rType { 107 case xdsresource.ListenerResource: 108 u = xdsresource.ListenerUpdate{} 109 case xdsresource.RouteConfigResource: 110 u = xdsresource.RouteConfigUpdate{} 111 case xdsresource.ClusterResource: 112 u = xdsresource.ClusterUpdate{} 113 case xdsresource.EndpointsResource: 114 u = xdsresource.EndpointsUpdate{} 115 } 116 wi.c.scheduleCallback(wi, u, err) 117 } 118 119 func (wi *watchInfo) cancel() { 120 wi.mu.Lock() 121 defer wi.mu.Unlock() 122 if wi.state == watchInfoStateCanceled { 123 return 124 } 125 wi.expiryTimer.Stop() 126 wi.state = watchInfoStateCanceled 127 } 128 129 func (pb *Pubsub) watch(wi *watchInfo) (first bool, cancel func() bool) { 130 pb.mu.Lock() 131 defer pb.mu.Unlock() 132 pb.logger.Debugf("new watch for type %v, resource name %v", wi.rType, wi.target) 133 var ( 134 watchers map[string]map[*watchInfo]bool 135 mds map[string]xdsresource.UpdateMetadata 136 ) 137 switch wi.rType { 138 case xdsresource.ListenerResource: 139 watchers = pb.ldsWatchers 140 mds = pb.ldsMD 141 case xdsresource.RouteConfigResource: 142 watchers = pb.rdsWatchers 143 mds = pb.rdsMD 144 case xdsresource.ClusterResource: 145 watchers = pb.cdsWatchers 146 mds = pb.cdsMD 147 case xdsresource.EndpointsResource: 148 watchers = pb.edsWatchers 149 mds = pb.edsMD 150 default: 151 pb.logger.Errorf("unknown watch type: %v", wi.rType) 152 return false, nil 153 } 154 155 var firstWatcher bool 156 resourceName := wi.target 157 s, ok := watchers[wi.target] 158 if !ok { 159 // If this is a new watcher, will ask lower level to send a new request 160 // with the resource name. 161 // 162 // If this (type+name) is already being watched, will not notify the 163 // underlying versioned apiClient. 164 pb.logger.Debugf("first watch for type %v, resource name %v, will send a new xDS request", wi.rType, wi.target) 165 s = make(map[*watchInfo]bool) 166 watchers[resourceName] = s 167 mds[resourceName] = xdsresource.UpdateMetadata{Status: xdsresource.ServiceStatusRequested} 168 firstWatcher = true 169 } 170 // No matter what, add the new watcher to the set, so it's callback will be 171 // call for new responses. 172 s[wi] = true 173 174 // If the resource is in cache, call the callback with the value. 175 switch wi.rType { 176 case xdsresource.ListenerResource: 177 if v, ok := pb.ldsCache[resourceName]; ok { 178 pb.logger.Debugf("LDS resource with name %v found in cache: %+v", wi.target, pretty.ToJSON(v)) 179 wi.newUpdate(v) 180 } 181 case xdsresource.RouteConfigResource: 182 if v, ok := pb.rdsCache[resourceName]; ok { 183 pb.logger.Debugf("RDS resource with name %v found in cache: %+v", wi.target, pretty.ToJSON(v)) 184 wi.newUpdate(v) 185 } 186 case xdsresource.ClusterResource: 187 if v, ok := pb.cdsCache[resourceName]; ok { 188 pb.logger.Debugf("CDS resource with name %v found in cache: %+v", wi.target, pretty.ToJSON(v)) 189 wi.newUpdate(v) 190 } 191 case xdsresource.EndpointsResource: 192 if v, ok := pb.edsCache[resourceName]; ok { 193 pb.logger.Debugf("EDS resource with name %v found in cache: %+v", wi.target, pretty.ToJSON(v)) 194 wi.newUpdate(v) 195 } 196 } 197 198 return firstWatcher, func() bool { 199 pb.logger.Debugf("watch for type %v, resource name %v canceled", wi.rType, wi.target) 200 wi.cancel() 201 pb.mu.Lock() 202 defer pb.mu.Unlock() 203 var lastWatcher bool 204 if s := watchers[resourceName]; s != nil { 205 // Remove this watcher, so it's callback will not be called in the 206 // future. 207 delete(s, wi) 208 if len(s) == 0 { 209 pb.logger.Debugf("last watch for type %v, resource name %v canceled, will send a new xDS request", wi.rType, wi.target) 210 // If this was the last watcher, also tell xdsv2Client to stop 211 // watching this resource. 212 delete(watchers, resourceName) 213 delete(mds, resourceName) 214 lastWatcher = true 215 // Remove the resource from cache. When a watch for this 216 // resource is added later, it will trigger a xDS request with 217 // resource names, and client will receive new xDS responses. 218 switch wi.rType { 219 case xdsresource.ListenerResource: 220 delete(pb.ldsCache, resourceName) 221 case xdsresource.RouteConfigResource: 222 delete(pb.rdsCache, resourceName) 223 case xdsresource.ClusterResource: 224 delete(pb.cdsCache, resourceName) 225 case xdsresource.EndpointsResource: 226 delete(pb.edsCache, resourceName) 227 } 228 } 229 } 230 return lastWatcher 231 } 232 }