Skip to content

Commit 865f9d3

Browse files
authored
Extract Cloud Map client cache (#67)
1 parent a74b47f commit 865f9d3

File tree

5 files changed

+457
-312
lines changed

5 files changed

+457
-312
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/confi
9595
MOCKS_DESTINATION=mocks
9696
generate-mocks: mockgen
9797
$(MOCKGEN) --source pkg/cloudmap/client.go --destination $(MOCKS_DESTINATION)/pkg/cloudmap/client_mock.go --package cloudmap
98+
$(MOCKGEN) --source pkg/cloudmap/cache.go --destination $(MOCKS_DESTINATION)/pkg/cloudmap/cache_mock.go --package cloudmap
9899
$(MOCKGEN) --source pkg/cloudmap/operation_poller.go --destination $(MOCKS_DESTINATION)/pkg/cloudmap/operation_poller_mock.go --package cloudmap
99100
$(MOCKGEN) --source pkg/cloudmap/operation_collector.go --destination $(MOCKS_DESTINATION)/pkg/cloudmap/operation_collector_mock.go --package cloudmap
100101
$(MOCKGEN) --source pkg/cloudmap/api.go --destination $(MOCKS_DESTINATION)/pkg/cloudmap/api_mock.go --package cloudmap

pkg/cloudmap/cache.go

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package cloudmap
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"github.com/aws/aws-cloud-map-mcs-controller-for-k8s/pkg/model"
7+
"github.com/go-logr/logr"
8+
"k8s.io/apimachinery/pkg/util/cache"
9+
ctrl "sigs.k8s.io/controller-runtime"
10+
"time"
11+
)
12+
13+
const (
14+
nsKeyPrefix = "ns"
15+
svcKeyPrefix = "svc"
16+
endptKeyPrefix = "endpt"
17+
18+
defaultCacheSize = 1024
19+
defaultNsTTL = 2 * time.Minute
20+
defaultSvcTTL = 2 * time.Minute
21+
defaultEndptTTL = 5 * time.Second
22+
)
23+
24+
type ServiceDiscoveryClientCache interface {
25+
GetNamespace(namespaceName string) (namespace *model.Namespace, found bool)
26+
CacheNamespace(namespace *model.Namespace)
27+
CacheNilNamespace(namespaceName string)
28+
GetServiceId(namespaceName string, serviceName string) (serviceId string, found bool)
29+
CacheServiceId(namespaceName string, serviceName string, serviceId string)
30+
GetEndpoints(serviceId string) (endpoints []*model.Endpoint, found bool)
31+
CacheEndpoints(serviceId string, endpoints []*model.Endpoint)
32+
EvictEndpoints(serviceId string)
33+
}
34+
35+
type sdCache struct {
36+
log logr.Logger
37+
cache *cache.LRUExpireCache
38+
config sdCacheConfig
39+
}
40+
41+
type sdCacheConfig struct {
42+
nsTTL time.Duration
43+
svcTTL time.Duration
44+
endptTTL time.Duration
45+
}
46+
47+
func NewDefaultServiceDiscoveryClientCache() ServiceDiscoveryClientCache {
48+
return &sdCache{
49+
log: ctrl.Log.WithName("cloudmap"),
50+
cache: cache.NewLRUExpireCache(defaultCacheSize),
51+
config: sdCacheConfig{
52+
nsTTL: defaultNsTTL,
53+
svcTTL: defaultSvcTTL,
54+
endptTTL: defaultEndptTTL,
55+
}}
56+
}
57+
58+
func (sdCache *sdCache) GetNamespace(nsName string) (ns *model.Namespace, found bool) {
59+
key := sdCache.buildNsKey(nsName)
60+
entry, exists := sdCache.cache.Get(key)
61+
if !exists {
62+
return nil, false
63+
}
64+
65+
if entry == nil {
66+
return nil, true
67+
}
68+
69+
nsEntry, ok := entry.(model.Namespace)
70+
if !ok {
71+
sdCache.log.Error(errors.New("failed to retrieve namespace from cache"), "", "nsName", nsName)
72+
sdCache.cache.Remove(key)
73+
return nil, false
74+
}
75+
76+
return &nsEntry, true
77+
}
78+
79+
func (sdCache *sdCache) CacheNamespace(namespace *model.Namespace) {
80+
key := sdCache.buildNsKey(namespace.Name)
81+
sdCache.cache.Add(key, *namespace, sdCache.config.nsTTL)
82+
}
83+
84+
func (sdCache *sdCache) CacheNilNamespace(nsName string) {
85+
key := sdCache.buildNsKey(nsName)
86+
sdCache.cache.Add(key, nil, sdCache.config.nsTTL)
87+
}
88+
89+
func (sdCache *sdCache) GetServiceId(nsName string, svcName string) (svcId string, found bool) {
90+
key := sdCache.buildSvcKey(nsName, svcName)
91+
entry, exists := sdCache.cache.Get(key)
92+
if !exists {
93+
return "", false
94+
}
95+
96+
svcId, ok := entry.(string)
97+
if !ok {
98+
sdCache.log.Error(errors.New("failed to retrieve service ID from cache"), "",
99+
"nsName", nsName, "svcName", svcName)
100+
sdCache.cache.Remove(key)
101+
return "", false
102+
}
103+
104+
return svcId, true
105+
}
106+
107+
func (sdCache *sdCache) CacheServiceId(nsName string, svcName string, svcId string) {
108+
key := sdCache.buildSvcKey(nsName, svcName)
109+
sdCache.cache.Add(key, svcId, sdCache.config.svcTTL)
110+
}
111+
112+
func (sdCache *sdCache) GetEndpoints(svcId string) (endpts []*model.Endpoint, found bool) {
113+
key := sdCache.buildEndptsKey(svcId)
114+
entry, exists := sdCache.cache.Get(key)
115+
if !exists {
116+
return nil, false
117+
}
118+
119+
endpts, ok := entry.([]*model.Endpoint)
120+
if !ok {
121+
sdCache.log.Error(errors.New("failed to retrieve endpoints from cache"), "", "svcId", svcId)
122+
sdCache.cache.Remove(key)
123+
return nil, false
124+
}
125+
126+
return endpts, true
127+
}
128+
129+
func (sdCache *sdCache) CacheEndpoints(svcId string, endpts []*model.Endpoint) {
130+
key := sdCache.buildEndptsKey(svcId)
131+
sdCache.cache.Add(key, endpts, sdCache.config.endptTTL)
132+
}
133+
134+
func (sdCache *sdCache) EvictEndpoints(svcId string) {
135+
key := sdCache.buildEndptsKey(svcId)
136+
sdCache.cache.Remove(key)
137+
}
138+
139+
func (sdCache *sdCache) buildNsKey(nsName string) (cacheKey string) {
140+
return fmt.Sprintf("%s:%s", nsKeyPrefix, nsName)
141+
}
142+
143+
func (sdCache *sdCache) buildSvcKey(nsName string, svcName string) (cacheKey string) {
144+
return fmt.Sprintf("%s:%s:%s", svcKeyPrefix, nsName, svcName)
145+
}
146+
147+
func (sdCache *sdCache) buildEndptsKey(svcId string) string {
148+
return fmt.Sprintf("%s:%s", endptKeyPrefix, svcId)
149+
}

pkg/cloudmap/cache_test.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package cloudmap
2+
3+
import (
4+
"github.com/aws/aws-cloud-map-mcs-controller-for-k8s/pkg/model"
5+
"github.com/aws/aws-cloud-map-mcs-controller-for-k8s/test"
6+
"github.com/stretchr/testify/assert"
7+
"testing"
8+
"time"
9+
)
10+
11+
func TestNewDefaultServiceDiscoveryClientCache(t *testing.T) {
12+
sdc := NewDefaultServiceDiscoveryClientCache().(*sdCache)
13+
14+
assert.Equal(t, defaultNsTTL, sdc.config.nsTTL)
15+
assert.Equal(t, defaultSvcTTL, sdc.config.svcTTL)
16+
assert.Equal(t, defaultEndptTTL, sdc.config.endptTTL)
17+
}
18+
19+
func TestServiceDiscoveryClientCacheGetNamespace_Found(t *testing.T) {
20+
sdc := NewDefaultServiceDiscoveryClientCache()
21+
sdc.CacheNamespace(test.GetTestHttpNamespace())
22+
23+
ns, found := sdc.GetNamespace(test.NsName)
24+
assert.True(t, found)
25+
assert.Equal(t, test.GetTestHttpNamespace(), ns)
26+
}
27+
28+
func TestServiceDiscoveryClientCacheGetNamespace_NotFound(t *testing.T) {
29+
sdc := NewDefaultServiceDiscoveryClientCache()
30+
31+
ns, found := sdc.GetNamespace(test.NsName)
32+
assert.False(t, found)
33+
assert.Nil(t, ns)
34+
}
35+
36+
func TestServiceDiscoveryClientCacheGetNamespace_Nil(t *testing.T) {
37+
sdc := NewDefaultServiceDiscoveryClientCache()
38+
sdc.CacheNilNamespace(test.NsName)
39+
40+
ns, found := sdc.GetNamespace(test.NsName)
41+
assert.True(t, found)
42+
assert.Nil(t, ns)
43+
}
44+
45+
func TestServiceDiscoveryClientCacheGetNamespace_Corrupt(t *testing.T) {
46+
sdc := NewDefaultServiceDiscoveryClientCache().(*sdCache)
47+
sdc.cache.Add(sdc.buildNsKey(test.NsName), &model.Resource{}, time.Minute)
48+
49+
ns, found := sdc.GetNamespace(test.NsName)
50+
assert.False(t, found)
51+
assert.Nil(t, ns)
52+
}
53+
54+
func TestServiceDiscoveryClientCacheGetServiceId_Found(t *testing.T) {
55+
sdc := NewDefaultServiceDiscoveryClientCache()
56+
sdc.CacheServiceId(test.NsName, test.SvcName, test.SvcId)
57+
58+
svcId, found := sdc.GetServiceId(test.NsName, test.SvcName)
59+
assert.True(t, found)
60+
assert.Equal(t, test.SvcId, svcId)
61+
}
62+
63+
func TestServiceDiscoveryClientCacheGetServiceId_NotFound(t *testing.T) {
64+
sdc := NewDefaultServiceDiscoveryClientCache()
65+
66+
svcId, found := sdc.GetServiceId(test.NsName, test.SvcName)
67+
assert.False(t, found)
68+
assert.Empty(t, svcId)
69+
}
70+
71+
func TestServiceDiscoveryClientCacheGetServiceId_Corrupt(t *testing.T) {
72+
sdc := NewDefaultServiceDiscoveryClientCache().(*sdCache)
73+
74+
sdc.cache.Add(sdc.buildSvcKey(test.NsName, test.SvcName), &model.Resource{}, time.Minute)
75+
svcId, found := sdc.GetServiceId(test.NsName, test.SvcName)
76+
assert.False(t, found)
77+
assert.Empty(t, svcId)
78+
}
79+
80+
func TestServiceDiscoveryClientCacheGetEndpoints_Found(t *testing.T) {
81+
sdc := NewDefaultServiceDiscoveryClientCache()
82+
sdc.CacheEndpoints(test.SvcId, []*model.Endpoint{test.GetTestEndpoint(), test.GetTestEndpoint2()})
83+
84+
endpts, found := sdc.GetEndpoints(test.SvcId)
85+
assert.True(t, found)
86+
assert.Equal(t, []*model.Endpoint{test.GetTestEndpoint(), test.GetTestEndpoint2()}, endpts)
87+
}
88+
89+
func TestServiceDiscoveryClientCacheGetEndpoints_NotFound(t *testing.T) {
90+
sdc := NewDefaultServiceDiscoveryClientCache()
91+
92+
endpts, found := sdc.GetEndpoints(test.SvcId)
93+
assert.False(t, found)
94+
assert.Nil(t, endpts)
95+
}
96+
97+
func TestServiceDiscoveryClientCacheGetEndpoints_Corrupt(t *testing.T) {
98+
sdc := NewDefaultServiceDiscoveryClientCache().(*sdCache)
99+
100+
sdc.cache.Add(sdc.buildEndptsKey(test.SvcId), &model.Resource{}, time.Minute)
101+
endpts, found := sdc.GetEndpoints(test.SvcId)
102+
assert.False(t, found)
103+
assert.Nil(t, endpts)
104+
}
105+
106+
func TestServiceDiscoveryClientEvictEndpoints(t *testing.T) {
107+
sdc := NewDefaultServiceDiscoveryClientCache()
108+
sdc.CacheEndpoints(test.SvcId, []*model.Endpoint{test.GetTestEndpoint(), test.GetTestEndpoint2()})
109+
sdc.EvictEndpoints(test.SvcId)
110+
111+
endpts, found := sdc.GetEndpoints(test.SvcId)
112+
assert.False(t, found)
113+
assert.Nil(t, endpts)
114+
}

0 commit comments

Comments
 (0)