unified-storage: use name instead of offset in kvstore continue token (#113560)

* unified-storage: use name instead of offset in kvstore continue token

---------

Co-authored-by: Georges Chaudy <chaudyg@gmail.com>
This commit is contained in:
Will Assis
2025-12-02 10:35:03 -05:00
committed by GitHub
co-authored by Georges Chaudy
parent bdf529c545
commit 047e6d45fa
11 changed files with 236 additions and 115 deletions
+56 -11
View File
@@ -303,7 +303,7 @@ func (d *dataStore) GetResourceKeyAtRevision(ctx context.Context, key GetRequest
listKey := ListRequestKey(key)
iter := d.ListResourceKeysAtRevision(ctx, listKey, rv)
iter := d.ListResourceKeysAtRevision(ctx, ListRequestOptions{Key: listKey, ResourceVersion: rv})
for dataKey, err := range iter {
if err != nil {
return DataKey{}, err
@@ -313,32 +313,77 @@ func (d *dataStore) GetResourceKeyAtRevision(ctx context.Context, key GetRequest
return DataKey{}, ErrNotFound
}
type ListRequestOptions struct {
// Key defines the range to query (Group/Resource/Namespace/Name prefix).
Key ListRequestKey
// ContinueNamespace is the namespace to continue from.
// Only used when Key.Namespace is empty (cross-namespace query).
ContinueNamespace string
// ContinueName is the name to continue from.
ContinueName string
ResourceVersion int64
}
// Validate checks that the ListRequestOptions are valid.
func (o ListRequestOptions) Validate() error {
if err := o.Key.Validate(); err != nil {
return fmt.Errorf("invalid list request key: %w", err)
}
// ContinueNamespace is only valid for cross-namespace queries
if o.ContinueNamespace != "" && o.Key.Namespace != "" {
return fmt.Errorf("continue namespace %q not allowed when request namespace is set to %q", o.ContinueNamespace, o.Key.Namespace)
}
return nil
}
// ListLatestResourceKeys returns an iterator over the data keys for the latest versions of resources.
// Only returns keys for resources that are not deleted.
func (d *dataStore) ListLatestResourceKeys(ctx context.Context, key ListRequestKey) iter.Seq2[DataKey, error] {
return d.ListResourceKeysAtRevision(ctx, key, 0)
return d.ListResourceKeysAtRevision(ctx, ListRequestOptions{
Key: key,
})
}
// ListResourceKeysAtRevision returns an iterator over data keys for resources at a specific revision.
// If rv is 0, it returns the latest versions. Only returns keys for resources that are not deleted at the given revision.
func (d *dataStore) ListResourceKeysAtRevision(ctx context.Context, key ListRequestKey, rv int64) iter.Seq2[DataKey, error] {
if err := key.Validate(); err != nil {
func (d *dataStore) ListResourceKeysAtRevision(ctx context.Context, options ListRequestOptions) iter.Seq2[DataKey, error] {
if err := options.Validate(); err != nil {
return func(yield func(DataKey, error) bool) {
yield(DataKey{}, fmt.Errorf("invalid list request key: %w", err))
yield(DataKey{}, err)
}
}
rv := options.ResourceVersion
prefix := options.Key.Prefix()
startKey := prefix
if options.ContinueName != "" {
// Build the start key from the continue position
continueKey := ListRequestKey{
Group: options.Key.Group,
Resource: options.Key.Resource,
Namespace: options.Key.Namespace,
Name: options.ContinueName,
}
// For cross-namespace queries, use the continue namespace
if options.Key.Namespace == "" && options.ContinueNamespace != "" {
continueKey.Namespace = options.ContinueNamespace
}
startKey = continueKey.Prefix()
}
listOptions := ListOptions{
StartKey: startKey,
EndKey: PrefixRangeEnd(prefix),
Sort: SortOrderAsc,
}
if rv == 0 {
rv = math.MaxInt64
}
prefix := key.Prefix()
// List all keys in the prefix.
iter := d.kv.Keys(ctx, dataSection, ListOptions{
StartKey: prefix,
EndKey: PrefixRangeEnd(prefix),
Sort: SortOrderAsc,
})
iter := d.kv.Keys(ctx, dataSection, listOptions)
return func(yield func(DataKey, error) bool) {
var candidateKey *DataKey // The current candidate key we are iterating over