From 678b79a173b9260b4cfb791ad0a0815ef97cf3d6 Mon Sep 17 00:00:00 2001 From: Shyam Jeedigunta Date: Tue, 14 Oct 2025 13:23:28 -0700 Subject: [PATCH] Properly account APF seats for legacy watches that compute init-events --- .../request/list_work_estimator.go | 8 +- .../util/flowcontrol/request/width_test.go | 83 +++++++++++++++++-- 2 files changed, 83 insertions(+), 8 deletions(-) diff --git a/staging/src/k8s.io/apiserver/pkg/util/flowcontrol/request/list_work_estimator.go b/staging/src/k8s.io/apiserver/pkg/util/flowcontrol/request/list_work_estimator.go index 8c84f20c825..c78a50cdd7a 100644 --- a/staging/src/k8s.io/apiserver/pkg/util/flowcontrol/request/list_work_estimator.go +++ b/staging/src/k8s.io/apiserver/pkg/util/flowcontrol/request/list_work_estimator.go @@ -86,10 +86,12 @@ func (e *listWorkEstimator) estimate(r *http.Request, flowSchemaName, priorityLe return WorkEstimate{InitialSeats: maxSeats} } - // For watch requests, we want to adjust the cost only if they explicitly request - // sending initial events. + // For watch requests we want to set the cost low if they aren't requesting for init events, + // either via the explicit SendInitialEvents param or via legacy watches that have RV=0 or unset. if requestInfo.Verb == "watch" { - if listOptions.SendInitialEvents == nil || !*listOptions.SendInitialEvents { + sendInitEvents := listOptions.SendInitialEvents != nil && *listOptions.SendInitialEvents + legacyWatch := listOptions.ResourceVersion == "" || listOptions.ResourceVersion == "0" + if !sendInitEvents && !legacyWatch { return WorkEstimate{InitialSeats: e.config.MinimumSeats} } } diff --git a/staging/src/k8s.io/apiserver/pkg/util/flowcontrol/request/width_test.go b/staging/src/k8s.io/apiserver/pkg/util/flowcontrol/request/width_test.go index 01dadab76ac..8c51aea8023 100644 --- a/staging/src/k8s.io/apiserver/pkg/util/flowcontrol/request/width_test.go +++ b/staging/src/k8s.io/apiserver/pkg/util/flowcontrol/request/width_test.go @@ -598,30 +598,79 @@ func TestWorkEstimator(t *testing.T) { initialSeatsExpected: 3, }, { - name: "request verb is watch, sendInitialEvents is nil", + name: "request verb is watch, sendInitialEvents is nil and RV unset (legacy watch with init events)", requestURI: "http://server/apis/foo.bar/v1/events?watch=true", requestInfo: &apirequest.RequestInfo{ Verb: "watch", APIGroup: "foo.bar", Resource: "events", }, - stats: storage.Stats{ObjectCount: 799, EstimatedAverageObjectSizeBytes: 10_000}, + stats: storage.Stats{ObjectCount: 799, EstimatedAverageObjectSizeBytes: 1_000}, + maxSeats: 100, + initialSeatsExpected: 8, + }, + { + name: "request verb is watch, sendInitialEvents is nil and RV set to 0 (legacy watch with init events)", + requestURI: "http://server/apis/foo.bar/v1/events?watch=true&resourceVersion=0", + requestInfo: &apirequest.RequestInfo{ + Verb: "watch", + APIGroup: "foo.bar", + Resource: "events", + }, + stats: storage.Stats{ObjectCount: 799, EstimatedAverageObjectSizeBytes: 1_000}, + maxSeats: 100, + initialSeatsExpected: 8, + }, + { + name: "request verb is watch, sendInitialEvents is nil and RV set to non-zero (legacy watch without init events)", + requestURI: "http://server/apis/foo.bar/v1/events?watch=true&resourceVersion=1", + requestInfo: &apirequest.RequestInfo{ + Verb: "watch", + APIGroup: "foo.bar", + Resource: "events", + }, + stats: storage.Stats{ObjectCount: 799, EstimatedAverageObjectSizeBytes: 1_000}, + maxSeats: 100, initialSeatsExpected: 1, }, { - name: "request verb is watch, sendInitialEvents is false", + name: "request verb is watch, sendInitialEvents is false and RV unset (legacy watch with init events)", requestURI: "http://server/apis/foo.bar/v1/events?watch=true&sendInitialEvents=false", requestInfo: &apirequest.RequestInfo{ Verb: "watch", APIGroup: "foo.bar", Resource: "events", }, - stats: storage.Stats{ObjectCount: 799, EstimatedAverageObjectSizeBytes: 10_000}, + stats: storage.Stats{ObjectCount: 799, EstimatedAverageObjectSizeBytes: 1_000}, + maxSeats: 100, + initialSeatsExpected: 8, + }, + { + name: "request verb is watch, sendInitialEvents is false and RV set to 0 (legacy watch with init events)", + requestURI: "http://server/apis/foo.bar/v1/events?watch=true&sendInitialEvents=false&resourceVersion=0", + requestInfo: &apirequest.RequestInfo{ + Verb: "watch", + APIGroup: "foo.bar", + Resource: "events", + }, + stats: storage.Stats{ObjectCount: 799, EstimatedAverageObjectSizeBytes: 1_000}, + maxSeats: 100, + initialSeatsExpected: 8, + }, + { + name: "request verb is watch, sendInitialEvents is false and RV set to non-zero (legacy watch without init events))", + requestURI: "http://server/apis/foo.bar/v1/events?watch=true&sendInitialEvents=false&resourceVersion=1", + requestInfo: &apirequest.RequestInfo{ + Verb: "watch", + APIGroup: "foo.bar", + Resource: "events", + }, + stats: storage.Stats{ObjectCount: 799, EstimatedAverageObjectSizeBytes: 1_000}, maxSeats: 100, initialSeatsExpected: 1, }, { - name: "request verb is watch, sendInitialEvents is true", + name: "request verb is watch, sendInitialEvents is true and RV unset (streaming list with init events)", requestURI: "http://server/apis/foo.bar/v1/events?watch=true&sendInitialEvents=true", requestInfo: &apirequest.RequestInfo{ Verb: "watch", @@ -632,6 +681,30 @@ func TestWorkEstimator(t *testing.T) { maxSeats: 100, initialSeatsExpected: 8, }, + { + name: "request verb is watch, sendInitialEvents is true and RV set to 0 (streaming list with init events)", + requestURI: "http://server/apis/foo.bar/v1/events?watch=true&sendInitialEvents=true&resourceVersion=0", + requestInfo: &apirequest.RequestInfo{ + Verb: "watch", + APIGroup: "foo.bar", + Resource: "events", + }, + stats: storage.Stats{ObjectCount: 799, EstimatedAverageObjectSizeBytes: 1_000}, + maxSeats: 100, + initialSeatsExpected: 8, + }, + { + name: "request verb is watch, sendInitialEvents is true and RV set to non-zero (streaming list with init events)", + requestURI: "http://server/apis/foo.bar/v1/events?watch=true&sendInitialEvents=true&resourceVersion=0", + requestInfo: &apirequest.RequestInfo{ + Verb: "watch", + APIGroup: "foo.bar", + Resource: "events", + }, + stats: storage.Stats{ObjectCount: 799, EstimatedAverageObjectSizeBytes: 1_000}, + maxSeats: 100, + initialSeatsExpected: 8, + }, { name: "request verb is create, no watches", requestURI: "http://server/apis/foo.bar/v1/foos",