diff --git a/staging/src/k8s.io/apiserver/pkg/storage/cacher/watch_cache.go b/staging/src/k8s.io/apiserver/pkg/storage/cacher/watch_cache.go index 920b576bf56..78380c4edcc 100644 --- a/staging/src/k8s.io/apiserver/pkg/storage/cacher/watch_cache.go +++ b/staging/src/k8s.io/apiserver/pkg/storage/cacher/watch_cache.go @@ -169,7 +169,7 @@ func newWatchCache( getAttrsFunc: getAttrsFunc, cache: make([]*watchCacheEvent, defaultLowerBoundCapacity), lowerBoundCapacity: defaultLowerBoundCapacity, - upperBoundCapacity: defaultUpperBoundCapacity, + upperBoundCapacity: capacityUpperBound(eventFreshDuration), startIndex: 0, endIndex: 0, store: newStoreIndexer(indexers), @@ -189,6 +189,30 @@ func newWatchCache( return wc } +// capacityUpperBound denotes the maximum possible capacity of the watch cache +// to which it can resize. +func capacityUpperBound(eventFreshDuration time.Duration) int { + if eventFreshDuration <= DefaultEventFreshDuration { + return defaultUpperBoundCapacity + } + // eventFreshDuration determines how long the watch events are supposed + // to be stored in the watch cache. + // In very high churn situations, there is a need to store more events + // in the watch cache, hence it would have to be upsized accordingly. + // Because of that, for larger values of eventFreshDuration, we set the + // upper bound of the watch cache's capacity proportionally to the ratio + // between eventFreshDuration and DefaultEventFreshDuration. + // Given that the watch cache size can only double, we round up that + // proportion to the next power of two. + exponent := int(math.Ceil((math.Log2(eventFreshDuration.Seconds() / DefaultEventFreshDuration.Seconds())))) + if maxExponent := int(math.Floor((math.Log2(math.MaxInt32 / defaultUpperBoundCapacity)))); exponent > maxExponent { + // Making sure that the capacity's upper bound fits in a 32-bit integer. + exponent = maxExponent + klog.Warningf("Capping watch cache capacity upper bound to %v", defaultUpperBoundCapacity<