Unified Storage: Adds pruner for kv eventstore (#110785)

* Adds pruner for eventstore - default 24 hours. Adds tests.

* update comment

* remove delay on startup. formatting

* updates log message type and removes useless comment

* caller handles goroutine for runCleanupOldEvents()

* simplify timestamp extraction

* adds config for event pruning interval

* uses start and end key to get all expired events

* remove sort when listing keys in event pruner - order doesnt matter

* use snowflake constants

* log when we delete 0 rows

* pass time.Time to cleanup old events func
This commit is contained in:
owensmallwood
2025-09-12 14:40:16 -06:00
committed by GitHub
parent c5ed2780ab
commit 7ce971cba1
3 changed files with 241 additions and 24 deletions
@@ -7,6 +7,9 @@ import (
"iter"
"strconv"
"strings"
"time"
"github.com/bwmarrin/snowflake"
)
const (
@@ -224,3 +227,30 @@ func (n *eventStore) ListSince(ctx context.Context, sinceRV int64) iter.Seq2[Eve
}
}
}
// CleanupOldEvents deletes events older than the specified retention period.
func (n *eventStore) CleanupOldEvents(ctx context.Context, cutoff time.Time) (int, error) {
deletedCount := 0
// Keys are stored in the format of "resource_version~namespace~group~resource~name"
// With a start key of "1" and an end key of the cutoff time we can get all expired events.
endKey := fmt.Sprintf("%d", snowflakeFromTime(cutoff))
for key, err := range n.kv.Keys(ctx, eventsSection, ListOptions{StartKey: "1", EndKey: endKey}) {
if err != nil {
return deletedCount, fmt.Errorf("failed to list event keys: %w", err)
}
// TODO should use batch deletes here when available
if err := n.kv.Delete(ctx, eventsSection, key); err != nil {
return deletedCount, fmt.Errorf("failed to delete event key %s: %w", key, err)
}
deletedCount++
}
return deletedCount, nil
}
// snowflake id with last two sections set to 0 (machine id and sequence)
func snowflakeFromTime(t time.Time) int64 {
return (t.UnixMilli() - snowflake.Epoch) << (snowflake.NodeBits + snowflake.StepBits)
}