Merge pull request #138701 from yedou37/feat-skip-consistent-list-fallback

apiserver: add feature gate to skip consistent list storage fallback
This commit is contained in:
Kubernetes Prow Robot 2026-05-04 19:28:22 +05:30 committed by GitHub
commit 7c7ae728f8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 56 additions and 3 deletions

View file

@ -2176,6 +2176,10 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate
{Version: version.MustParse("1.34"), Default: true, PreRelease: featuregate.GA, LockToDefault: true},
},
genericfeatures.ConsistentListFromCacheSkipTimeoutFallback: {
{Version: version.MustParse("1.37"), Default: false, PreRelease: featuregate.Alpha},
},
genericfeatures.ConstrainedImpersonation: {
{Version: version.MustParse("1.35"), Default: false, PreRelease: featuregate.Alpha},
{Version: version.MustParse("1.36"), Default: true, PreRelease: featuregate.Beta},
@ -2714,6 +2718,8 @@ var defaultKubernetesFeatureGateDependencies = map[featuregate.Feature][]feature
genericfeatures.ConsistentListFromCache: {},
genericfeatures.ConsistentListFromCacheSkipTimeoutFallback: {genericfeatures.ConsistentListFromCache},
genericfeatures.ConstrainedImpersonation: {},
genericfeatures.DeclarativeValidation: {},

View file

@ -103,6 +103,13 @@ const (
// Allow the API server to serve consistent lists from cache
ConsistentListFromCache featuregate.Feature = "ConsistentListFromCache"
// owner: @yedou37
//
// Skip the timeout fallback to storage when a consistent LIST from cache cannot be served,
// and return a retryable response instead.
// See https://github.com/kubernetes/kubernetes/issues/138494.
ConsistentListFromCacheSkipTimeoutFallback featuregate.Feature = "ConsistentListFromCacheSkipTimeoutFallback"
// owner: @enj @qiujian16
// kep: https://kep.k8s.io/5284
//
@ -379,6 +386,10 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate
{Version: version.MustParse("1.34"), Default: true, PreRelease: featuregate.GA, LockToDefault: true},
},
ConsistentListFromCacheSkipTimeoutFallback: {
{Version: version.MustParse("1.37"), Default: false, PreRelease: featuregate.Alpha},
},
ConstrainedImpersonation: {
{Version: version.MustParse("1.35"), Default: false, PreRelease: featuregate.Alpha},
{Version: version.MustParse("1.36"), Default: true, PreRelease: featuregate.Beta},

View file

@ -373,11 +373,13 @@ func TestConsistentReadFallback(t *testing.T) {
tcs := []struct {
name string
consistentReadsEnabled bool
skipStorageFallback bool
watchCacheRV string
storageRV string
fallbackError bool
expectError bool
expectTooManyRequests bool
expectRV string
expectBlock bool
expectRequestsToStorage int
@ -423,6 +425,22 @@ apiserver_watch_cache_consistent_read_total{fallback="true", group="", resource=
# HELP apiserver_watch_cache_consistent_read_total [ALPHA] Counter for consistent reads from cache.
# TYPE apiserver_watch_cache_consistent_read_total counter
apiserver_watch_cache_consistent_read_total{fallback="true", group="", resource="pods", success="false"} 1
`,
},
{
name: "Skip Storage Fallback",
consistentReadsEnabled: true,
skipStorageFallback: true,
watchCacheRV: "2",
storageRV: "42",
expectError: true,
expectTooManyRequests: true,
expectBlock: true,
expectRequestsToStorage: 1,
expectMetric: `
# HELP apiserver_watch_cache_consistent_read_total [ALPHA] Counter for consistent reads from cache.
# TYPE apiserver_watch_cache_consistent_read_total counter
apiserver_watch_cache_consistent_read_total{fallback="skipped", group="", resource="pods", success="false"} 1
`,
},
{
@ -442,6 +460,9 @@ apiserver_watch_cache_consistent_read_total{fallback="true", group="", resource=
featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.33"))
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ConsistentListFromCache, false)
}
if tc.skipStorageFallback {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ConsistentListFromCacheSkipTimeoutFallback, true)
}
registry := k8smetrics.NewKubeRegistry()
metrics.ConsistentReadTotal.Reset()
@ -518,6 +539,9 @@ apiserver_watch_cache_consistent_read_total{fallback="true", group="", resource=
if (err != nil) != tc.expectError {
t.Fatalf("Unexpected error err: %v", err)
}
if tc.expectTooManyRequests && !apierrors.IsTooManyRequests(err) {
t.Fatalf("Unexpected error, got: %v, want TooManyRequests", err)
}
if result.ResourceVersion != tc.expectRV {
t.Fatalf("Unexpected List response RV, got: %q, want: %q", result.ResourceVersion, tc.expectRV)
}

View file

@ -217,10 +217,15 @@ func (c *CacheDelegator) GetList(ctx context.Context, key string, opts storage.L
}
if result.ConsistentRead {
// IsTooLargeResourceVersion occurs when the requested RV is higher than cache's current RV
// and cache hasn't caught up within the timeout period. Fall back to etcd.
// and cache hasn't caught up within the timeout period.
if storage.IsTooLargeResourceVersion(err) {
fallback = "true"
err = c.storage.GetList(ctx, key, opts, listObj)
if utilfeature.DefaultFeatureGate.Enabled(features.ConsistentListFromCacheSkipTimeoutFallback) {
fallback = "skipped"
err = errors.NewTooManyRequests(err.Error(), resourceVersionTooHighRetrySeconds)
} else {
fallback = "true"
err = c.storage.GetList(ctx, key, opts, listObj)
}
}
if err != nil {
success = "false"

View file

@ -43,6 +43,7 @@
| ComponentStatusz | :ballot_box_with_check: 1.36+ | | 1.321.35 | 1.36 | | | | [code](https://cs.k8s.io/?q=%5CbComponentStatusz%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbComponentStatusz%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
| ConcurrentWatchObjectDecode | | | | 1.31 | | | | [code](https://cs.k8s.io/?q=%5CbConcurrentWatchObjectDecode%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbConcurrentWatchObjectDecode%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
| ConsistentListFromCache | :ballot_box_with_check: 1.31+ | :closed_lock_with_key: 1.34+ | 1.281.30 | 1.311.33 | 1.34 | | | [code](https://cs.k8s.io/?q=%5CbConsistentListFromCache%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbConsistentListFromCache%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
| ConsistentListFromCacheSkipTimeoutFallback | | | 1.37 | | | | ConsistentListFromCache | [code](https://cs.k8s.io/?q=%5CbConsistentListFromCacheSkipTimeoutFallback%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbConsistentListFromCacheSkipTimeoutFallback%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
| ConstrainedImpersonation | :ballot_box_with_check: 1.36+ | | 1.35 | 1.36 | | | | [code](https://cs.k8s.io/?q=%5CbConstrainedImpersonation%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbConstrainedImpersonation%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
| ContainerCheckpoint | :ballot_box_with_check: 1.30+ | | 1.251.29 | 1.30 | | | | [code](https://cs.k8s.io/?q=%5CbContainerCheckpoint%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbContainerCheckpoint%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
| ContainerRestartRules | :ballot_box_with_check: 1.35+ | | 1.34 | 1.35 | | | | [code](https://cs.k8s.io/?q=%5CbContainerRestartRules%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbContainerRestartRules%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |

View file

@ -235,6 +235,12 @@
lockToDefault: true
preRelease: GA
version: "1.34"
- name: ConsistentListFromCacheSkipTimeoutFallback
versionedSpecs:
- default: false
lockToDefault: false
preRelease: Alpha
version: "1.37"
- name: ConstrainedImpersonation
versionedSpecs:
- default: false