Skip to content

Commit 8c3db95

Browse files
mnenciaarmru
andauthored
fix(object-cache): improve reliability of object cache management (#508)
* make sure objects expire after DefaultTTLSeconds * make cached objects have GKV information * fix cache retrieval and removal logic Closes #502 Signed-off-by: Marco Nenciarini <[email protected]> Signed-off-by: Armando Ruocco <[email protected]> Co-authored-by: Armando Ruocco <[email protected]>
1 parent 3c0d8c3 commit 8c3db95

File tree

2 files changed

+64
-26
lines changed

2 files changed

+64
-26
lines changed

internal/cnpgi/instance/internal/client/client.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99

1010
"github.com/cloudnative-pg/machinery/pkg/log"
1111
corev1 "k8s.io/api/core/v1"
12-
"k8s.io/apimachinery/pkg/runtime"
1312
"sigs.k8s.io/controller-runtime/pkg/client"
1413

1514
pluginBarman "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
@@ -21,11 +20,11 @@ const DefaultTTLSeconds = 10
2120
type cachedEntry struct {
2221
entry client.Object
2322
fetchUnixTime int64
24-
ttl time.Duration
23+
ttlSeconds int64
2524
}
2625

2726
func (e *cachedEntry) isExpired() bool {
28-
return time.Now().Unix()-e.fetchUnixTime > int64(e.ttl)
27+
return time.Now().Unix()-e.fetchUnixTime > e.ttlSeconds
2928
}
3029

3130
// ExtendedClient is an extended client that is capable of caching multiple secrets without relying on informers
@@ -91,7 +90,7 @@ func (e *ExtendedClient) getCachedObject(
9190
if cacheEntry.entry.GetNamespace() != key.Namespace || cacheEntry.entry.GetName() != key.Name {
9291
continue
9392
}
94-
if cacheEntry.entry.GetObjectKind().GroupVersionKind() != obj.GetObjectKind().GroupVersionKind() {
93+
if reflect.TypeOf(cacheEntry.entry) != reflect.TypeOf(obj) {
9594
continue
9695
}
9796
if cacheEntry.isExpired() {
@@ -121,8 +120,9 @@ func (e *ExtendedClient) getCachedObject(
121120
}
122121

123122
cs := cachedEntry{
124-
entry: obj.(runtime.Object).DeepCopyObject().(client.Object),
123+
entry: obj.DeepCopyObject().(client.Object),
125124
fetchUnixTime: time.Now().Unix(),
125+
ttlSeconds: DefaultTTLSeconds,
126126
}
127127

128128
contextLogger.Debug("setting object in the cache")
@@ -143,7 +143,7 @@ func (e *ExtendedClient) removeObject(object client.Object) {
143143
for i, cache := range e.cachedObjects {
144144
if cache.entry.GetNamespace() == object.GetNamespace() &&
145145
cache.entry.GetName() == object.GetName() &&
146-
cache.entry.GetObjectKind().GroupVersionKind() != object.GetObjectKind().GroupVersionKind() {
146+
reflect.TypeOf(cache.entry) == reflect.TypeOf(object) {
147147
e.cachedObjects = append(e.cachedObjects[:i], e.cachedObjects[i+1:]...)
148148
return
149149
}

internal/cnpgi/instance/internal/client/client_test.go

Lines changed: 58 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
"sigs.k8s.io/controller-runtime/pkg/client"
1010
"sigs.k8s.io/controller-runtime/pkg/client/fake"
1111

12-
v1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
12+
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
1313

1414
. "github.com/onsi/ginkgo/v2"
1515
. "github.com/onsi/gomega"
@@ -20,16 +20,26 @@ var scheme = buildScheme()
2020
func buildScheme() *runtime.Scheme {
2121
scheme := runtime.NewScheme()
2222
_ = corev1.AddToScheme(scheme)
23-
_ = v1.AddToScheme(scheme)
23+
_ = barmancloudv1.AddToScheme(scheme)
2424

2525
return scheme
2626
}
2727

28+
func addToCache(c *ExtendedClient, obj client.Object, fetchUnixTime int64) {
29+
ce := cachedEntry{
30+
entry: obj.DeepCopyObject().(client.Object),
31+
fetchUnixTime: fetchUnixTime,
32+
ttlSeconds: DefaultTTLSeconds,
33+
}
34+
ce.entry.SetResourceVersion("from cache")
35+
c.cachedObjects = append(c.cachedObjects, ce)
36+
}
37+
2838
var _ = Describe("ExtendedClient Get", func() {
2939
var (
3040
extendedClient *ExtendedClient
3141
secretInClient *corev1.Secret
32-
objectStore *v1.ObjectStore
42+
objectStore *barmancloudv1.ObjectStore
3343
)
3444

3545
BeforeEach(func() {
@@ -39,12 +49,12 @@ var _ = Describe("ExtendedClient Get", func() {
3949
Name: "test-secret",
4050
},
4151
}
42-
objectStore = &v1.ObjectStore{
52+
objectStore = &barmancloudv1.ObjectStore{
4353
ObjectMeta: metav1.ObjectMeta{
4454
Namespace: "default",
4555
Name: "test-object-store",
4656
},
47-
Spec: v1.ObjectStoreSpec{},
57+
Spec: barmancloudv1.ObjectStoreSpec{},
4858
}
4959

5060
baseClient := fake.NewClientBuilder().
@@ -61,35 +71,34 @@ var _ = Describe("ExtendedClient Get", func() {
6171
},
6272
}
6373

64-
// manually add the secret to the cache, this is not present in the fake client so we are sure it is from the
65-
// cache
66-
extendedClient.cachedObjects = []cachedEntry{
67-
{
68-
entry: secretNotInClient,
69-
fetchUnixTime: time.Now().Unix(),
70-
},
71-
}
74+
// manually add the secret to the cache, this is not present in the fake client,
75+
// so we are sure it is from the cache
76+
addToCache(extendedClient, secretNotInClient, time.Now().Unix())
7277

7378
err := extendedClient.Get(ctx, client.ObjectKeyFromObject(secretNotInClient), secretInClient)
7479
Expect(err).NotTo(HaveOccurred())
75-
Expect(secretNotInClient).To(Equal(extendedClient.cachedObjects[0].entry))
80+
Expect(secretInClient).To(Equal(extendedClient.cachedObjects[0].entry))
81+
Expect(secretInClient.GetResourceVersion()).To(Equal("from cache"))
7682
})
7783

7884
It("fetches secret from base client if cache is expired", func(ctx SpecContext) {
79-
extendedClient.cachedObjects = []cachedEntry{
80-
{
81-
entry: secretInClient.DeepCopy(),
82-
fetchUnixTime: time.Now().Add(-2 * time.Minute).Unix(),
83-
},
84-
}
85+
addToCache(extendedClient, secretInClient, time.Now().Add(-2*time.Minute).Unix())
8586

8687
err := extendedClient.Get(ctx, client.ObjectKeyFromObject(secretInClient), secretInClient)
8788
Expect(err).NotTo(HaveOccurred())
89+
Expect(secretInClient.GetResourceVersion()).NotTo(Equal("from cache"))
90+
91+
// the cache is updated with the new value
92+
Expect(extendedClient.cachedObjects).To(HaveLen(1))
93+
Expect(extendedClient.cachedObjects[0].entry.GetResourceVersion()).NotTo(Equal("from cache"))
8894
})
8995

9096
It("fetches secret from base client if not in cache", func(ctx SpecContext) {
9197
err := extendedClient.Get(ctx, client.ObjectKeyFromObject(secretInClient), secretInClient)
9298
Expect(err).NotTo(HaveOccurred())
99+
100+
// the cache is updated with the new value
101+
Expect(extendedClient.cachedObjects).To(HaveLen(1))
93102
})
94103

95104
It("does not cache non-secret objects", func(ctx SpecContext) {
@@ -106,4 +115,33 @@ var _ = Describe("ExtendedClient Get", func() {
106115
Expect(err).NotTo(HaveOccurred())
107116
Expect(extendedClient.cachedObjects).To(BeEmpty())
108117
})
118+
119+
It("returns the correct object from cache when multiple objects with the same object key are cached",
120+
func(ctx SpecContext) {
121+
secretNotInClient := &corev1.Secret{
122+
ObjectMeta: metav1.ObjectMeta{
123+
Namespace: "default",
124+
Name: "common-name",
125+
},
126+
}
127+
objectStoreNotInClient := &barmancloudv1.ObjectStore{
128+
ObjectMeta: metav1.ObjectMeta{
129+
Namespace: "default",
130+
Name: "common-name",
131+
},
132+
}
133+
134+
// manually add the objects to the cache, these are not present in the fake client,
135+
// so we are sure they are from the cache
136+
addToCache(extendedClient, secretNotInClient, time.Now().Unix())
137+
addToCache(extendedClient, objectStoreNotInClient, time.Now().Unix())
138+
139+
err := extendedClient.Get(ctx, client.ObjectKeyFromObject(secretNotInClient), secretInClient)
140+
Expect(err).NotTo(HaveOccurred())
141+
err = extendedClient.Get(ctx, client.ObjectKeyFromObject(objectStoreNotInClient), objectStore)
142+
Expect(err).NotTo(HaveOccurred())
143+
144+
Expect(secretInClient.GetResourceVersion()).To(Equal("from cache"))
145+
Expect(objectStore.GetResourceVersion()).To(Equal("from cache"))
146+
})
109147
})

0 commit comments

Comments
 (0)