mirror of https://github.com/status-im/consul.git
agent: check cache hit count to verify CA root caching, background update
This commit is contained in:
parent
6902d721d6
commit
917a9e63d5
|
@ -2121,32 +2121,77 @@ func TestAgentConnectCARoots_empty(t *testing.T) {
|
||||||
func TestAgentConnectCARoots_list(t *testing.T) {
|
func TestAgentConnectCARoots_list(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
assert := assert.New(t)
|
require := require.New(t)
|
||||||
a := NewTestAgent(t.Name(), "")
|
a := NewTestAgent(t.Name(), "")
|
||||||
defer a.Shutdown()
|
defer a.Shutdown()
|
||||||
|
|
||||||
|
// Grab the initial cache hit count
|
||||||
|
cacheHits := a.cache.Hits()
|
||||||
|
|
||||||
// Set some CAs
|
// Set some CAs
|
||||||
var reply interface{}
|
var reply interface{}
|
||||||
ca1 := connect.TestCA(t, nil)
|
ca1 := connect.TestCA(t, nil)
|
||||||
ca1.Active = false
|
ca1.Active = false
|
||||||
ca2 := connect.TestCA(t, nil)
|
ca2 := connect.TestCA(t, nil)
|
||||||
assert.Nil(a.RPC("Test.ConnectCASetRoots",
|
require.Nil(a.RPC("Test.ConnectCASetRoots",
|
||||||
[]*structs.CARoot{ca1, ca2}, &reply))
|
[]*structs.CARoot{ca1, ca2}, &reply))
|
||||||
|
|
||||||
// List
|
// List
|
||||||
req, _ := http.NewRequest("GET", "/v1/agent/connect/ca/roots", nil)
|
req, _ := http.NewRequest("GET", "/v1/agent/connect/ca/roots", nil)
|
||||||
resp := httptest.NewRecorder()
|
resp := httptest.NewRecorder()
|
||||||
obj, err := a.srv.AgentConnectCARoots(resp, req)
|
obj, err := a.srv.AgentConnectCARoots(resp, req)
|
||||||
assert.Nil(err)
|
require.Nil(err)
|
||||||
|
|
||||||
value := obj.(structs.IndexedCARoots)
|
value := obj.(structs.IndexedCARoots)
|
||||||
assert.Equal(value.ActiveRootID, ca2.ID)
|
require.Equal(value.ActiveRootID, ca2.ID)
|
||||||
assert.Len(value.Roots, 2)
|
require.Len(value.Roots, 2)
|
||||||
|
|
||||||
// We should never have the secret information
|
// We should never have the secret information
|
||||||
for _, r := range value.Roots {
|
for _, r := range value.Roots {
|
||||||
assert.Equal("", r.SigningCert)
|
require.Equal("", r.SigningCert)
|
||||||
assert.Equal("", r.SigningKey)
|
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,6 +23,11 @@ import (
|
||||||
|
|
||||||
// Cache is a agent-local cache of Consul data.
|
// Cache is a agent-local cache of Consul data.
|
||||||
type Cache struct {
|
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.
|
// types stores the list of data types that the cache knows how to service.
|
||||||
// These can be dynamically registered with RegisterType.
|
// These can be dynamically registered with RegisterType.
|
||||||
typesLock sync.RWMutex
|
typesLock sync.RWMutex
|
||||||
|
@ -127,6 +133,9 @@ func (c *Cache) Get(t string, r Request) (interface{}, error) {
|
||||||
// Get the actual key for our entry
|
// Get the actual key for our entry
|
||||||
key := c.entryKey(&info)
|
key := c.entryKey(&info)
|
||||||
|
|
||||||
|
// First time through
|
||||||
|
first := true
|
||||||
|
|
||||||
RETRY_GET:
|
RETRY_GET:
|
||||||
// Get the current value
|
// Get the current value
|
||||||
c.entriesLock.RLock()
|
c.entriesLock.RLock()
|
||||||
|
@ -139,10 +148,22 @@ RETRY_GET:
|
||||||
// we have.
|
// we have.
|
||||||
if ok && entry.Valid {
|
if ok && entry.Valid {
|
||||||
if info.MinIndex == 0 || info.MinIndex < entry.Index {
|
if info.MinIndex == 0 || info.MinIndex < entry.Index {
|
||||||
|
if first {
|
||||||
|
atomic.AddUint64(&c.hits, 1)
|
||||||
|
}
|
||||||
|
|
||||||
return entry.Value, nil
|
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
|
// 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.
|
// value we have is too old. We need to wait for new data.
|
||||||
waiter, err := c.fetch(t, key, r)
|
waiter, err := c.fetch(t, key, r)
|
||||||
|
@ -263,3 +284,8 @@ func (c *Cache) refresh(opts *RegisterOptions, t string, key string, r Request)
|
||||||
// Trigger
|
// Trigger
|
||||||
c.fetch(t, key, r)
|
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…
Reference in New Issue