github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/go-control-plane/pkg/cache/v3/cache.go (about) 1 // Copyright 2018 Envoyproxy Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package cache defines a configuration cache for the server. 16 package cache 17 18 import ( 19 "context" 20 "errors" 21 "fmt" 22 "sync/atomic" 23 24 discovery "github.com/hxx258456/ccgo/go-control-plane/envoy/service/discovery/v3" 25 26 "github.com/golang/protobuf/proto" 27 28 "github.com/golang/protobuf/ptypes" 29 "github.com/golang/protobuf/ptypes/any" 30 31 "github.com/hxx258456/ccgo/go-control-plane/pkg/cache/types" 32 "github.com/hxx258456/ccgo/go-control-plane/pkg/server/stream/v3" 33 ) 34 35 // Request is an alias for the discovery request type. 36 type Request = discovery.DiscoveryRequest 37 38 // DeltaRequest is an alias for the delta discovery request type. 39 type DeltaRequest = discovery.DeltaDiscoveryRequest 40 41 // ConfigWatcher requests watches for configuration resources by a node, last 42 // applied version identifier, and resource names hint. The watch should send 43 // the responses when they are ready. The watch can be canceled by the 44 // consumer, in effect terminating the watch for the request. 45 // ConfigWatcher implementation must be thread-safe. 46 type ConfigWatcher interface { 47 // CreateWatch returns a new open watch from a non-empty request. 48 // An individual consumer normally issues a single open watch by each type URL. 49 // 50 // The provided channel produces requested resources as responses, once they are available. 51 // 52 // Cancel is an optional function to release resources in the producer. If 53 // provided, the consumer may call this function multiple times. 54 CreateWatch(*Request, chan Response) (cancel func()) 55 56 // CreateDeltaWatch returns a new open incremental xDS watch. 57 // 58 // The provided channel produces requested resources as responses, or spontaneous updates in accordance 59 // with the incremental xDS specification. 60 // 61 // Cancel is an optional function to release resources in the producer. If 62 // provided, the consumer may call this function multiple times. 63 CreateDeltaWatch(*DeltaRequest, stream.StreamState, chan DeltaResponse) (cancel func()) 64 } 65 66 // ConfigFetcher fetches configuration resources from cache 67 type ConfigFetcher interface { 68 // Fetch implements the polling method of the config cache using a non-empty request. 69 Fetch(context.Context, *Request) (Response, error) 70 } 71 72 // Cache is a generic config cache with a watcher. 73 type Cache interface { 74 ConfigWatcher 75 ConfigFetcher 76 } 77 78 // Response is a wrapper around Envoy's DiscoveryResponse. 79 type Response interface { 80 // Get the Constructed DiscoveryResponse 81 GetDiscoveryResponse() (*discovery.DiscoveryResponse, error) 82 83 // Get the original Request for the Response. 84 GetRequest() *discovery.DiscoveryRequest 85 86 // Get the version in the Response. 87 GetVersion() (string, error) 88 89 // Get the context provided during response creation. 90 GetContext() context.Context 91 } 92 93 // DeltaResponse is a wrapper around Envoy's DeltaDiscoveryResponse 94 type DeltaResponse interface { 95 // Get the constructed DeltaDiscoveryResponse 96 GetDeltaDiscoveryResponse() (*discovery.DeltaDiscoveryResponse, error) 97 98 // Get the request that created the watch that we're now responding to. This is provided to allow the caller to correlate the 99 // response with a request. Generally this will be the latest request seen on the stream for the specific type. 100 GetDeltaRequest() *discovery.DeltaDiscoveryRequest 101 102 // Get the version in the DeltaResponse. This field is generally used for debugging purposes as noted by the Envoy documentation. 103 GetSystemVersion() (string, error) 104 105 // Get the version map of the internal cache. 106 // The version map consists of updated version mappings after this response is applied 107 GetNextVersionMap() map[string]string 108 109 // Get the context provided during response creation 110 GetContext() context.Context 111 } 112 113 // RawResponse is a pre-serialized xDS response containing the raw resources to 114 // be included in the final Discovery Response. 115 type RawResponse struct { 116 // Request is the original request. 117 Request *discovery.DiscoveryRequest 118 119 // Version of the resources as tracked by the cache for the given type. 120 // Proxy responds with this version as an acknowledgement. 121 Version string 122 123 // Resources to be included in the response. 124 Resources []types.ResourceWithTTL 125 126 // Whether this is a heartbeat response. For xDS versions that support TTL, this 127 // will be converted into a response that doesn't contain the actual resource protobuf. 128 // This allows for more lightweight updates that server only to update the TTL timer. 129 Heartbeat bool 130 131 // Context provided at the time of response creation. This allows associating additional 132 // information with a generated response. 133 Ctx context.Context 134 135 // marshaledResponse holds an atomic reference to the serialized discovery response. 136 marshaledResponse atomic.Value 137 } 138 139 // RawDeltaResponse is a pre-serialized xDS response that utilizes the delta discovery request/response objects. 140 type RawDeltaResponse struct { 141 // Request is the latest delta request on the stream. 142 DeltaRequest *discovery.DeltaDiscoveryRequest 143 144 // SystemVersionInfo holds the currently applied response system version and should be used for debugging purposes only. 145 SystemVersionInfo string 146 147 // Resources to be included in the response. 148 Resources []types.Resource 149 150 // RemovedResources is a list of resource aliases which should be dropped by the consuming client. 151 RemovedResources []string 152 153 // NextVersionMap consists of updated version mappings after this response is applied 154 NextVersionMap map[string]string 155 156 // Context provided at the time of response creation. This allows associating additional 157 // information with a generated response. 158 Ctx context.Context 159 160 // Marshaled Resources to be included in the response. 161 marshaledResponse atomic.Value 162 } 163 164 var _ Response = &RawResponse{} 165 var _ DeltaResponse = &RawDeltaResponse{} 166 167 // PassthroughResponse is a pre constructed xDS response that need not go through marshaling transformations. 168 type PassthroughResponse struct { 169 // Request is the original request. 170 Request *discovery.DiscoveryRequest 171 172 // The discovery response that needs to be sent as is, without any marshaling transformations. 173 DiscoveryResponse *discovery.DiscoveryResponse 174 175 ctx context.Context 176 } 177 178 // DeltaPassthroughResponse is a pre constructed xDS response that need not go through marshaling transformations. 179 type DeltaPassthroughResponse struct { 180 // Request is the latest delta request on the stream 181 DeltaRequest *discovery.DeltaDiscoveryRequest 182 183 // NextVersionMap consists of updated version mappings after this response is applied 184 NextVersionMap map[string]string 185 186 // This discovery response that needs to be sent as is, without any marshaling transformations 187 DeltaDiscoveryResponse *discovery.DeltaDiscoveryResponse 188 189 ctx context.Context 190 } 191 192 var _ Response = &PassthroughResponse{} 193 var _ DeltaResponse = &DeltaPassthroughResponse{} 194 195 // GetDiscoveryResponse performs the marshaling the first time its called and uses the cached response subsequently. 196 // This is necessary because the marshaled response does not change across the calls. 197 // This caching behavior is important in high throughput scenarios because grpc marshaling has a cost and it drives the cpu utilization under load. 198 func (r *RawResponse) GetDiscoveryResponse() (*discovery.DiscoveryResponse, error) { 199 200 marshaledResponse := r.marshaledResponse.Load() 201 202 if marshaledResponse == nil { 203 204 marshaledResources := make([]*any.Any, len(r.Resources)) 205 206 for i, resource := range r.Resources { 207 maybeTtldResource, resourceType, err := r.maybeCreateTTLResource(resource) 208 if err != nil { 209 return nil, err 210 } 211 marshaledResource, err := MarshalResource(maybeTtldResource) 212 if err != nil { 213 return nil, err 214 } 215 marshaledResources[i] = &any.Any{ 216 TypeUrl: resourceType, 217 Value: marshaledResource, 218 } 219 } 220 221 marshaledResponse = &discovery.DiscoveryResponse{ 222 VersionInfo: r.Version, 223 Resources: marshaledResources, 224 TypeUrl: r.Request.TypeUrl, 225 } 226 227 r.marshaledResponse.Store(marshaledResponse) 228 } 229 230 return marshaledResponse.(*discovery.DiscoveryResponse), nil 231 } 232 233 // GetDeltaDiscoveryResponse performs the marshaling the first time its called and uses the cached response subsequently. 234 // We can do this because the marshaled response does not change across the calls. 235 // This caching behavior is important in high throughput scenarios because grpc marshaling has a cost and it drives the cpu utilization under load. 236 func (r *RawDeltaResponse) GetDeltaDiscoveryResponse() (*discovery.DeltaDiscoveryResponse, error) { 237 marshaledResponse := r.marshaledResponse.Load() 238 239 if marshaledResponse == nil { 240 marshaledResources := make([]*discovery.Resource, len(r.Resources)) 241 242 for i, resource := range r.Resources { 243 name := GetResourceName(resource) 244 marshaledResource, err := MarshalResource(resource) 245 if err != nil { 246 return nil, err 247 } 248 version := HashResource(marshaledResource) 249 if version == "" { 250 return nil, errors.New("failed to create a resource hash") 251 } 252 marshaledResources[i] = &discovery.Resource{ 253 Name: name, 254 Resource: &any.Any{ 255 TypeUrl: r.DeltaRequest.TypeUrl, 256 Value: marshaledResource, 257 }, 258 Version: version, 259 } 260 } 261 262 marshaledResponse = &discovery.DeltaDiscoveryResponse{ 263 Resources: marshaledResources, 264 RemovedResources: r.RemovedResources, 265 TypeUrl: r.DeltaRequest.TypeUrl, 266 SystemVersionInfo: r.SystemVersionInfo, 267 } 268 r.marshaledResponse.Store(marshaledResponse) 269 } 270 271 return marshaledResponse.(*discovery.DeltaDiscoveryResponse), nil 272 } 273 274 // GetRequest returns the original Discovery Request. 275 func (r *RawResponse) GetRequest() *discovery.DiscoveryRequest { 276 return r.Request 277 } 278 279 func (r *RawResponse) GetContext() context.Context { 280 return r.Ctx 281 } 282 283 // GetDeltaRequest returns the original DeltaRequest 284 func (r *RawDeltaResponse) GetDeltaRequest() *discovery.DeltaDiscoveryRequest { 285 return r.DeltaRequest 286 } 287 288 // GetVersion returns the response version. 289 func (r *RawResponse) GetVersion() (string, error) { 290 return r.Version, nil 291 } 292 293 // GetSystemVersion returns the raw SystemVersion 294 func (r *RawDeltaResponse) GetSystemVersion() (string, error) { 295 return r.SystemVersionInfo, nil 296 } 297 298 // NextVersionMap returns the version map which consists of updated version mappings after this response is applied 299 func (r *RawDeltaResponse) GetNextVersionMap() map[string]string { 300 return r.NextVersionMap 301 } 302 303 func (r *RawDeltaResponse) GetContext() context.Context { 304 return r.Ctx 305 } 306 307 var deltaResourceTypeURL = "type.googleapis.com/" + proto.MessageName(&discovery.Resource{}) 308 309 func (r *RawResponse) maybeCreateTTLResource(resource types.ResourceWithTTL) (types.Resource, string, error) { 310 if resource.TTL != nil { 311 wrappedResource := &discovery.Resource{ 312 Name: GetResourceName(resource.Resource), 313 Ttl: ptypes.DurationProto(*resource.TTL), 314 } 315 316 if !r.Heartbeat { 317 any, err := ptypes.MarshalAny(resource.Resource) 318 if err != nil { 319 return nil, "", err 320 } 321 any.TypeUrl = r.Request.TypeUrl 322 wrappedResource.Resource = any 323 } 324 325 return wrappedResource, deltaResourceTypeURL, nil 326 } 327 328 return resource.Resource, r.Request.TypeUrl, nil 329 } 330 331 // GetDiscoveryResponse returns the final passthrough Discovery Response. 332 func (r *PassthroughResponse) GetDiscoveryResponse() (*discovery.DiscoveryResponse, error) { 333 return r.DiscoveryResponse, nil 334 } 335 336 // GetDeltaDiscoveryResponse returns the final passthrough Delta Discovery Response. 337 func (r *DeltaPassthroughResponse) GetDeltaDiscoveryResponse() (*discovery.DeltaDiscoveryResponse, error) { 338 return r.DeltaDiscoveryResponse, nil 339 } 340 341 // GetRequest returns the original Discovery Request 342 func (r *PassthroughResponse) GetRequest() *discovery.DiscoveryRequest { 343 return r.Request 344 } 345 346 // GetDeltaRequest returns the original Delta Discovery Request 347 func (r *DeltaPassthroughResponse) GetDeltaRequest() *discovery.DeltaDiscoveryRequest { 348 return r.DeltaRequest 349 } 350 351 // GetVersion returns the response version. 352 func (r *PassthroughResponse) GetVersion() (string, error) { 353 if r.DiscoveryResponse != nil { 354 return r.DiscoveryResponse.VersionInfo, nil 355 } 356 return "", fmt.Errorf("DiscoveryResponse is nil") 357 } 358 func (r *PassthroughResponse) GetContext() context.Context { 359 return r.ctx 360 } 361 362 // GetSystemVersion returns the response version. 363 func (r *DeltaPassthroughResponse) GetSystemVersion() (string, error) { 364 if r.DeltaDiscoveryResponse != nil { 365 return r.DeltaDiscoveryResponse.SystemVersionInfo, nil 366 } 367 return "", fmt.Errorf("DeltaDiscoveryResponse is nil") 368 } 369 370 // NextVersionMap returns the version map from a DeltaPassthroughResponse 371 func (r *DeltaPassthroughResponse) GetNextVersionMap() map[string]string { 372 return r.NextVersionMap 373 } 374 375 func (r *DeltaPassthroughResponse) GetContext() context.Context { 376 return r.ctx 377 }