Merge pull request #133604 from serathius/watchcache-count

Fix storage counting all objects instead of objects for resource
This commit is contained in:
Kubernetes Prow Robot 2025-08-19 09:23:36 -07:00 committed by GitHub
commit 4e8b192b66
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 114 additions and 2 deletions

View file

@ -634,7 +634,14 @@ func (s *store) Stats(ctx context.Context) (stats storage.Stats, err error) {
return s.stats.Stats(ctx)
}
startTime := time.Now()
count, err := s.client.Kubernetes.Count(ctx, s.pathPrefix, kubernetes.CountOptions{})
prefix, err := s.prepareKey(s.resourcePrefix)
if err != nil {
return storage.Stats{}, err
}
if !strings.HasSuffix(prefix, "/") {
prefix += "/"
}
count, err := s.client.Kubernetes.Count(ctx, prefix, kubernetes.CountOptions{})
metrics.RecordEtcdRequest("listWithCount", s.groupResource, err, startTime)
if err != nil {
return storage.Stats{}, err
@ -652,7 +659,14 @@ func (s *store) SetKeysFunc(keys storage.KeysFunc) {
func (s *store) getKeys(ctx context.Context) ([]string, error) {
startTime := time.Now()
resp, err := s.client.KV.Get(ctx, s.pathPrefix, clientv3.WithPrefix(), clientv3.WithKeysOnly())
prefix, err := s.prepareKey(s.resourcePrefix)
if err != nil {
return nil, err
}
if !strings.HasSuffix(prefix, "/") {
prefix += "/"
}
resp, err := s.client.KV.Get(ctx, prefix, clientv3.WithPrefix(), clientv3.WithKeysOnly())
metrics.RecordEtcdRequest("listOnlyKeys", s.groupResource, err, startTime)
if err != nil {
return nil, err

View file

@ -28,6 +28,7 @@ import (
"time"
"github.com/go-logr/logr"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/require"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/kubernetes"
@ -588,6 +589,12 @@ func withPrefix(prefix string) setupOption {
}
}
func withResourcePrefix(prefix string) setupOption {
return func(options *setupOptions) {
options.resourcePrefix = prefix
}
}
func withLeaseConfig(leaseConfig LeaseManagerConfig) setupOption {
return func(options *setupOptions) {
options.leaseConfig = leaseConfig
@ -996,3 +1003,94 @@ func BenchmarkStatsCacheCleanKeys(b *testing.B) {
b.Fatalf("Unexpected number of keys in stats, want: %d, got: %d", namespaceCount*podPerNamespaceCount, len(store.stats.keys))
}
}
func TestPrefixGetKeys(t *testing.T) {
ctx, store, c := testSetup(t, withPrefix("/registry"), withResourcePrefix("pods"))
_, err := c.KV.Put(ctx, "key", "a")
if err != nil {
t.Fatal(err)
}
_, err = c.KV.Put(ctx, "/registry/key", "b")
if err != nil {
t.Fatal(err)
}
_, err = c.KV.Put(ctx, "/registry/pods/key", "c")
if err != nil {
t.Fatal(err)
}
_, err = c.KV.Put(ctx, "/registry/podskey", "d")
if err != nil {
t.Fatal(err)
}
gotKeys, err := store.getKeys(ctx)
if err != nil {
t.Fatal(err)
}
wantKeys := []string{"/registry/pods/key"}
if diff := cmp.Diff(wantKeys, gotKeys); diff != "" {
t.Errorf("getKeys diff:\n%s", diff)
}
}
func TestPrefixStats(t *testing.T) {
tcs := []struct {
name string
estimate bool
expectStats storage.Stats
}{
{
name: "SizeBasedListCostEstimate=false",
estimate: false,
expectStats: storage.Stats{ObjectCount: 1},
},
{
name: "SizeBasedListCostEstimate=true",
estimate: true,
expectStats: storage.Stats{ObjectCount: 1, EstimatedAverageObjectSizeBytes: 3},
},
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SizeBasedListCostEstimate, tc.estimate)
ctx, store, c := testSetup(t, withPrefix("/registry"), withResourcePrefix("pods"))
_, err := c.KV.Put(ctx, "key", "a")
if err != nil {
t.Fatal(err)
}
_, err = c.KV.Put(ctx, "/registry/key", "ab")
if err != nil {
t.Fatal(err)
}
_, err = c.KV.Put(ctx, "/registry/pods/key", "abc")
if err != nil {
t.Fatal(err)
}
_, err = c.KV.Put(ctx, "/registry/podskey", "abcd")
if err != nil {
t.Fatal(err)
}
listOut := &example.PodList{}
// Ignore error as decode is expected to fail
_ = store.GetList(ctx, "pods", storage.ListOptions{Predicate: storage.Everything, Recursive: true}, listOut)
gotStats, err := store.Stats(ctx)
if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(tc.expectStats, gotStats); diff != "" {
t.Errorf("Stats diff:\n%s", diff)
}
})
}
}