Skip to content

Commit 5ca4a9f

Browse files
cache/json: use shared buffers for JSON decoding (#1140)
This commit uses a shared buffer in a shared decoder for the ListBundles call, which should reduce our memory footprint when we're asked for this data. I also changed the sorting of the key-set to be an explicit call to sort.Slice, instead of an implicit side-effect from sets.New().List(). Signed-off-by: Steve Kuznetsov <[email protected]>
1 parent d02b0b6 commit 5ca4a9f

File tree

1 file changed

+60
-23
lines changed

1 file changed

+60
-23
lines changed

Diff for: pkg/cache/json.go

+60-23
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@ import (
66
"errors"
77
"fmt"
88
"hash/fnv"
9+
"io"
910
"io/fs"
1011
"os"
1112
"path/filepath"
13+
"sort"
1214
"strings"
1315

1416
"github.com/operator-framework/operator-registry/alpha/declcfg"
1517
"github.com/operator-framework/operator-registry/pkg/api"
1618
"github.com/operator-framework/operator-registry/pkg/registry"
17-
"k8s.io/apimachinery/pkg/util/sets"
19+
"github.com/sirupsen/logrus"
1820
)
1921

2022
var _ Cache = &JSON{}
@@ -58,31 +60,66 @@ func (q *JSON) ListBundles(ctx context.Context) ([]*api.Bundle, error) {
5860
}
5961

6062
func (q *JSON) SendBundles(_ context.Context, s registry.BundleSender) error {
63+
var keys []apiBundleKey
6164
for _, pkg := range q.packageIndex {
62-
channels := sets.KeySet(pkg.Channels)
63-
for _, chName := range sets.List(channels) {
64-
ch := pkg.Channels[chName]
65-
66-
bundles := sets.KeySet(ch.Bundles)
67-
for _, bName := range sets.List(bundles) {
68-
b := ch.Bundles[bName]
69-
apiBundle, err := q.loadAPIBundle(apiBundleKey{pkg.Name, ch.Name, b.Name})
70-
if err != nil {
71-
return fmt.Errorf("convert bundle %q: %v", b.Name, err)
72-
}
73-
if apiBundle.BundlePath != "" {
74-
// The SQLite-based server
75-
// configures its querier to
76-
// omit these fields when
77-
// bundle path is set.
78-
apiBundle.CsvJson = ""
79-
apiBundle.Object = nil
80-
}
81-
if err := s.Send(apiBundle); err != nil {
82-
return err
83-
}
65+
for _, ch := range pkg.Channels {
66+
for _, b := range ch.Bundles {
67+
keys = append(keys, apiBundleKey{pkg.Name, ch.Name, b.Name})
68+
}
69+
}
70+
}
71+
sort.Slice(keys, func(i, j int) bool {
72+
if keys[i].chName != keys[j].chName {
73+
return keys[i].chName < keys[j].chName
74+
}
75+
if keys[i].pkgName != keys[j].pkgName {
76+
return keys[i].pkgName < keys[j].pkgName
77+
}
78+
return keys[i].name < keys[j].name
79+
})
80+
var files []*os.File
81+
var readers []io.Reader
82+
for _, key := range keys {
83+
filename, ok := q.apiBundles[key]
84+
if !ok {
85+
return fmt.Errorf("package %q, channel %q, key %q not found", key.pkgName, key.chName, key.name)
86+
}
87+
file, err := os.Open(filename)
88+
if err != nil {
89+
return fmt.Errorf("failed to open file for package %q, channel %q, key %q: %w", key.pkgName, key.chName, key.name, err)
90+
}
91+
files = append(files, file)
92+
readers = append(readers, file)
93+
}
94+
defer func() {
95+
for _, file := range files {
96+
if err := file.Close(); err != nil {
97+
logrus.WithError(err).WithField("file", file.Name()).Warn("could not close file")
8498
}
8599
}
100+
}()
101+
multiReader := io.MultiReader(readers...)
102+
decoder := json.NewDecoder(multiReader)
103+
index := 0
104+
for {
105+
var bundle api.Bundle
106+
if err := decoder.Decode(&bundle); err == io.EOF {
107+
break
108+
} else if err != nil {
109+
return fmt.Errorf("failed to decode file for package %q, channel %q, key %q: %w", keys[index].pkgName, keys[index].chName, keys[index].name, err)
110+
}
111+
if bundle.BundlePath != "" {
112+
// The SQLite-based server
113+
// configures its querier to
114+
// omit these fields when
115+
// key path is set.
116+
bundle.CsvJson = ""
117+
bundle.Object = nil
118+
}
119+
if err := s.Send(&bundle); err != nil {
120+
return err
121+
}
122+
index += 1
86123
}
87124
return nil
88125
}

0 commit comments

Comments
 (0)