Skip to content

Commit bab1f06

Browse files
committed
Use more effiicent ListPager API.
Use ListPager.EachListItem instead of ListPager.List. Allows for incremental conversion page-by-page instead of loading up all pages into an upstream-format List object and then converting en-masse.
1 parent 2b55f03 commit bab1f06

File tree

1 file changed

+34
-15
lines changed

1 file changed

+34
-15
lines changed

libcalico-go/lib/backend/k8s/resources/list_pager.go

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package resources
1616

1717
import (
1818
"context"
19+
"sync/atomic"
1920

2021
"github.com/sirupsen/logrus"
2122
"k8s.io/apimachinery/pkg/api/meta"
@@ -39,28 +40,47 @@ func pagedList(
3940
*model.KVPairList,
4041
error,
4142
) {
43+
// Wrap our incoming listFunc with one that stashes the revision and number
44+
// of items we've seen so far. This allows us to use the more efficient
45+
// EachListItem() method, while also capturing the list metadata that we
46+
// need.
47+
listResourceVersion := ""
48+
var numItemsLoaded atomic.Int64 // listFunc is called from background goroutine.
49+
listFunc = func(ctx context.Context, opts metav1.ListOptions) (runtime.Object, error) {
50+
obj, err := listFunc(ctx, opts)
51+
m, err := meta.ListAccessor(obj)
52+
if err != nil {
53+
return nil, err
54+
}
55+
if m.GetResourceVersion() != "" {
56+
listResourceVersion = m.GetResourceVersion()
57+
}
58+
numItemsLoaded.Add(int64(meta.LenList(obj)))
59+
return obj, err
60+
}
4261
lp := pager.New(listFunc)
62+
4363
opts := metav1.ListOptions{ResourceVersion: revision}
4464
if revision != "" {
4565
opts.ResourceVersionMatch = metav1.ResourceVersionMatchNotOlderThan
4666
}
47-
result, isPaged, err := lp.List(ctx, opts)
48-
if err != nil {
49-
return nil, K8sErrorToCalico(err, list)
50-
}
51-
logCtx := log.WithField("pagedList", isPaged)
52-
logCtx.Debug("List() call completed, convert results")
5367

54-
// For each item in the response, convert it to a KVPair and add it to the list.
55-
kvps := []*model.KVPair{}
56-
err = meta.EachListItem(result, func(obj runtime.Object) error {
68+
var kvps []*model.KVPair
69+
err := lp.EachListItem(ctx, opts, func(obj runtime.Object) error {
5770
res := obj.(Resource)
5871
result, err := toKVPs(res)
5972
if err != nil {
60-
logCtx.WithError(err).WithField("Item", res).Warning("unable to process resource, skipping")
73+
log.WithError(err).WithField("Item", res).Warning("Unable to process resource, skipping")
6174
return nil
6275
}
6376
if result != nil {
77+
if kvps == nil {
78+
// Try to guess a suitable result slice capacity. In practice,
79+
// this will be the size of the first page (but that's usually
80+
// the only page.)
81+
ratio := len(result)
82+
kvps = make([]*model.KVPair, 0, int(numItemsLoaded.Load())*ratio)
83+
}
6484
kvps = append(kvps, result...)
6585
}
6686
return nil
@@ -69,13 +89,12 @@ func pagedList(
6989
return nil, K8sErrorToCalico(err, list)
7090
}
7191

72-
// Extract list revision information.
73-
m, err := meta.ListAccessor(result)
74-
if err != nil {
75-
return nil, err
92+
if listResourceVersion == "" {
93+
log.WithField("list", list).Panic("Failed to extract resource version from list.")
7694
}
95+
7796
return &model.KVPairList{
7897
KVPairs: kvps,
79-
Revision: m.GetResourceVersion(),
98+
Revision: listResourceVersion,
8099
}, nil
81100
}

0 commit comments

Comments
 (0)