fix(34): WR-06 bound the Plugins screen's search cache

pluginCatalog.lists was keyed by every distinct query and category and
never dropped an expired entry, so searching distinct strings grew the
process without limit (each entry can hold a 4 MiB answer). Storing a
search into a full cache now drops the expired searches and then the
ones that expire first, keeping at most 256.
This commit is contained in:
Jakub Zych
2026-10-29 22:10:41 +01:00
parent 2f3eba0cb1
commit 7b193032c8
4 changed files with 61 additions and 3 deletions

View File

@@ -133,6 +133,9 @@ const (
registryFailureTTL = time.Minute
registryLookups = 4
marketplaceMaxQuery = 100
// marketplaceCacheSize bounds the cached searches: every distinct
// query is a key, so the map must not grow with them (WR-06).
marketplaceCacheSize = 256
)
var (
@@ -247,11 +250,35 @@ func (c *pluginCatalog) list(ctx context.Context, q, category string) (kiosk.Lis
resp, ttl = kiosk.ListResponse{}, registryFailureTTL
}
c.mu.Lock()
c.lists[key] = cachedList{resp: resp, err: err, expires: c.now().Add(ttl)}
c.storeList(key, cachedList{resp: resp, err: err, expires: c.now().Add(ttl)})
c.mu.Unlock()
return resp, err
}
// storeList caches a search under key with c.mu held. It first drops the
// expired searches and then, while the cache is full, the one that
// expires first, so it never holds more than marketplaceCacheSize.
func (c *pluginCatalog) storeList(key string, entry cachedList) {
if _, ok := c.lists[key]; !ok && len(c.lists) >= marketplaceCacheSize {
now := c.now()
for k, v := range c.lists {
if !now.Before(v.expires) {
delete(c.lists, k)
}
}
for len(c.lists) >= marketplaceCacheSize {
oldest, first := "", true
for k, v := range c.lists {
if first || v.expires.Before(c.lists[oldest].expires) {
oldest, first = k, false
}
}
delete(c.lists, oldest)
}
}
c.lists[key] = entry
}
// releases returns the releases of plugin id, cached. listed is false when
// the registry does not know the ID (a 404 is an answer, not a failure).
func (c *pluginCatalog) releases(ctx context.Context, id string) ([]kiosk.RegistryVersion, bool, error) {