Add watch cache capacity upper bound adjusting logic
Kubernetes-commit: 2173a0fafd448e55b7738b88fbbab392125dd975
This commit is contained in:
parent
710763dd43
commit
efe7a1b26d
|
@ -169,7 +169,7 @@ func newWatchCache(
|
||||||
getAttrsFunc: getAttrsFunc,
|
getAttrsFunc: getAttrsFunc,
|
||||||
cache: make([]*watchCacheEvent, defaultLowerBoundCapacity),
|
cache: make([]*watchCacheEvent, defaultLowerBoundCapacity),
|
||||||
lowerBoundCapacity: defaultLowerBoundCapacity,
|
lowerBoundCapacity: defaultLowerBoundCapacity,
|
||||||
upperBoundCapacity: defaultUpperBoundCapacity,
|
upperBoundCapacity: capacityUpperBound(eventFreshDuration),
|
||||||
startIndex: 0,
|
startIndex: 0,
|
||||||
endIndex: 0,
|
endIndex: 0,
|
||||||
store: newStoreIndexer(indexers),
|
store: newStoreIndexer(indexers),
|
||||||
|
@ -189,6 +189,30 @@ func newWatchCache(
|
||||||
return wc
|
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<<exponent)
|
||||||
|
}
|
||||||
|
return defaultUpperBoundCapacity << exponent
|
||||||
|
}
|
||||||
|
|
||||||
// Add takes runtime.Object as an argument.
|
// Add takes runtime.Object as an argument.
|
||||||
func (w *watchCache) Add(obj interface{}) error {
|
func (w *watchCache) Add(obj interface{}) error {
|
||||||
object, resourceVersion, err := w.objectToVersionedRuntimeObject(obj)
|
object, resourceVersion, err := w.objectToVersionedRuntimeObject(obj)
|
||||||
|
|
|
@ -1163,6 +1163,49 @@ func TestSuggestedWatchChannelSize(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCapacityUpperBound(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
eventFreshDuration time.Duration
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "default eventFreshDuration",
|
||||||
|
eventFreshDuration: DefaultEventFreshDuration, // 75s
|
||||||
|
expected: defaultUpperBoundCapacity, // 100 * 1024
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "lower eventFreshDuration, capacity limit unchanged",
|
||||||
|
eventFreshDuration: 45 * time.Second, // 45s
|
||||||
|
expected: defaultUpperBoundCapacity, // 100 * 1024
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "higher eventFreshDuration, capacity limit scaled up",
|
||||||
|
eventFreshDuration: 4 * DefaultEventFreshDuration, // 4 * 75s
|
||||||
|
expected: 4 * defaultUpperBoundCapacity, // 4 * 100 * 1024
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "higher eventFreshDuration, capacity limit scaled and rounded up",
|
||||||
|
eventFreshDuration: 3 * DefaultEventFreshDuration, // 3 * 75s
|
||||||
|
expected: 4 * defaultUpperBoundCapacity, // 4 * 100 * 1024
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "higher eventFreshDuration, capacity limit scaled up and capped",
|
||||||
|
eventFreshDuration: DefaultEventFreshDuration << 20, // 2^20 * 75s
|
||||||
|
expected: defaultUpperBoundCapacity << 14, // 2^14 * 100 * 1024
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
capacity := capacityUpperBound(test.eventFreshDuration)
|
||||||
|
if test.expected != capacity {
|
||||||
|
t.Errorf("expected %v, got %v", test.expected, capacity)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkWatchCache_updateCache(b *testing.B) {
|
func BenchmarkWatchCache_updateCache(b *testing.B) {
|
||||||
store := newTestWatchCache(defaultUpperBoundCapacity, DefaultEventFreshDuration, &cache.Indexers{})
|
store := newTestWatchCache(defaultUpperBoundCapacity, DefaultEventFreshDuration, &cache.Indexers{})
|
||||||
defer store.Stop()
|
defer store.Stop()
|
||||||
|
|
Loading…
Reference in New Issue