cache

package
v1.9.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 15, 2021 License: MPL-2.0 Imports: 18 Imported by: 101

Documentation

Overview

Package cache provides caching features for data from a Consul server.

While this is similar in some ways to the "agent/ae" package, a key difference is that with anti-entropy, the agent is the authoritative source so it resolves differences the server may have. With caching (this package), the server is the authoritative source and we do our best to balance performance and correctness, depending on the type of data being requested.

The types of data that can be cached is configurable via the Type interface. This allows specialized behavior for certain types of data. Each type of Consul data (CA roots, leaf certs, intentions, KV, catalog, etc.) will have to be manually implemented. This usually is not much work, see the "agent/cache-types" package.

Index

Constants

View Source
const (
	CacheRefreshBackoffMin = 3               // 3 attempts before backing off
	CacheRefreshMaxWait    = 1 * time.Minute // maximum backoff wait time

	// DefaultEntryFetchRate is the default rate at which cache entries can
	// be fetch. This defaults to not being unlimited
	DefaultEntryFetchRate = rate.Inf

	// DefaultEntryFetchMaxBurst is the number of cache entry fetches that can
	// occur in a burst.
	DefaultEntryFetchMaxBurst = 2
)

Constants related to refresh backoff. We probably don't ever need to make these configurable knobs since they primarily exist to lower load.

Variables

View Source
var Counters = []prometheus.CounterDefinition{
	{
		Name: []string{"consul", "cache", "bypass"},
		Help: "Counts how many times a request bypassed the cache because no cache-key was provided.",
	},
	{
		Name: []string{"consul", "cache", "fetch_success"},
		Help: "Counts the number of successful fetches by the cache.",
	},
	{
		Name: []string{"consul", "cache", "fetch_error"},
		Help: "Counts the number of failed fetches by the cache.",
	},
	{
		Name: []string{"consul", "cache", "evict_expired"},
		Help: "Counts the number of expired entries that are evicted.",
	},
}

TODO(kit): remove the namespace from these once the metrics themselves change

View Source
var Gauges = []prometheus.GaugeDefinition{
	{
		Name: []string{"consul", "cache", "entries_count"},
		Help: "Represents the number of entries in this cache.",
	},
}

TODO(kit): remove the namespace from these once the metrics themselves change

Functions

func TestCacheGetCh

func TestCacheGetCh(t testing.T, c *Cache, typ string, r Request) <-chan interface{}

TestCacheGetCh returns a channel that returns the result of the Get call. This is useful for testing timing and concurrency with Get calls. Any error will be logged, so the result value should always be asserted.

func TestCacheGetChResult

func TestCacheGetChResult(t testing.T, ch <-chan interface{}, expected interface{})

TestCacheGetChResult tests that the result from TestCacheGetCh matches within a reasonable period of time (it expects it to be "immediate" but waits some milliseconds).

func TestCacheNotifyChResult added in v1.3.0

func TestCacheNotifyChResult(t testing.T, ch <-chan UpdateEvent, expected ...UpdateEvent)

TestCacheNotifyChResult tests that the expected updated was delivered on a Notify() chan within a reasonable period of time (it expects it to be "immediate" but waits some milliseconds). Expected may be given multiple times and if so these are all waited for and asserted to match but IN ANY ORDER to ensure we aren't timing dependent.

Types

type Cache

type Cache struct {
	// contains filtered or unexported fields
}

Cache is a agent-local cache of Consul data. Create a Cache using the New function. A zero-value Cache is not ready for usage and will result in a panic.

The types of data to be cached must be registered via RegisterType. Then, calls to Get specify the type and a Request implementation. The implementation of Request is usually done directly on the standard RPC struct in agent/structs. This API makes cache usage a mostly drop-in replacement for non-cached RPC calls.

The cache is partitioned by ACL and datacenter. This allows the cache to be safe for multi-DC queries and for queries where the data is modified due to ACLs all without the cache having to have any clever logic, at the slight expense of a less perfect cache.

The Cache exposes various metrics via go-metrics. Please view the source searching for "metrics." to see the various metrics exposed. These can be used to explore the performance of the cache.

func New

func New(options Options) *Cache

New creates a new cache with the given RPC client and reasonable defaults. Further settings can be tweaked on the returned value.

func (*Cache) Close added in v1.3.0

func (c *Cache) Close() error

Close stops any background work and frees all resources for the cache. Current Fetch requests are allowed to continue to completion and callers may still access the current cache values so coordination isn't needed with callers, however no background activity will continue. It's intended to close the cache at agent shutdown so no further requests should be made, however concurrent or in-flight ones won't break.

func (*Cache) Get

func (c *Cache) Get(ctx context.Context, t string, r Request) (interface{}, ResultMeta, error)

Get loads the data for the given type and request. If data satisfying the minimum index is present in the cache, it is returned immediately. Otherwise, this will block until the data is available or the request timeout is reached.

Multiple Get calls for the same Request (matching CacheKey value) will block on a single network request.

The timeout specified by the Request will be the timeout on the cache Get, and does not correspond to the timeout of any background data fetching. If the timeout is reached before data satisfying the minimum index is retrieved, the last known value (maybe nil) is returned. No error is returned on timeout. This matches the behavior of Consul blocking queries.

func (*Cache) Notify added in v1.3.0

func (c *Cache) Notify(
	ctx context.Context,
	t string,
	r Request,
	correlationID string,
	ch chan<- UpdateEvent,
) error

Notify registers a desire to be updated about changes to a cache result.

It is a helper that abstracts code from performing their own "blocking" query logic against a cache key to watch for changes and to maintain the key in cache actively. It will continue to perform blocking Get requests until the context is canceled.

The passed context must be canceled or timeout in order to free resources and stop maintaining the value in cache. Typically request-scoped resources do this but if a long-lived context like context.Background is used, then the caller must arrange for it to be canceled when the watch is no longer needed.

The passed chan may be buffered or unbuffered, if the caller doesn't consume fast enough it will block the notification loop. When the chan is later drained, watching resumes correctly. If the pause is longer than the cachetype's TTL, the result might be removed from the local cache. Even in this case though when the chan is drained again, the new Get will re-fetch the entry from servers and resume notification behavior transparently.

The chan is passed in to allow multiple cached results to be watched by a single consumer without juggling extra goroutines per watch. The correlationID is opaque and will be returned in all UpdateEvents generated by result of watching the specified request so the caller can set this to any value that allows them to disambiguate between events in the returned chan when sharing a chan between multiple cache entries. If the chan is closed, the notify loop will terminate.

func (*Cache) Prepopulate added in v1.5.2

func (c *Cache) Prepopulate(t string, res FetchResult, dc, token, k string) error

Prepopulate puts something in the cache manually. This is useful when the correct initial value is know and the cache shouldn't refetch the same thing on startup. It is used to set the ConnectRootCA and AgentLeafCert when AutoEncrypt.TLS is turned on. The cache itself cannot fetch that the first time because it requires a special RPCType. Subsequent runs are fine though.

func (*Cache) RegisterType

func (c *Cache) RegisterType(n string, typ Type)

RegisterType registers a cacheable type.

This makes the type available for Get but does not automatically perform any prefetching. In order to populate the cache, Get must be called.

func (*Cache) ReloadOptions added in v1.8.4

func (c *Cache) ReloadOptions(options Options) bool

ReloadOptions updates the cache with the new options return true if Cache is updated, false if already up to date

type FetchOptions

type FetchOptions struct {
	// MinIndex is the minimum index to be used for blocking queries.
	// If blocking queries aren't supported for data being returned,
	// this value can be ignored.
	MinIndex uint64

	// Timeout is the maximum time for the query. This must be implemented
	// in the Fetch itself.
	Timeout time.Duration

	// LastResult is the result from the last successful Fetch and represents the
	// value currently stored in the cache at the time Fetch is invoked. It will
	// be nil on first call where there is no current cache value. There may have
	// been other Fetch attempts that resulted in an error in the mean time. These
	// are not explicitly represented currently. We could add that if needed this
	// was just simpler for now.
	//
	// The FetchResult read-only! It is constructed per Fetch call so modifying
	// the struct directly (e.g. changing it's Index of Value field) will have no
	// effect, however the Value and State fields may be pointers to the actual
	// values stored in the cache entry. It is thread-unsafe to modify the Value
	// or State via pointers since readers may be concurrently inspecting those
	// values under the entry lock (although we guarantee only one Fetch call per
	// entry) and modifying them even if the index doesn't change or the Fetch
	// eventually errors will likely break logical invariants in the cache too!
	LastResult *FetchResult
}

FetchOptions are various settable options when a Fetch is called.

type FetchResult

type FetchResult struct {
	// Value is the result of the fetch.
	Value interface{}

	// State is opaque data stored in the cache but not returned to clients. It
	// can be used by Types to maintain any bookkeeping they need between fetches
	// (using FetchOptions.LastResult) in a way that gets automatically cleaned up
	// by TTL expiry etc.
	State interface{}

	// Index is the corresponding index value for this data.
	Index uint64

	// NotModified indicates that the Value has not changed since LastResult, and
	// the LastResult value should be used instead of Value.
	NotModified bool
}

FetchResult is the result of a Type Fetch operation and contains the data along with metadata gathered from that operation.

type MockRequest

type MockRequest struct {
	mock.Mock
}

MockRequest is an autogenerated mock type for the Request type

func TestRequest

func TestRequest(t testing.T, info RequestInfo) *MockRequest

TestRequest returns a Request that returns the given cache key and index. The Reset method can be called to reset it for custom usage.

func (*MockRequest) CacheInfo

func (_m *MockRequest) CacheInfo() RequestInfo

CacheInfo provides a mock function with given fields:

func (*MockRequest) Reset

func (m *MockRequest) Reset()

type MockType

type MockType struct {
	mock.Mock
}

MockType is an autogenerated mock type for the Type type

func TestType

func TestType(t testing.T) *MockType

TestType returns a MockType that sets default RegisterOptions.

func TestTypeNonBlocking added in v1.3.0

func TestTypeNonBlocking(t testing.T) *MockType

TestTypeNonBlocking returns a MockType that returns false to SupportsBlocking.

func (*MockType) Fetch

func (_m *MockType) Fetch(_a0 FetchOptions, _a1 Request) (FetchResult, error)

Fetch provides a mock function with given fields: _a0, _a1

func (*MockType) RegisterOptions added in v1.8.0

func (_m *MockType) RegisterOptions() RegisterOptions

RegisterOptions provides a mock function with given fields:

func (*MockType) Static

func (m *MockType) Static(r FetchResult, err error) *mock.Call

Static sets a static value to return for a call to Fetch.

type Options

type Options struct {
	Logger hclog.Logger

	// EntryFetchMaxBurst max burst size of RateLimit for a single cache entry
	EntryFetchMaxBurst int
	// EntryFetchRate represents the max calls/sec for a single cache entry
	EntryFetchRate rate.Limit
}

Options are options for the Cache.

func (Options) Equal added in v1.8.4

func (o Options) Equal(other Options) bool

Equal return true if both options are equivalent

type RegisterOptions

type RegisterOptions struct {
	// LastGetTTL is the time that the values returned by this type remain
	// in the cache after the last get operation. If a value isn't accessed
	// within this duration, the value is purged from the cache and
	// background refreshing will cease.
	LastGetTTL time.Duration

	// Refresh configures whether the data is actively refreshed or if
	// the data is only refreshed on an explicit Get. The default (false)
	// is to only request data on explicit Get.
	Refresh bool

	// SupportsBlocking should be set to true if the type supports blocking queries.
	// Types that do not support blocking queries will not be able to use
	// background refresh nor will the cache attempt blocking fetches if the
	// client requests them with MinIndex.
	SupportsBlocking bool

	// RefreshTimer is the time to sleep between attempts to refresh data.
	// If this is zero, then data is refreshed immediately when a fetch
	// is returned.
	//
	// Using different values for RefreshTimer and QueryTimeout, various
	// "refresh" mechanisms can be implemented:
	//
	//   * With a high timer duration and a low timeout, a timer-based
	//     refresh can be set that minimizes load on the Consul servers.
	//
	//   * With a low timer and high timeout duration, a blocking-query-based
	//     refresh can be set so that changes in server data are recognized
	//     within the cache very quickly.
	//
	RefreshTimer time.Duration

	// QueryTimeout is the default value for the maximum query time for a fetch
	// operation. It is set as FetchOptions.Timeout so that cache.Type
	// implementations can use it as the MaxQueryTime.
	QueryTimeout time.Duration
}

RegisterOptions are options that can be associated with a type being registered for the cache. This changes the behavior of the cache for this type.

type Request

type Request interface {
	// CacheInfo returns information used for caching this request.
	CacheInfo() RequestInfo
}

Request is a cacheable request.

This interface is typically implemented by request structures in the agent/structs package.

type RequestInfo

type RequestInfo struct {
	// Key is a unique cache key for this request. This key should
	// be globally unique to identify this request, since any conflicting
	// cache keys could result in invalid data being returned from the cache.
	// The Key does not need to include ACL or DC information, since the
	// cache already partitions by these values prior to using this key.
	Key string

	// Token is the ACL token associated with this request.
	//
	// Datacenter is the datacenter that the request is targeting.
	//
	// Both of these values are used to partition the cache. The cache framework
	// today partitions data on these values to simplify behavior: by
	// partitioning ACL tokens, the cache doesn't need to be smart about
	// filtering results. By filtering datacenter results, the cache can
	// service the multi-DC nature of Consul. This comes at the expense of
	// working set size, but in general the effect is minimal.
	Token      string
	Datacenter string

	// MinIndex is the minimum index being queried. This is used to
	// determine if we already have data satisfying the query or if we need
	// to block until new data is available. If no index is available, the
	// default value (zero) is acceptable.
	MinIndex uint64

	// Timeout is the timeout for waiting on a blocking query. When the
	// timeout is reached, the last known value is returned (or maybe nil
	// if there was no prior value). This "last known value" behavior matches
	// normal Consul blocking queries.
	Timeout time.Duration

	// MaxAge if set limits how stale a cache entry can be. If it is non-zero and
	// there is an entry in cache that is older than specified, it is treated as a
	// cache miss and re-fetched. It is ignored for cachetypes with Refresh =
	// true.
	MaxAge time.Duration

	// MustRevalidate forces a new lookup of the cache even if there is an
	// existing one that has not expired. It is implied by HTTP requests with
	// `Cache-Control: max-age=0` but we can't distinguish that case from the
	// unset case for MaxAge. Later we may support revalidating the index without
	// a full re-fetch but for now the only option is to refetch. It is ignored
	// for cachetypes with Refresh = true.
	MustRevalidate bool
}

RequestInfo represents cache information for a request. The caching framework uses this to control the behavior of caching and to determine cacheability.

type ResultMeta

type ResultMeta struct {
	// Hit indicates whether or not the request was a cache hit
	Hit bool

	// Age identifies how "stale" the result is. It's semantics differ based on
	// whether or not the cache type performs background refresh or not as defined
	// in https://www.consul.io/api/index.html#agent-caching.
	//
	// For background refresh types, Age is 0 unless the background blocking query
	// is currently in a failed state and so not keeping up with the server's
	// values. If it is non-zero it represents the time since the first failure to
	// connect during background refresh, and is reset after a background request
	// does manage to reconnect and either return successfully, or block for at
	// least the yamux keepalive timeout of 30 seconds (which indicates the
	// connection is OK but blocked as expected).
	//
	// For simple cache types, Age is the time since the result being returned was
	// fetched from the servers.
	Age time.Duration

	// Index is the internal ModifyIndex for the cache entry. Not all types
	// support blocking and all that do will likely have this in their result type
	// already but this allows generic code to reason about whether cache values
	// have changed.
	Index uint64
}

ResultMeta is returned from Get calls along with the value and can be used to expose information about the cache status for debugging or testing.

type Type

type Type interface {
	// Fetch fetches a single unique item.
	//
	// The FetchOptions contain the index and timeouts for blocking queries. The
	// MinIndex value on the Request itself should NOT be used as the blocking
	// index since a request may be reused multiple times as part of Refresh
	// behavior.
	//
	// The return value is a FetchResult which contains information about the
	// fetch. If an error is given, the FetchResult is ignored. The cache does not
	// support backends that return partial values. Optional State can be added to
	// the FetchResult which will be stored with the cache entry and provided to
	// the next Fetch call but will not be returned to clients. This allows types
	// to add additional bookkeeping data per cache entry that will still be aged
	// out along with the entry's TTL.
	//
	// On timeout, FetchResult can behave one of two ways. First, it can return
	// the last known value. This is the default behavior of blocking RPC calls in
	// Consul so this allows cache types to be implemented with no extra logic.
	// Second, FetchResult can return an unset value and index. In this case, the
	// cache will reuse the last value automatically. If an unset Value is
	// returned, the State field will still be updated which allows maintaining
	// metadata even when there is no result.
	Fetch(FetchOptions, Request) (FetchResult, error)

	// RegisterOptions are used when the type is registered to configure the
	// behaviour of cache entries for this type.
	RegisterOptions() RegisterOptions
}

Type implements the logic to fetch certain types of data.

type UpdateEvent added in v1.3.0

type UpdateEvent struct {
	// CorrelationID is used by the Notify API to allow correlation of updates
	// with specific requests. We could return the full request object and
	// cachetype for consumers to match against the calls they made but in
	// practice it's cleaner for them to choose the minimal necessary unique
	// identifier given the set of things they are watching. They might even
	// choose to assign random IDs for example.
	CorrelationID string
	Result        interface{}
	Meta          ResultMeta
	Err           error
}

UpdateEvent is a struct summarizing an update to a cache entry

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL