Unified Storage: Adds pruner to kv backend (#110549)
* WIP adding pruner to kv store impl * pruner only keeps 20 most recent versions * ignore grafana-kv-data folder * extracts some stuff to pruner.go file. Adds tests. Adds kvBackendOptions. * update logging, comments, exports kvbackendoptions fields * update nooppruner ref * fixes field casing in test * fix test * linter fixes * remove comment * make KvStorageBackend private * Adds pruner key validation and tests. Fixes broken tests. * update error message when validating pruner key
This commit is contained in:
@@ -27,7 +27,14 @@ var appsNamespace = NamespacedResource{
|
||||
|
||||
func setupTestStorageBackend(t *testing.T) *kvStorageBackend {
|
||||
kv := setupTestKV(t)
|
||||
return NewKvStorageBackend(kv)
|
||||
opts := KvBackendOptions{
|
||||
KvStore: kv,
|
||||
WithPruner: true,
|
||||
}
|
||||
backend, err := NewKvStorageBackend(opts)
|
||||
kvBackend := backend.(*kvStorageBackend)
|
||||
require.NoError(t, err)
|
||||
return kvBackend
|
||||
}
|
||||
|
||||
func TestNewKvStorageBackend(t *testing.T) {
|
||||
@@ -1180,6 +1187,141 @@ func TestKvStorageBackend_GetResourceStats_Success(t *testing.T) {
|
||||
require.Equal(t, int64(2), filteredStats[0].Count)
|
||||
}
|
||||
|
||||
func TestKvStorageBackend_PruneEvents(t *testing.T) {
|
||||
t.Run("will prune oldest events when exceeding limit", func(t *testing.T) {
|
||||
backend := setupTestStorageBackend(t)
|
||||
ctx := context.Background()
|
||||
|
||||
// Create a resource
|
||||
testObj, err := createTestObjectWithName("test-resource", "apps", "test-data")
|
||||
require.NoError(t, err)
|
||||
metaAccessor, err := utils.MetaAccessor(testObj)
|
||||
require.NoError(t, err)
|
||||
writeEvent := WriteEvent{
|
||||
Type: resourcepb.WatchEvent_ADDED,
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: "default",
|
||||
Group: "apps",
|
||||
Resource: "resources",
|
||||
Name: "test-resource",
|
||||
},
|
||||
Value: objectToJSONBytes(t, testObj),
|
||||
Object: metaAccessor,
|
||||
PreviousRV: 0,
|
||||
}
|
||||
rv1, err := backend.WriteEvent(ctx, writeEvent)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Update the resource prunerMaxEvents times. This will create one more event than the pruner limit.
|
||||
previousRV := rv1
|
||||
for i := 0; i < prunerMaxEvents; i++ {
|
||||
testObj.Object["spec"].(map[string]any)["value"] = fmt.Sprintf("update-%d", i)
|
||||
writeEvent.Type = resourcepb.WatchEvent_MODIFIED
|
||||
writeEvent.Value = objectToJSONBytes(t, testObj)
|
||||
writeEvent.PreviousRV = previousRV
|
||||
newRv, err := backend.WriteEvent(ctx, writeEvent)
|
||||
require.NoError(t, err)
|
||||
previousRV = newRv
|
||||
}
|
||||
|
||||
pruningKey := PruningKey{
|
||||
Namespace: "default",
|
||||
Group: "apps",
|
||||
Resource: "resources",
|
||||
Name: "test-resource",
|
||||
}
|
||||
|
||||
err = backend.pruneEvents(ctx, pruningKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify the first event has been pruned (rv1)
|
||||
eventKey1 := DataKey{
|
||||
Namespace: "default",
|
||||
Group: "apps",
|
||||
Resource: "resources",
|
||||
Name: "test-resource",
|
||||
ResourceVersion: rv1,
|
||||
}
|
||||
|
||||
_, err = backend.dataStore.Get(ctx, eventKey1)
|
||||
require.Error(t, err) // Should return error as event is pruned
|
||||
|
||||
// assert prunerMaxEvents most recent events exist
|
||||
counter := 0
|
||||
for datakey, err := range backend.dataStore.Keys(ctx, ListRequestKey{
|
||||
Namespace: "default",
|
||||
Group: "apps",
|
||||
Resource: "resources",
|
||||
Name: "test-resource",
|
||||
}) {
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, rv1, datakey.ResourceVersion)
|
||||
counter++
|
||||
}
|
||||
require.Equal(t, prunerMaxEvents, counter)
|
||||
})
|
||||
|
||||
t.Run("will not prune events when less than limit", func(t *testing.T) {
|
||||
backend := setupTestStorageBackend(t)
|
||||
ctx := context.Background()
|
||||
|
||||
// Create a resource
|
||||
testObj, err := createTestObjectWithName("test-resource", "apps", "test-data")
|
||||
require.NoError(t, err)
|
||||
metaAccessor, err := utils.MetaAccessor(testObj)
|
||||
require.NoError(t, err)
|
||||
writeEvent := WriteEvent{
|
||||
Type: resourcepb.WatchEvent_ADDED,
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: "default",
|
||||
Group: "apps",
|
||||
Resource: "resources",
|
||||
Name: "test-resource",
|
||||
},
|
||||
Value: objectToJSONBytes(t, testObj),
|
||||
Object: metaAccessor,
|
||||
PreviousRV: 0,
|
||||
}
|
||||
rv1, err := backend.WriteEvent(ctx, writeEvent)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Update the resource prunerMaxEvents-1 times. This will create same number of events as the pruner limit.
|
||||
previousRV := rv1
|
||||
for i := 0; i < prunerMaxEvents-1; i++ {
|
||||
testObj.Object["spec"].(map[string]any)["value"] = fmt.Sprintf("update-%d", i)
|
||||
writeEvent.Type = resourcepb.WatchEvent_MODIFIED
|
||||
writeEvent.Value = objectToJSONBytes(t, testObj)
|
||||
writeEvent.PreviousRV = previousRV
|
||||
newRv, err := backend.WriteEvent(ctx, writeEvent)
|
||||
require.NoError(t, err)
|
||||
previousRV = newRv
|
||||
}
|
||||
|
||||
pruningKey := PruningKey{
|
||||
Namespace: "default",
|
||||
Group: "apps",
|
||||
Resource: "resources",
|
||||
Name: "test-resource",
|
||||
}
|
||||
|
||||
err = backend.pruneEvents(ctx, pruningKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
// assert all events exist
|
||||
counter := 0
|
||||
for _, err := range backend.dataStore.Keys(ctx, ListRequestKey{
|
||||
Namespace: "default",
|
||||
Group: "apps",
|
||||
Resource: "resources",
|
||||
Name: "test-resource",
|
||||
}) {
|
||||
require.NoError(t, err)
|
||||
counter++
|
||||
}
|
||||
require.Equal(t, prunerMaxEvents, counter)
|
||||
})
|
||||
}
|
||||
|
||||
// createTestObject creates a test unstructured object with standard values
|
||||
func createTestObject() (*unstructured.Unstructured, error) {
|
||||
return createTestObjectWithName("test-resource", appsNamespace, "test data")
|
||||
|
||||
Reference in New Issue
Block a user