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  }