dubbo.apache.org/dubbo-go/v3@v3.1.1/xds/client/pubsub/update.go (about) 1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. 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 * 20 * Copyright 2021 gRPC authors. 21 * 22 */ 23 24 package pubsub 25 26 import ( 27 "google.golang.org/protobuf/proto" 28 ) 29 30 import ( 31 "dubbo.apache.org/dubbo-go/v3/xds/client/resource" 32 "dubbo.apache.org/dubbo-go/v3/xds/utils/pretty" 33 ) 34 35 type watcherInfoWithUpdate struct { 36 wi *watchInfo 37 update interface{} 38 err error 39 } 40 41 // scheduleCallback should only be called by methods of watchInfo, which checks 42 // for watcher states and maintain consistency. 43 func (pb *Pubsub) scheduleCallback(wi *watchInfo, update interface{}, err error) { 44 pb.updateCh.Put(&watcherInfoWithUpdate{ 45 wi: wi, 46 update: update, 47 err: err, 48 }) 49 } 50 51 func (pb *Pubsub) callCallback(wiu *watcherInfoWithUpdate) { 52 pb.mu.Lock() 53 // Use a closure to capture the callback and type assertion, to save one 54 // more switch case. 55 // 56 // The callback must be called without pb.mu. Otherwise if the callback calls 57 // another watch() inline, it will cause a deadlock. This leaves a small 58 // window that a watcher's callback could be called after the watcher is 59 // canceled, and the user needs to take care of it. 60 var ccb func() 61 switch wiu.wi.rType { 62 case resource.ListenerResource: 63 if s, ok := pb.ldsWatchers[wiu.wi.target]; ok && s[wiu.wi] { 64 ccb = func() { wiu.wi.ldsCallback(wiu.update.(resource.ListenerUpdate), wiu.err) } 65 } 66 case resource.RouteConfigResource: 67 if s, ok := pb.rdsWatchers[wiu.wi.target]; ok && s[wiu.wi] { 68 ccb = func() { wiu.wi.rdsCallback(wiu.update.(resource.RouteConfigUpdate), wiu.err) } 69 } 70 case resource.ClusterResource: 71 if s, ok := pb.cdsWatchers["*"]; ok && s[wiu.wi] { 72 ccb = func() { wiu.wi.cdsCallback(wiu.update.(resource.ClusterUpdate), wiu.err) } 73 } 74 if s, ok := pb.cdsWatchers[wiu.wi.target]; ok && s[wiu.wi] { 75 ccb = func() { wiu.wi.cdsCallback(wiu.update.(resource.ClusterUpdate), wiu.err) } 76 } 77 case resource.EndpointsResource: 78 if s, ok := pb.edsWatchers[wiu.wi.target]; ok && s[wiu.wi] { 79 ccb = func() { wiu.wi.edsCallback(wiu.update.(resource.EndpointsUpdate), wiu.err) } 80 } 81 } 82 pb.mu.Unlock() 83 84 if ccb != nil { 85 ccb() 86 } 87 } 88 89 // NewListeners is called when there's a new LDS update. 90 func (pb *Pubsub) NewListeners(updates map[string]resource.ListenerUpdateErrTuple, metadata resource.UpdateMetadata) { 91 pb.mu.Lock() 92 defer pb.mu.Unlock() 93 94 for name, uErr := range updates { 95 if s, ok := pb.ldsWatchers[name]; ok { 96 if uErr.Err != nil { 97 // On error, keep previous version for each resource. But update 98 // status and error. 99 mdCopy := pb.ldsMD[name] 100 mdCopy.ErrState = metadata.ErrState 101 mdCopy.Status = metadata.Status 102 pb.ldsMD[name] = mdCopy 103 for wi := range s { 104 wi.newError(uErr.Err) 105 } 106 continue 107 } 108 // If we get here, it means that the update is a valid one. Notify 109 // watchers only if this is a first time update or it is different 110 // from the one currently cached. 111 if cur, ok := pb.ldsCache[name]; !ok || !proto.Equal(cur.Raw, uErr.Update.Raw) { 112 for wi := range s { 113 wi.newUpdate(uErr.Update) 114 } 115 } 116 // Sync cache. 117 pb.logger.Debugf("LDS resource with name %v, value %+v added to cache", name, pretty.ToJSON(uErr)) 118 pb.ldsCache[name] = uErr.Update 119 // Set status to ACK, and clear error state. The metadata might be a 120 // NACK metadata because some other resources in the same response 121 // are invalid. 122 mdCopy := metadata 123 mdCopy.Status = resource.ServiceStatusACKed 124 mdCopy.ErrState = nil 125 if metadata.ErrState != nil { 126 mdCopy.Version = metadata.ErrState.Version 127 } 128 pb.ldsMD[name] = mdCopy 129 } 130 } 131 // Resources not in the new update were removed by the server, so delete 132 // them. 133 for name := range pb.ldsCache { 134 if _, ok := updates[name]; !ok { 135 // If resource exists in cache, but not in the new update, delete 136 // the resource from cache, and also send an resource not found 137 // error to indicate resource removed. 138 delete(pb.ldsCache, name) 139 pb.ldsMD[name] = resource.UpdateMetadata{Status: resource.ServiceStatusNotExist} 140 for wi := range pb.ldsWatchers[name] { 141 wi.resourceNotFound() 142 } 143 } 144 } 145 // When LDS resource is removed, we don't delete corresponding RDS cached 146 // data. The RDS watch will be canceled, and cache entry is removed when the 147 // last watch is canceled. 148 } 149 150 // NewRouteConfigs is called when there's a new RDS update. 151 func (pb *Pubsub) NewRouteConfigs(updates map[string]resource.RouteConfigUpdateErrTuple, metadata resource.UpdateMetadata) { 152 pb.mu.Lock() 153 defer pb.mu.Unlock() 154 155 // If no error received, the status is ACK. 156 for name, uErr := range updates { 157 if s, ok := pb.rdsWatchers[name]; ok { 158 if uErr.Err != nil { 159 // On error, keep previous version for each resource. But update 160 // status and error. 161 mdCopy := pb.rdsMD[name] 162 mdCopy.ErrState = metadata.ErrState 163 mdCopy.Status = metadata.Status 164 pb.rdsMD[name] = mdCopy 165 for wi := range s { 166 wi.newError(uErr.Err) 167 } 168 continue 169 } 170 // If we get here, it means that the update is a valid one. Notify 171 // watchers only if this is a first time update or it is different 172 // from the one currently cached. 173 if cur, ok := pb.rdsCache[name]; !ok || !proto.Equal(cur.Raw, uErr.Update.Raw) { 174 for wi := range s { 175 wi.newUpdate(uErr.Update) 176 } 177 } 178 // Sync cache. 179 pb.logger.Debugf("RDS resource with name %v, value %+v added to cache", name, pretty.ToJSON(uErr)) 180 pb.rdsCache[name] = uErr.Update 181 // Set status to ACK, and clear error state. The metadata might be a 182 // NACK metadata because some other resources in the same response 183 // are invalid. 184 mdCopy := metadata 185 mdCopy.Status = resource.ServiceStatusACKed 186 mdCopy.ErrState = nil 187 if metadata.ErrState != nil { 188 mdCopy.Version = metadata.ErrState.Version 189 } 190 pb.rdsMD[name] = mdCopy 191 } 192 } 193 } 194 195 // NewClusters is called when there's a new CDS update. 196 func (pb *Pubsub) NewClusters(updates map[string]resource.ClusterUpdateErrTuple, metadata resource.UpdateMetadata) { 197 pb.mu.Lock() 198 defer pb.mu.Unlock() 199 200 for k, update := range pb.cdsCache { 201 if _, ok := updates[k]; !ok { 202 // this is a delete event 203 s, ok := pb.cdsWatchers[k] 204 if !ok { 205 s, ok = pb.cdsWatchers["*"] 206 } 207 if ok { 208 for wi := range s { 209 // delete 210 update.ClusterName = "-" + update.ClusterName 211 wi.newUpdate(update) 212 } 213 } 214 } 215 } 216 217 for name, uErr := range updates { 218 s, ok := pb.cdsWatchers[name] 219 if !ok { 220 s, ok = pb.cdsWatchers["*"] 221 } 222 if ok { 223 if uErr.Err != nil { 224 // On error, keep previous version for each resource. But update 225 // status and error. 226 mdCopy := pb.cdsMD[name] 227 mdCopy.ErrState = metadata.ErrState 228 mdCopy.Status = metadata.Status 229 pb.cdsMD[name] = mdCopy 230 for wi := range s { 231 // Send the watcher the individual error, instead of the 232 // overall combined error from the metadata.ErrState. 233 wi.newError(uErr.Err) 234 } 235 continue 236 } 237 // If we get here, it means that the update is a valid one. Notify 238 // watchers only if this is a first time update or it is different 239 // from the one currently cached. 240 if cur, ok := pb.cdsCache[name]; !ok || !proto.Equal(cur.Raw, uErr.Update.Raw) { 241 for wi := range s { 242 wi.newUpdate(uErr.Update) 243 } 244 } 245 // Sync cache. 246 pb.logger.Debugf("CDS resource with name %v, value %+v added to cache", name, pretty.ToJSON(uErr)) 247 pb.cdsCache[name] = uErr.Update 248 // Set status to ACK, and clear error state. The metadata might be a 249 // NACK metadata because some other resources in the same response 250 // are invalid. 251 mdCopy := metadata 252 mdCopy.Status = resource.ServiceStatusACKed 253 mdCopy.ErrState = nil 254 if metadata.ErrState != nil { 255 mdCopy.Version = metadata.ErrState.Version 256 } 257 pb.cdsMD[name] = mdCopy 258 } 259 } 260 // Resources not in the new update were removed by the server, so delete 261 // them. 262 for name := range pb.cdsCache { 263 if _, ok := updates[name]; !ok { 264 // If resource exists in cache, but not in the new update, delete it 265 // from cache, and also send an resource not found error to indicate 266 // resource removed. 267 delete(pb.cdsCache, name) 268 pb.ldsMD[name] = resource.UpdateMetadata{Status: resource.ServiceStatusNotExist} 269 for wi := range pb.cdsWatchers[name] { 270 wi.resourceNotFound() 271 } 272 } 273 } 274 // When CDS resource is removed, we don't delete corresponding EDS cached 275 // data. The EDS watch will be canceled, and cache entry is removed when the 276 // last watch is canceled. 277 } 278 279 // NewEndpoints is called when there's anew EDS update. 280 func (pb *Pubsub) NewEndpoints(updates map[string]resource.EndpointsUpdateErrTuple, metadata resource.UpdateMetadata) { 281 pb.mu.Lock() 282 defer pb.mu.Unlock() 283 284 for name, uErr := range updates { 285 if s, ok := pb.edsWatchers[name]; ok { 286 if uErr.Err != nil { 287 // On error, keep previous version for each resource. But update 288 // status and error. 289 mdCopy := pb.edsMD[name] 290 mdCopy.ErrState = metadata.ErrState 291 mdCopy.Status = metadata.Status 292 pb.edsMD[name] = mdCopy 293 for wi := range s { 294 // Send the watcher the individual error, instead of the 295 // overall combined error from the metadata.ErrState. 296 wi.newError(uErr.Err) 297 } 298 continue 299 } 300 // If we get here, it means that the update is a valid one. Notify 301 // watchers only if this is a first time update or it is different 302 // from the one currently cached. 303 if cur, ok := pb.edsCache[name]; !ok || !proto.Equal(cur.Raw, uErr.Update.Raw) { 304 for wi := range s { 305 wi.newUpdate(uErr.Update) 306 } 307 } 308 // Sync cache. 309 pb.logger.Debugf("EDS resource with name %v, value %+v added to cache", name, pretty.ToJSON(uErr)) 310 pb.edsCache[name] = uErr.Update 311 // Set status to ACK, and clear error state. The metadata might be a 312 // NACK metadata because some other resources in the same response 313 // are invalid. 314 mdCopy := metadata 315 mdCopy.Status = resource.ServiceStatusACKed 316 mdCopy.ErrState = nil 317 if metadata.ErrState != nil { 318 mdCopy.Version = metadata.ErrState.Version 319 } 320 pb.edsMD[name] = mdCopy 321 } 322 } 323 } 324 325 // NewConnectionError is called by the underlying xdsAPIClient when it receives 326 // a connection error. The error will be forwarded to all the resource watchers. 327 func (pb *Pubsub) NewConnectionError(err error) { 328 pb.mu.Lock() 329 defer pb.mu.Unlock() 330 331 for _, s := range pb.ldsWatchers { 332 for wi := range s { 333 wi.newError(resource.NewErrorf(resource.ErrorTypeConnection, "xds: error received from xDS stream: %v", err)) 334 } 335 } 336 for _, s := range pb.rdsWatchers { 337 for wi := range s { 338 wi.newError(resource.NewErrorf(resource.ErrorTypeConnection, "xds: error received from xDS stream: %v", err)) 339 } 340 } 341 for _, s := range pb.cdsWatchers { 342 for wi := range s { 343 wi.newError(resource.NewErrorf(resource.ErrorTypeConnection, "xds: error received from xDS stream: %v", err)) 344 } 345 } 346 for _, s := range pb.edsWatchers { 347 for wi := range s { 348 wi.newError(resource.NewErrorf(resource.ErrorTypeConnection, "xds: error received from xDS stream: %v", err)) 349 } 350 } 351 }