agent: check cache hit count to verify CA root caching, background update

pull/4275/head
Mitchell Hashimoto 7 years ago
parent 6902d721d6
commit 917a9e63d5
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A

@ -2121,32 +2121,77 @@ func TestAgentConnectCARoots_empty(t *testing.T) {
func TestAgentConnectCARoots_list(t *testing.T) {
t.Parallel()
assert := assert.New(t)
require := require.New(t)
a := NewTestAgent(t.Name(), "")
defer a.Shutdown()
// Grab the initial cache hit count
cacheHits := a.cache.Hits()
// Set some CAs
var reply interface{}
ca1 := connect.TestCA(t, nil)
ca1.Active = false
ca2 := connect.TestCA(t, nil)
assert.Nil(a.RPC("Test.ConnectCASetRoots",
require.Nil(a.RPC("Test.ConnectCASetRoots",
[]*structs.CARoot{ca1, ca2}, &reply))
// List
req, _ := http.NewRequest("GET", "/v1/agent/connect/ca/roots", nil)
resp := httptest.NewRecorder()
obj, err := a.srv.AgentConnectCARoots(resp, req)
assert.Nil(err)
require.Nil(err)
value := obj.(structs.IndexedCARoots)
assert.Equal(value.ActiveRootID, ca2.ID)
assert.Len(value.Roots, 2)
require.Equal(value.ActiveRootID, ca2.ID)
require.Len(value.Roots, 2)
// We should never have the secret information
for _, r := range value.Roots {
assert.Equal("", r.SigningCert)
assert.Equal("", r.SigningKey)
require.Equal("", r.SigningCert)
require.Equal("", r.SigningKey)
}
// That should've been a cache miss, so not hit change
require.Equal(cacheHits, a.cache.Hits())
// Test caching
{
// List it again
obj2, err := a.srv.AgentConnectCARoots(httptest.NewRecorder(), req)
require.Nil(err)
require.Equal(obj, obj2)
// Should cache hit this time and not make request
require.Equal(cacheHits+1, a.cache.Hits())
cacheHits++
}
// Test that caching is updated in the background
{
// Set some new CAs
var reply interface{}
ca := connect.TestCA(t, nil)
require.Nil(a.RPC("Test.ConnectCASetRoots",
[]*structs.CARoot{ca}, &reply))
// Sleep a bit to wait for the cache to update
time.Sleep(100 * time.Millisecond)
// List it again
obj, err := a.srv.AgentConnectCARoots(httptest.NewRecorder(), req)
require.Nil(err)
require.Equal(obj, obj)
value := obj.(structs.IndexedCARoots)
require.Equal(value.ActiveRootID, ca.ID)
require.Len(value.Roots, 1)
// Should be a cache hit! The data should've updated in the cache
// in the background so this should've been fetched directly from
// the cache.
require.Equal(cacheHits+1, a.cache.Hits())
cacheHits++
}
}

@ -15,6 +15,7 @@ package cache
import (
"fmt"
"sync"
"sync/atomic"
"time"
)
@ -22,6 +23,11 @@ import (
// Cache is a agent-local cache of Consul data.
type Cache struct {
// Keeps track of the cache hits and misses in total. This is used by
// tests currently to verify cache behavior and is not meant for general
// analytics; for that, go-metrics emitted values are better.
hits, misses uint64
// types stores the list of data types that the cache knows how to service.
// These can be dynamically registered with RegisterType.
typesLock sync.RWMutex
@ -127,6 +133,9 @@ func (c *Cache) Get(t string, r Request) (interface{}, error) {
// Get the actual key for our entry
key := c.entryKey(&info)
// First time through
first := true
RETRY_GET:
// Get the current value
c.entriesLock.RLock()
@ -139,10 +148,22 @@ RETRY_GET:
// we have.
if ok && entry.Valid {
if info.MinIndex == 0 || info.MinIndex < entry.Index {
if first {
atomic.AddUint64(&c.hits, 1)
}
return entry.Value, nil
}
}
if first {
// Record the miss if its our first time through
atomic.AddUint64(&c.misses, 1)
}
// No longer our first time through
first = false
// At this point, we know we either don't have a value at all or the
// value we have is too old. We need to wait for new data.
waiter, err := c.fetch(t, key, r)
@ -263,3 +284,8 @@ func (c *Cache) refresh(opts *RegisterOptions, t string, key string, r Request)
// Trigger
c.fetch(t, key, r)
}
// Returns the number of cache hits. Safe to call concurrently.
func (c *Cache) Hits() uint64 {
return atomic.LoadUint64(&c.hits)
}

Loading…
Cancel
Save