@@ -344,7 +344,7 @@ func TestShouldDelegateList(t *testing.T) {
344
344
}
345
345
}
346
346
347
- runTestCases := func (t * testing.T , testcases map [opts ]bool , overrides ... map [opts ]bool ) {
347
+ runTestCases := func (t * testing.T , snapshotAvailable bool , testcases map [opts ]bool , overrides ... map [opts ]bool ) {
348
348
for opt , expectBypass := range testCases {
349
349
for _ , override := range overrides {
350
350
if bypass , ok := override [opt ]; ok {
@@ -362,6 +362,9 @@ func TestShouldDelegateList(t *testing.T) {
362
362
t .Fatalf ("Couldn't create cacher: %v" , err )
363
363
}
364
364
defer cacher .Stop ()
365
+ if snapshotAvailable {
366
+ cacher .watchCache .snapshots .Add (uint64 (mustAtoi (oldRV )), fakeOrderedLister {})
367
+ }
365
368
result , err := shouldDelegateList (toStorageOpts (opt ), cacher )
366
369
if err != nil {
367
370
t .Fatal (err )
@@ -371,27 +374,88 @@ func TestShouldDelegateList(t *testing.T) {
371
374
}
372
375
}
373
376
}
374
- consistentListFromCacheOverrides := map [opts ]bool {}
375
- for _ , recursive := range []bool {true , false } {
376
- consistentListFromCacheOverrides [opts {Recursive : recursive }] = false
377
- consistentListFromCacheOverrides [opts {Limit : 100 , Recursive : recursive }] = false
378
- }
377
+
378
+ // Exacts and continue on current cache RV.
379
+ listFromSnapshotEnabledOverrides := map [opts ]bool {}
380
+ listFromSnapshotEnabledOverrides [opts {Recursive : true , ResourceVersion : cacheRV , Limit : 100 }] = false
381
+ listFromSnapshotEnabledOverrides [opts {Recursive : true , ResourceVersion : cacheRV , ResourceVersionMatch : metav1 .ResourceVersionMatchExact }] = false
382
+ listFromSnapshotEnabledOverrides [opts {Recursive : true , ResourceVersion : cacheRV , Limit : 100 , ResourceVersionMatch : metav1 .ResourceVersionMatchExact }] = false
383
+ listFromSnapshotEnabledOverrides [opts {Recursive : true , ResourceVersion : "0" , Limit : 100 , Continue : continueOnCacheRV }] = false
384
+ listFromSnapshotEnabledOverrides [opts {Recursive : true , ResourceVersion : "0" , Continue : continueOnCacheRV }] = false
385
+ listFromSnapshotEnabledOverrides [opts {Recursive : true , Limit : 100 , Continue : continueOnCacheRV }] = false
386
+ listFromSnapshotEnabledOverrides [opts {Recursive : true , Continue : continueOnCacheRV }] = false
387
+
388
+ // Exacts and continue RV with a snapshot.
389
+ snapshotAvailableOverrides := map [opts ]bool {}
390
+ snapshotAvailableOverrides [opts {Recursive : true , Continue : continueOnOldRV }] = false
391
+ snapshotAvailableOverrides [opts {Recursive : true , Limit : 100 , Continue : continueOnOldRV }] = false
392
+ snapshotAvailableOverrides [opts {Recursive : true , ResourceVersion : "0" , Continue : continueOnOldRV }] = false
393
+ snapshotAvailableOverrides [opts {Recursive : true , ResourceVersion : "0" , Limit : 100 , Continue : continueOnOldRV }] = false
394
+ snapshotAvailableOverrides [opts {Recursive : true , ResourceVersion : oldRV , Limit : 100 }] = false
395
+ snapshotAvailableOverrides [opts {Recursive : true , ResourceVersion : oldRV , ResourceVersionMatch : metav1 .ResourceVersionMatchExact }] = false
396
+ snapshotAvailableOverrides [opts {Recursive : true , ResourceVersion : oldRV , ResourceVersionMatch : metav1 .ResourceVersionMatchExact , Limit : 100 }] = false
379
397
380
398
t .Run ("ConsistentListFromCache=false" , func (t * testing.T ) {
381
399
featuregatetesting .SetFeatureGateDuringTest (t , utilfeature .DefaultFeatureGate , features .ConsistentListFromCache , false )
382
- runTestCases (t , testCases )
400
+ t .Run ("ListFromCacheSnapshot=false" , func (t * testing.T ) {
401
+ featuregatetesting .SetFeatureGateDuringTest (t , utilfeature .DefaultFeatureGate , features .ListFromCacheSnapshot , false )
402
+ runTestCases (t , false , testCases )
403
+ })
404
+
405
+ t .Run ("ListFromCacheSnapshot=true" , func (t * testing.T ) {
406
+ featuregatetesting .SetFeatureGateDuringTest (t , utilfeature .DefaultFeatureGate , features .ListFromCacheSnapshot , true )
407
+ t .Run ("SnapshotAvailable=false" , func (t * testing.T ) {
408
+ runTestCases (t , false , testCases , listFromSnapshotEnabledOverrides )
409
+ })
410
+ t .Run ("SnapshotAvailable=true" , func (t * testing.T ) {
411
+ runTestCases (t , true , testCases , listFromSnapshotEnabledOverrides , snapshotAvailableOverrides )
412
+ })
413
+ })
383
414
})
384
415
t .Run ("ConsistentListFromCache=true" , func (t * testing.T ) {
385
416
featuregatetesting .SetFeatureGateDuringTest (t , utilfeature .DefaultFeatureGate , features .ConsistentListFromCache , true )
386
-
387
417
// TODO(p0lyn0mial): the following tests assume that etcdfeature.DefaultFeatureSupportChecker.Supports(storage.RequestWatchProgress)
388
418
// evaluates to true. Otherwise the cache will be bypassed and the test will fail.
389
419
//
390
420
// If you were to run only TestGetListCacheBypass you would see that the test fail.
391
421
// However in CI all test are run and there must be a test(s) that properly
392
422
// initialize the storage layer so that the mentioned method evaluates to true
393
423
forceRequestWatchProgressSupport (t )
394
- runTestCases (t , testCases , consistentListFromCacheOverrides )
424
+
425
+ consistentListFromCacheOverrides := map [opts ]bool {}
426
+ for _ , recursive := range []bool {true , false } {
427
+ consistentListFromCacheOverrides [opts {Recursive : recursive }] = false
428
+ consistentListFromCacheOverrides [opts {Limit : 100 , Recursive : recursive }] = false
429
+ }
430
+
431
+ t .Run ("ListFromCacheSnapshot=false" , func (t * testing.T ) {
432
+ featuregatetesting .SetFeatureGateDuringTest (t , utilfeature .DefaultFeatureGate , features .ListFromCacheSnapshot , false )
433
+ runTestCases (t , false , testCases , consistentListFromCacheOverrides )
434
+ })
435
+ t .Run ("ListFromCacheSnapshot=true" , func (t * testing.T ) {
436
+ featuregatetesting .SetFeatureGateDuringTest (t , utilfeature .DefaultFeatureGate , features .ListFromCacheSnapshot , true )
437
+
438
+ consistentReadWithSnapshotOverrides := map [opts ]bool {}
439
+ // Continues with negative RV are same as consistent read.
440
+ consistentReadWithSnapshotOverrides [opts {Recursive : true , Limit : 0 , Continue : continueOnNegativeRV }] = false
441
+ consistentReadWithSnapshotOverrides [opts {Recursive : true , Limit : 100 , Continue : continueOnNegativeRV }] = false
442
+ consistentReadWithSnapshotOverrides [opts {Recursive : true , ResourceVersion : "0" , Limit : 0 , Continue : continueOnNegativeRV }] = false
443
+ consistentReadWithSnapshotOverrides [opts {Recursive : true , ResourceVersion : "0" , Limit : 100 , Continue : continueOnNegativeRV }] = false
444
+ // Exact on RV not yet observed by cache
445
+ consistentReadWithSnapshotOverrides [opts {Recursive : true , ResourceVersion : etcdRV , Limit : 100 }] = false
446
+ consistentReadWithSnapshotOverrides [opts {Recursive : true , ResourceVersion : etcdRV , ResourceVersionMatch : metav1 .ResourceVersionMatchExact }] = false
447
+ consistentReadWithSnapshotOverrides [opts {Recursive : true , ResourceVersion : etcdRV , ResourceVersionMatch : metav1 .ResourceVersionMatchExact , Limit : 100 }] = false
448
+ consistentReadWithSnapshotOverrides [opts {Recursive : true , Continue : continueOnEtcdRV }] = false
449
+ consistentReadWithSnapshotOverrides [opts {Recursive : true , ResourceVersion : "0" , Continue : continueOnEtcdRV }] = false
450
+ consistentReadWithSnapshotOverrides [opts {Recursive : true , Continue : continueOnEtcdRV , Limit : 100 }] = false
451
+ consistentReadWithSnapshotOverrides [opts {Recursive : true , ResourceVersion : "0" , Continue : continueOnEtcdRV , Limit : 100 }] = false
452
+ t .Run ("SnapshotAvailable=false" , func (t * testing.T ) {
453
+ runTestCases (t , false , testCases , listFromSnapshotEnabledOverrides , consistentListFromCacheOverrides , consistentReadWithSnapshotOverrides )
454
+ })
455
+ t .Run ("SnapshotAvailable=true" , func (t * testing.T ) {
456
+ runTestCases (t , true , testCases , listFromSnapshotEnabledOverrides , consistentListFromCacheOverrides , consistentReadWithSnapshotOverrides , snapshotAvailableOverrides )
457
+ })
458
+ })
395
459
})
396
460
}
397
461
@@ -568,6 +632,91 @@ apiserver_watch_cache_consistent_read_total{fallback="true", resource="pods", su
568
632
}
569
633
}
570
634
635
+ func TestMatchExactResourceVersionFallback (t * testing.T ) {
636
+ tcs := []struct {
637
+ name string
638
+ snapshotsAvailable []bool
639
+
640
+ expectStoreRequests int
641
+ expectSnapshotRequests int
642
+ }{
643
+ {
644
+ name : "Disabled" ,
645
+ snapshotsAvailable : []bool {false , false },
646
+ expectStoreRequests : 2 ,
647
+ expectSnapshotRequests : 1 ,
648
+ },
649
+ {
650
+ name : "Enabled" ,
651
+ snapshotsAvailable : []bool {true , true },
652
+ expectStoreRequests : 1 ,
653
+ expectSnapshotRequests : 2 ,
654
+ },
655
+ {
656
+ name : "Fallback" ,
657
+ snapshotsAvailable : []bool {true , false },
658
+ expectSnapshotRequests : 2 ,
659
+ expectStoreRequests : 2 ,
660
+ },
661
+ }
662
+ for _ , tc := range tcs {
663
+ t .Run (tc .name , func (t * testing.T ) {
664
+ backingStorage := & dummyStorage {}
665
+ expectStoreRequests := 0
666
+ backingStorage .getListFn = func (_ context.Context , key string , opts storage.ListOptions , listObj runtime.Object ) error {
667
+ expectStoreRequests ++
668
+ podList := listObj .(* example.PodList )
669
+ switch opts .ResourceVersionMatch {
670
+ case "" :
671
+ podList .ResourceVersion = "42"
672
+ case metav1 .ResourceVersionMatchExact :
673
+ podList .ResourceVersion = opts .ResourceVersion
674
+ }
675
+ return nil
676
+ }
677
+ cacher , _ , err := newTestCacherWithoutSyncing (backingStorage , clock.RealClock {})
678
+ if err != nil {
679
+ t .Fatalf ("Couldn't create cacher: %v" , err )
680
+ }
681
+ defer cacher .Stop ()
682
+ snapshotRequestCount := 0
683
+ cacher .watchCache .RWMutex .Lock ()
684
+ cacher .watchCache .snapshots = & fakeSnapshotter {
685
+ getLessOrEqual : func (rv uint64 ) (orderedLister , bool ) {
686
+ snapshotAvailable := tc .snapshotsAvailable [snapshotRequestCount ]
687
+ snapshotRequestCount ++
688
+ if snapshotAvailable {
689
+ return fakeOrderedLister {}, true
690
+ } else {
691
+ return nil , false
692
+ }
693
+ },
694
+ }
695
+ cacher .watchCache .RWMutex .Unlock ()
696
+ if err := cacher .ready .wait (context .Background ()); err != nil {
697
+ t .Fatalf ("unexpected error waiting for the cache to be ready" )
698
+ }
699
+ delegator := NewCacheDelegator (cacher , backingStorage )
700
+
701
+ result := & example.PodList {}
702
+ err = delegator .GetList (context .TODO (), "pods/ns" , storage.ListOptions {ResourceVersion : "20" , ResourceVersionMatch : metav1 .ResourceVersionMatchExact , Recursive : true }, result )
703
+ if err != nil {
704
+ t .Fatalf ("Unexpected error: %v" , err )
705
+ }
706
+ if result .ResourceVersion != "20" {
707
+ t .Fatalf ("Unexpected List response RV, got: %q, want: %d" , result .ResourceVersion , 20 )
708
+ }
709
+ if expectStoreRequests != tc .expectStoreRequests {
710
+ t .Fatalf ("Unexpected number of requests to storage, got: %d, want: %d" , expectStoreRequests , tc .expectStoreRequests )
711
+ }
712
+ if snapshotRequestCount != tc .expectSnapshotRequests {
713
+ t .Fatalf ("Unexpected number of requests to snapshots, got: %d, want: %d" , snapshotRequestCount , tc .expectSnapshotRequests )
714
+ }
715
+
716
+ })
717
+ }
718
+ }
719
+
571
720
func TestGetListNonRecursiveCacheBypass (t * testing.T ) {
572
721
featuregatetesting .SetFeatureGateDuringTest (t , utilfeature .DefaultFeatureGate , features .ConsistentListFromCache , false )
573
722
backingStorage := & dummyStorage {}
0 commit comments