Matt Keeler 326c0ecfbe
In-Memory gRPC (#19942)
* Implement In-Process gRPC for use by controller caching/indexing

This replaces the pipe base listener implementation we were previously using. The new style CAN avoid cloning resources which our controller caching/indexing is taking advantage of to not duplicate resource objects in memory.

To maintain safety for controllers and for them to be able to modify data they get back from the cache and the resource service, the client they are presented in their runtime will be wrapped with an autogenerated client which clones request and response messages as they pass through the client.

Another sizable change in this PR is to consolidate how server specific gRPC services get registered and managed. Before this was in a bunch of different methods and it was difficult to track down how gRPC services were registered. Now its all in one place.

* Fix race in tests

* Ensure the resource service is registered to the multiplexed handler for forwarding from client agents

* Expose peer streaming on the internal handler
2024-01-12 11:54:07 -05:00

94 lines
2.8 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package cache
import (
"github.com/hashicorp/consul/internal/protoutil"
"github.com/hashicorp/consul/proto-public/pbresource"
)
// NewCloningReadOnlyCache will wrap the provided ReadOnlyCache with
// an implementation which clones resources that are read out of the cache.
//
// In order to protect the Cache (and the resource service it shares resources with)
// from accidental user modification, the ReadOnlyCache interface is wrapped with
// the cloning variant. This wrapped/cloning cache is then what is given to
// dependency mappers and controller reconcilers. This allows those methods to
// freely modify data they get back from the cache without having to worry about
// accidental data modifications that could wreck the consistent state storage.
func NewCloningReadOnlyCache(cache ReadOnlyCache) ReadOnlyCache {
return cloningReadOnlyCache{ReadOnlyCache: cache}
}
type cloningReadOnlyCache struct {
ReadOnlyCache
}
func (c cloningReadOnlyCache) Get(it *pbresource.Type, indexName string, args ...any) (*pbresource.Resource, error) {
res, err := c.ReadOnlyCache.Get(it, indexName, args...)
if err != nil {
return nil, err
}
return protoutil.Clone(res), nil
}
func (c cloningReadOnlyCache) List(it *pbresource.Type, indexName string, args ...any) ([]*pbresource.Resource, error) {
resources, err := c.ReadOnlyCache.List(it, indexName, args...)
if err != nil {
return nil, err
}
return protoutil.CloneSlice(resources), nil
}
func (c cloningReadOnlyCache) ListIterator(it *pbresource.Type, indexName string, args ...any) (ResourceIterator, error) {
rit, err := c.ReadOnlyCache.ListIterator(it, indexName, args...)
if err != nil {
return nil, err
}
return cloningIterator{ResourceIterator: rit}, nil
}
func (c cloningReadOnlyCache) Parents(it *pbresource.Type, indexName string, args ...any) ([]*pbresource.Resource, error) {
resources, err := c.ReadOnlyCache.Parents(it, indexName, args...)
if err != nil {
return nil, err
}
return protoutil.CloneSlice(resources), nil
}
func (c cloningReadOnlyCache) ParentsIterator(it *pbresource.Type, indexName string, args ...any) (ResourceIterator, error) {
rit, err := c.ReadOnlyCache.ParentsIterator(it, indexName, args...)
if err != nil {
return nil, err
}
return cloningIterator{ResourceIterator: rit}, nil
}
func (c cloningReadOnlyCache) Query(name string, args ...any) (ResourceIterator, error) {
rit, err := c.ReadOnlyCache.Query(name, args...)
if err != nil {
return nil, err
}
return cloningIterator{ResourceIterator: rit}, nil
}
type cloningIterator struct {
ResourceIterator
}
func (it cloningIterator) Next() *pbresource.Resource {
res := it.ResourceIterator.Next()
if res == nil {
return nil
}
return protoutil.Clone(res)
}