k8s.io/apiserver@v0.29.3/pkg/storage/cacher/watch_cache_test.go (about)

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package cacher
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"strconv"
    23  	"strings"
    24  	"testing"
    25  	"time"
    26  
    27  	v1 "k8s.io/api/core/v1"
    28  	apiequality "k8s.io/apimachinery/pkg/api/equality"
    29  	"k8s.io/apimachinery/pkg/api/errors"
    30  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    31  	"k8s.io/apimachinery/pkg/fields"
    32  	"k8s.io/apimachinery/pkg/labels"
    33  	"k8s.io/apimachinery/pkg/runtime"
    34  	"k8s.io/apimachinery/pkg/runtime/schema"
    35  	"k8s.io/apimachinery/pkg/util/wait"
    36  	"k8s.io/apimachinery/pkg/watch"
    37  	"k8s.io/apiserver/pkg/features"
    38  	"k8s.io/apiserver/pkg/storage"
    39  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    40  	"k8s.io/client-go/tools/cache"
    41  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    42  	"k8s.io/utils/clock"
    43  	testingclock "k8s.io/utils/clock/testing"
    44  )
    45  
    46  func makeTestPod(name string, resourceVersion uint64) *v1.Pod {
    47  	return makeTestPodDetails(name, resourceVersion, "some-node", map[string]string{"k8s-app": "my-app"})
    48  }
    49  
    50  func makeTestPodDetails(name string, resourceVersion uint64, nodeName string, labels map[string]string) *v1.Pod {
    51  	return &v1.Pod{
    52  		ObjectMeta: metav1.ObjectMeta{
    53  			Namespace:       "ns",
    54  			Name:            name,
    55  			ResourceVersion: strconv.FormatUint(resourceVersion, 10),
    56  			Labels:          labels,
    57  		},
    58  		Spec: v1.PodSpec{
    59  			NodeName: nodeName,
    60  		},
    61  	}
    62  }
    63  
    64  func makeTestStoreElement(pod *v1.Pod) *storeElement {
    65  	return &storeElement{
    66  		Key:    "prefix/ns/" + pod.Name,
    67  		Object: pod,
    68  		Labels: labels.Set(pod.Labels),
    69  		Fields: fields.Set{"spec.nodeName": pod.Spec.NodeName},
    70  	}
    71  }
    72  
    73  type testWatchCache struct {
    74  	*watchCache
    75  
    76  	bookmarkRevision chan int64
    77  	stopCh           chan struct{}
    78  }
    79  
    80  func (w *testWatchCache) getAllEventsSince(resourceVersion uint64) ([]*watchCacheEvent, error) {
    81  	cacheInterval, err := w.getCacheIntervalForEvents(resourceVersion)
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  
    86  	result := []*watchCacheEvent{}
    87  	for {
    88  		event, err := cacheInterval.Next()
    89  		if err != nil {
    90  			return nil, err
    91  		}
    92  		if event == nil {
    93  			break
    94  		}
    95  		result = append(result, event)
    96  	}
    97  
    98  	return result, nil
    99  }
   100  
   101  func (w *testWatchCache) getCacheIntervalForEvents(resourceVersion uint64) (*watchCacheInterval, error) {
   102  	w.RLock()
   103  	defer w.RUnlock()
   104  
   105  	return w.getAllEventsSinceLocked(resourceVersion)
   106  }
   107  
   108  // newTestWatchCache just adds a fake clock.
   109  func newTestWatchCache(capacity int, indexers *cache.Indexers) *testWatchCache {
   110  	keyFunc := func(obj runtime.Object) (string, error) {
   111  		return storage.NamespaceKeyFunc("prefix", obj)
   112  	}
   113  	getAttrsFunc := func(obj runtime.Object) (labels.Set, fields.Set, error) {
   114  		pod, ok := obj.(*v1.Pod)
   115  		if !ok {
   116  			return nil, nil, fmt.Errorf("not a pod")
   117  		}
   118  		return labels.Set(pod.Labels), fields.Set{"spec.nodeName": pod.Spec.NodeName}, nil
   119  	}
   120  	versioner := storage.APIObjectVersioner{}
   121  	mockHandler := func(*watchCacheEvent) {}
   122  	wc := &testWatchCache{}
   123  	wc.bookmarkRevision = make(chan int64, 1)
   124  	wc.stopCh = make(chan struct{})
   125  	pr := newConditionalProgressRequester(wc.RequestWatchProgress, &immediateTickerFactory{}, nil)
   126  	go pr.Run(wc.stopCh)
   127  	wc.watchCache = newWatchCache(keyFunc, mockHandler, getAttrsFunc, versioner, indexers, testingclock.NewFakeClock(time.Now()), schema.GroupResource{Resource: "pods"}, pr)
   128  	// To preserve behavior of tests that assume a given capacity,
   129  	// resize it to th expected size.
   130  	wc.capacity = capacity
   131  	wc.cache = make([]*watchCacheEvent, capacity)
   132  	wc.lowerBoundCapacity = min(capacity, defaultLowerBoundCapacity)
   133  	wc.upperBoundCapacity = max(capacity, defaultUpperBoundCapacity)
   134  
   135  	return wc
   136  }
   137  
   138  type immediateTickerFactory struct{}
   139  
   140  func (t *immediateTickerFactory) NewTicker(d time.Duration) clock.Ticker {
   141  	return &immediateTicker{stopCh: make(chan struct{})}
   142  }
   143  
   144  type immediateTicker struct {
   145  	stopCh chan struct{}
   146  }
   147  
   148  func (t *immediateTicker) C() <-chan time.Time {
   149  	ch := make(chan time.Time)
   150  	go func() {
   151  		for {
   152  			select {
   153  			case ch <- time.Now():
   154  			case <-t.stopCh:
   155  				return
   156  			}
   157  		}
   158  	}()
   159  	return ch
   160  }
   161  
   162  func (t *immediateTicker) Stop() {
   163  	close(t.stopCh)
   164  }
   165  
   166  func (w *testWatchCache) RequestWatchProgress(ctx context.Context) error {
   167  	go func() {
   168  		select {
   169  		case rev := <-w.bookmarkRevision:
   170  			w.UpdateResourceVersion(fmt.Sprintf("%d", rev))
   171  		case <-ctx.Done():
   172  			return
   173  		}
   174  	}()
   175  	return nil
   176  }
   177  
   178  func (w *testWatchCache) Stop() {
   179  	close(w.stopCh)
   180  }
   181  
   182  func TestWatchCacheBasic(t *testing.T) {
   183  	store := newTestWatchCache(2, &cache.Indexers{})
   184  	defer store.Stop()
   185  
   186  	// Test Add/Update/Delete.
   187  	pod1 := makeTestPod("pod", 1)
   188  	if err := store.Add(pod1); err != nil {
   189  		t.Errorf("unexpected error: %v", err)
   190  	}
   191  	if item, ok, _ := store.Get(pod1); !ok {
   192  		t.Errorf("didn't find pod")
   193  	} else {
   194  		expected := makeTestStoreElement(makeTestPod("pod", 1))
   195  		if !apiequality.Semantic.DeepEqual(expected, item) {
   196  			t.Errorf("expected %v, got %v", expected, item)
   197  		}
   198  	}
   199  	pod2 := makeTestPod("pod", 2)
   200  	if err := store.Update(pod2); err != nil {
   201  		t.Errorf("unexpected error: %v", err)
   202  	}
   203  	if item, ok, _ := store.Get(pod2); !ok {
   204  		t.Errorf("didn't find pod")
   205  	} else {
   206  		expected := makeTestStoreElement(makeTestPod("pod", 2))
   207  		if !apiequality.Semantic.DeepEqual(expected, item) {
   208  			t.Errorf("expected %v, got %v", expected, item)
   209  		}
   210  	}
   211  	pod3 := makeTestPod("pod", 3)
   212  	if err := store.Delete(pod3); err != nil {
   213  		t.Errorf("unexpected error: %v", err)
   214  	}
   215  	if _, ok, _ := store.Get(pod3); ok {
   216  		t.Errorf("found pod")
   217  	}
   218  
   219  	// Test List.
   220  	store.Add(makeTestPod("pod1", 4))
   221  	store.Add(makeTestPod("pod2", 5))
   222  	store.Add(makeTestPod("pod3", 6))
   223  	{
   224  		expected := map[string]storeElement{
   225  			"prefix/ns/pod1": *makeTestStoreElement(makeTestPod("pod1", 4)),
   226  			"prefix/ns/pod2": *makeTestStoreElement(makeTestPod("pod2", 5)),
   227  			"prefix/ns/pod3": *makeTestStoreElement(makeTestPod("pod3", 6)),
   228  		}
   229  		items := make(map[string]storeElement)
   230  		for _, item := range store.List() {
   231  			elem := item.(*storeElement)
   232  			items[elem.Key] = *elem
   233  		}
   234  		if !apiequality.Semantic.DeepEqual(expected, items) {
   235  			t.Errorf("expected %v, got %v", expected, items)
   236  		}
   237  	}
   238  
   239  	// Test Replace.
   240  	store.Replace([]interface{}{
   241  		makeTestPod("pod4", 7),
   242  		makeTestPod("pod5", 8),
   243  	}, "8")
   244  	{
   245  		expected := map[string]storeElement{
   246  			"prefix/ns/pod4": *makeTestStoreElement(makeTestPod("pod4", 7)),
   247  			"prefix/ns/pod5": *makeTestStoreElement(makeTestPod("pod5", 8)),
   248  		}
   249  		items := make(map[string]storeElement)
   250  		for _, item := range store.List() {
   251  			elem := item.(*storeElement)
   252  			items[elem.Key] = *elem
   253  		}
   254  		if !apiequality.Semantic.DeepEqual(expected, items) {
   255  			t.Errorf("expected %v, got %v", expected, items)
   256  		}
   257  	}
   258  }
   259  
   260  func TestEvents(t *testing.T) {
   261  	store := newTestWatchCache(5, &cache.Indexers{})
   262  	defer store.Stop()
   263  
   264  	// no dynamic-size cache to fit old tests.
   265  	store.lowerBoundCapacity = 5
   266  	store.upperBoundCapacity = 5
   267  
   268  	store.Add(makeTestPod("pod", 3))
   269  
   270  	// Test for Added event.
   271  	{
   272  		_, err := store.getAllEventsSince(1)
   273  		if err == nil {
   274  			t.Errorf("expected error too old")
   275  		}
   276  		if _, ok := err.(*errors.StatusError); !ok {
   277  			t.Errorf("expected error to be of type StatusError")
   278  		}
   279  	}
   280  	{
   281  		result, err := store.getAllEventsSince(2)
   282  		if err != nil {
   283  			t.Errorf("unexpected error: %v", err)
   284  		}
   285  		if len(result) != 1 {
   286  			t.Fatalf("unexpected events: %v", result)
   287  		}
   288  		if result[0].Type != watch.Added {
   289  			t.Errorf("unexpected event type: %v", result[0].Type)
   290  		}
   291  		pod := makeTestPod("pod", uint64(3))
   292  		if !apiequality.Semantic.DeepEqual(pod, result[0].Object) {
   293  			t.Errorf("unexpected item: %v, expected: %v", result[0].Object, pod)
   294  		}
   295  		if result[0].PrevObject != nil {
   296  			t.Errorf("unexpected item: %v", result[0].PrevObject)
   297  		}
   298  	}
   299  
   300  	store.Update(makeTestPod("pod", 4))
   301  	store.Update(makeTestPod("pod", 5))
   302  
   303  	// Test with not full cache.
   304  	{
   305  		_, err := store.getAllEventsSince(1)
   306  		if err == nil {
   307  			t.Errorf("expected error too old")
   308  		}
   309  	}
   310  	{
   311  		result, err := store.getAllEventsSince(3)
   312  		if err != nil {
   313  			t.Errorf("unexpected error: %v", err)
   314  		}
   315  		if len(result) != 2 {
   316  			t.Fatalf("unexpected events: %v", result)
   317  		}
   318  		for i := 0; i < 2; i++ {
   319  			if result[i].Type != watch.Modified {
   320  				t.Errorf("unexpected event type: %v", result[i].Type)
   321  			}
   322  			pod := makeTestPod("pod", uint64(i+4))
   323  			if !apiequality.Semantic.DeepEqual(pod, result[i].Object) {
   324  				t.Errorf("unexpected item: %v, expected: %v", result[i].Object, pod)
   325  			}
   326  			prevPod := makeTestPod("pod", uint64(i+3))
   327  			if !apiequality.Semantic.DeepEqual(prevPod, result[i].PrevObject) {
   328  				t.Errorf("unexpected item: %v, expected: %v", result[i].PrevObject, prevPod)
   329  			}
   330  		}
   331  	}
   332  
   333  	for i := 6; i < 10; i++ {
   334  		store.Update(makeTestPod("pod", uint64(i)))
   335  	}
   336  
   337  	// Test with full cache - there should be elements from 5 to 9.
   338  	{
   339  		_, err := store.getAllEventsSince(3)
   340  		if err == nil {
   341  			t.Errorf("expected error too old")
   342  		}
   343  	}
   344  	{
   345  		result, err := store.getAllEventsSince(4)
   346  		if err != nil {
   347  			t.Errorf("unexpected error: %v", err)
   348  		}
   349  		if len(result) != 5 {
   350  			t.Fatalf("unexpected events: %v", result)
   351  		}
   352  		for i := 0; i < 5; i++ {
   353  			pod := makeTestPod("pod", uint64(i+5))
   354  			if !apiequality.Semantic.DeepEqual(pod, result[i].Object) {
   355  				t.Errorf("unexpected item: %v, expected: %v", result[i].Object, pod)
   356  			}
   357  		}
   358  	}
   359  
   360  	// Test for delete event.
   361  	store.Delete(makeTestPod("pod", uint64(10)))
   362  
   363  	{
   364  		result, err := store.getAllEventsSince(9)
   365  		if err != nil {
   366  			t.Errorf("unexpected error: %v", err)
   367  		}
   368  		if len(result) != 1 {
   369  			t.Fatalf("unexpected events: %v", result)
   370  		}
   371  		if result[0].Type != watch.Deleted {
   372  			t.Errorf("unexpected event type: %v", result[0].Type)
   373  		}
   374  		pod := makeTestPod("pod", uint64(10))
   375  		if !apiequality.Semantic.DeepEqual(pod, result[0].Object) {
   376  			t.Errorf("unexpected item: %v, expected: %v", result[0].Object, pod)
   377  		}
   378  		prevPod := makeTestPod("pod", uint64(9))
   379  		if !apiequality.Semantic.DeepEqual(prevPod, result[0].PrevObject) {
   380  			t.Errorf("unexpected item: %v, expected: %v", result[0].PrevObject, prevPod)
   381  		}
   382  	}
   383  }
   384  
   385  func TestMarker(t *testing.T) {
   386  	store := newTestWatchCache(3, &cache.Indexers{})
   387  	defer store.Stop()
   388  
   389  	// First thing that is called when propagated from storage is Replace.
   390  	store.Replace([]interface{}{
   391  		makeTestPod("pod1", 5),
   392  		makeTestPod("pod2", 9),
   393  	}, "9")
   394  
   395  	_, err := store.getAllEventsSince(8)
   396  	if err == nil || !strings.Contains(err.Error(), "too old resource version") {
   397  		t.Errorf("unexpected error: %v", err)
   398  	}
   399  	// Getting events from 8 should return no events,
   400  	// even though there is a marker there.
   401  	result, err := store.getAllEventsSince(9)
   402  	if err != nil {
   403  		t.Fatalf("unexpected error: %v", err)
   404  	}
   405  	if len(result) != 0 {
   406  		t.Errorf("unexpected result: %#v, expected no events", result)
   407  	}
   408  
   409  	pod := makeTestPod("pods", 12)
   410  	store.Add(pod)
   411  	// Getting events from 8 should still work and return one event.
   412  	result, err = store.getAllEventsSince(9)
   413  	if err != nil {
   414  		t.Fatalf("unexpected error: %v", err)
   415  	}
   416  	if len(result) != 1 || !apiequality.Semantic.DeepEqual(result[0].Object, pod) {
   417  		t.Errorf("unexpected result: %#v, expected %v", result, pod)
   418  	}
   419  }
   420  
   421  func TestWaitUntilFreshAndList(t *testing.T) {
   422  	ctx := context.Background()
   423  	store := newTestWatchCache(3, &cache.Indexers{
   424  		"l:label": func(obj interface{}) ([]string, error) {
   425  			pod, ok := obj.(*v1.Pod)
   426  			if !ok {
   427  				return nil, fmt.Errorf("not a pod %#v", obj)
   428  			}
   429  			if value, ok := pod.Labels["label"]; ok {
   430  				return []string{value}, nil
   431  			}
   432  			return nil, nil
   433  		},
   434  		"f:spec.nodeName": func(obj interface{}) ([]string, error) {
   435  			pod, ok := obj.(*v1.Pod)
   436  			if !ok {
   437  				return nil, fmt.Errorf("not a pod %#v", obj)
   438  			}
   439  			return []string{pod.Spec.NodeName}, nil
   440  		},
   441  	})
   442  	defer store.Stop()
   443  	// In background, update the store.
   444  	go func() {
   445  		store.Add(makeTestPodDetails("pod1", 2, "node1", map[string]string{"label": "value1"}))
   446  		store.Add(makeTestPodDetails("pod2", 3, "node1", map[string]string{"label": "value1"}))
   447  		store.Add(makeTestPodDetails("pod3", 5, "node2", map[string]string{"label": "value2"}))
   448  	}()
   449  
   450  	// list by empty MatchValues.
   451  	list, resourceVersion, indexUsed, err := store.WaitUntilFreshAndList(ctx, 5, nil)
   452  	if err != nil {
   453  		t.Fatalf("unexpected error: %v", err)
   454  	}
   455  	if resourceVersion != 5 {
   456  		t.Errorf("unexpected resourceVersion: %v, expected: 5", resourceVersion)
   457  	}
   458  	if len(list) != 3 {
   459  		t.Errorf("unexpected list returned: %#v", list)
   460  	}
   461  	if indexUsed != "" {
   462  		t.Errorf("Used index %q but expected none to be used", indexUsed)
   463  	}
   464  
   465  	// list by label index.
   466  	matchValues := []storage.MatchValue{
   467  		{IndexName: "l:label", Value: "value1"},
   468  		{IndexName: "f:spec.nodeName", Value: "node2"},
   469  	}
   470  	list, resourceVersion, indexUsed, err = store.WaitUntilFreshAndList(ctx, 5, matchValues)
   471  	if err != nil {
   472  		t.Fatalf("unexpected error: %v", err)
   473  	}
   474  	if resourceVersion != 5 {
   475  		t.Errorf("unexpected resourceVersion: %v, expected: 5", resourceVersion)
   476  	}
   477  	if len(list) != 2 {
   478  		t.Errorf("unexpected list returned: %#v", list)
   479  	}
   480  	if indexUsed != "l:label" {
   481  		t.Errorf("Used index %q but expected %q", indexUsed, "l:label")
   482  	}
   483  
   484  	// list with spec.nodeName index.
   485  	matchValues = []storage.MatchValue{
   486  		{IndexName: "l:not-exist-label", Value: "whatever"},
   487  		{IndexName: "f:spec.nodeName", Value: "node2"},
   488  	}
   489  	list, resourceVersion, indexUsed, err = store.WaitUntilFreshAndList(ctx, 5, matchValues)
   490  	if err != nil {
   491  		t.Fatalf("unexpected error: %v", err)
   492  	}
   493  	if resourceVersion != 5 {
   494  		t.Errorf("unexpected resourceVersion: %v, expected: 5", resourceVersion)
   495  	}
   496  	if len(list) != 1 {
   497  		t.Errorf("unexpected list returned: %#v", list)
   498  	}
   499  	if indexUsed != "f:spec.nodeName" {
   500  		t.Errorf("Used index %q but expected %q", indexUsed, "f:spec.nodeName")
   501  	}
   502  
   503  	// list with index not exists.
   504  	matchValues = []storage.MatchValue{
   505  		{IndexName: "l:not-exist-label", Value: "whatever"},
   506  	}
   507  	list, resourceVersion, indexUsed, err = store.WaitUntilFreshAndList(ctx, 5, matchValues)
   508  	if err != nil {
   509  		t.Fatalf("unexpected error: %v", err)
   510  	}
   511  	if resourceVersion != 5 {
   512  		t.Errorf("unexpected resourceVersion: %v, expected: 5", resourceVersion)
   513  	}
   514  	if len(list) != 3 {
   515  		t.Errorf("unexpected list returned: %#v", list)
   516  	}
   517  	if indexUsed != "" {
   518  		t.Errorf("Used index %q but expected none to be used", indexUsed)
   519  	}
   520  }
   521  
   522  func TestWaitUntilFreshAndListFromCache(t *testing.T) {
   523  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ConsistentListFromCache, true)()
   524  	ctx := context.Background()
   525  	store := newTestWatchCache(3, &cache.Indexers{})
   526  	defer store.Stop()
   527  	// In background, update the store.
   528  	go func() {
   529  		store.Add(makeTestPod("pod1", 2))
   530  		store.bookmarkRevision <- 3
   531  	}()
   532  
   533  	// list from future revision. Requires watch cache to request bookmark to get it.
   534  	list, resourceVersion, indexUsed, err := store.WaitUntilFreshAndList(ctx, 3, nil)
   535  	if err != nil {
   536  		t.Fatalf("unexpected error: %v", err)
   537  	}
   538  	if resourceVersion != 3 {
   539  		t.Errorf("unexpected resourceVersion: %v, expected: 6", resourceVersion)
   540  	}
   541  	if len(list) != 1 {
   542  		t.Errorf("unexpected list returned: %#v", list)
   543  	}
   544  	if indexUsed != "" {
   545  		t.Errorf("Used index %q but expected none to be used", indexUsed)
   546  	}
   547  }
   548  
   549  func TestWaitUntilFreshAndGet(t *testing.T) {
   550  	ctx := context.Background()
   551  	store := newTestWatchCache(3, &cache.Indexers{})
   552  	defer store.Stop()
   553  
   554  	// In background, update the store.
   555  	go func() {
   556  		store.Add(makeTestPod("foo", 2))
   557  		store.Add(makeTestPod("bar", 5))
   558  	}()
   559  
   560  	obj, exists, resourceVersion, err := store.WaitUntilFreshAndGet(ctx, 5, "prefix/ns/bar")
   561  	if err != nil {
   562  		t.Fatalf("unexpected error: %v", err)
   563  	}
   564  	if resourceVersion != 5 {
   565  		t.Errorf("unexpected resourceVersion: %v, expected: 5", resourceVersion)
   566  	}
   567  	if !exists {
   568  		t.Fatalf("no results returned: %#v", obj)
   569  	}
   570  	expected := makeTestStoreElement(makeTestPod("bar", 5))
   571  	if !apiequality.Semantic.DeepEqual(expected, obj) {
   572  		t.Errorf("expected %v, got %v", expected, obj)
   573  	}
   574  }
   575  
   576  func TestWaitUntilFreshAndListTimeout(t *testing.T) {
   577  	tcs := []struct {
   578  		name                    string
   579  		ConsistentListFromCache bool
   580  	}{
   581  		{
   582  			name:                    "FromStorage",
   583  			ConsistentListFromCache: false,
   584  		},
   585  		{
   586  			name:                    "FromCache",
   587  			ConsistentListFromCache: true,
   588  		},
   589  	}
   590  	for _, tc := range tcs {
   591  		t.Run(tc.name, func(t *testing.T) {
   592  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ConsistentListFromCache, tc.ConsistentListFromCache)()
   593  			ctx := context.Background()
   594  			store := newTestWatchCache(3, &cache.Indexers{})
   595  			defer store.Stop()
   596  			fc := store.clock.(*testingclock.FakeClock)
   597  
   598  			// In background, step clock after the below call starts the timer.
   599  			go func() {
   600  				for !fc.HasWaiters() {
   601  					time.Sleep(time.Millisecond)
   602  				}
   603  				store.Add(makeTestPod("foo", 2))
   604  				store.bookmarkRevision <- 3
   605  				fc.Step(blockTimeout)
   606  
   607  				// Add an object to make sure the test would
   608  				// eventually fail instead of just waiting
   609  				// forever.
   610  				time.Sleep(30 * time.Second)
   611  				store.Add(makeTestPod("bar", 4))
   612  			}()
   613  
   614  			_, _, _, err := store.WaitUntilFreshAndList(ctx, 4, nil)
   615  			if !errors.IsTimeout(err) {
   616  				t.Errorf("expected timeout error but got: %v", err)
   617  			}
   618  			if !storage.IsTooLargeResourceVersion(err) {
   619  				t.Errorf("expected 'Too large resource version' cause in error but got: %v", err)
   620  			}
   621  		})
   622  	}
   623  }
   624  
   625  type testLW struct {
   626  	ListFunc  func(options metav1.ListOptions) (runtime.Object, error)
   627  	WatchFunc func(options metav1.ListOptions) (watch.Interface, error)
   628  }
   629  
   630  func (t *testLW) List(options metav1.ListOptions) (runtime.Object, error) {
   631  	return t.ListFunc(options)
   632  }
   633  func (t *testLW) Watch(options metav1.ListOptions) (watch.Interface, error) {
   634  	return t.WatchFunc(options)
   635  }
   636  
   637  func TestReflectorForWatchCache(t *testing.T) {
   638  	ctx := context.Background()
   639  	store := newTestWatchCache(5, &cache.Indexers{})
   640  	defer store.Stop()
   641  
   642  	{
   643  		_, version, _, err := store.WaitUntilFreshAndList(ctx, 0, nil)
   644  		if err != nil {
   645  			t.Fatalf("unexpected error: %v", err)
   646  		}
   647  		if version != 0 {
   648  			t.Errorf("unexpected resource version: %d", version)
   649  		}
   650  	}
   651  
   652  	lw := &testLW{
   653  		WatchFunc: func(_ metav1.ListOptions) (watch.Interface, error) {
   654  			fw := watch.NewFake()
   655  			go fw.Stop()
   656  			return fw, nil
   657  		},
   658  		ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
   659  			return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "10"}}, nil
   660  		},
   661  	}
   662  	r := cache.NewReflector(lw, &v1.Pod{}, store, 0)
   663  	r.ListAndWatch(wait.NeverStop)
   664  
   665  	{
   666  		_, version, _, err := store.WaitUntilFreshAndList(ctx, 10, nil)
   667  		if err != nil {
   668  			t.Fatalf("unexpected error: %v", err)
   669  		}
   670  		if version != 10 {
   671  			t.Errorf("unexpected resource version: %d", version)
   672  		}
   673  	}
   674  }
   675  
   676  func TestDynamicCache(t *testing.T) {
   677  	tests := []struct {
   678  		name          string
   679  		eventCount    int
   680  		cacheCapacity int
   681  		startIndex    int
   682  		// interval is time duration between adjacent events.
   683  		lowerBoundCapacity int
   684  		upperBoundCapacity int
   685  		interval           time.Duration
   686  		expectCapacity     int
   687  		expectStartIndex   int
   688  	}{
   689  		{
   690  			name:               "[capacity not equals 4*n] events inside eventFreshDuration cause cache expanding",
   691  			eventCount:         5,
   692  			cacheCapacity:      5,
   693  			lowerBoundCapacity: 5 / 2,
   694  			upperBoundCapacity: 5 * 2,
   695  			interval:           eventFreshDuration / 6,
   696  			expectCapacity:     10,
   697  			expectStartIndex:   0,
   698  		},
   699  		{
   700  			name:               "[capacity not equals 4*n] events outside eventFreshDuration without change cache capacity",
   701  			eventCount:         5,
   702  			cacheCapacity:      5,
   703  			lowerBoundCapacity: 5 / 2,
   704  			upperBoundCapacity: 5 * 2,
   705  			interval:           eventFreshDuration / 4,
   706  			expectCapacity:     5,
   707  			expectStartIndex:   0,
   708  		},
   709  		{
   710  			name:               "[capacity not equals 4*n] quarter of recent events outside eventFreshDuration cause cache shrinking",
   711  			eventCount:         5,
   712  			cacheCapacity:      5,
   713  			lowerBoundCapacity: 5 / 2,
   714  			upperBoundCapacity: 5 * 2,
   715  			interval:           eventFreshDuration + time.Second,
   716  			expectCapacity:     2,
   717  			expectStartIndex:   3,
   718  		},
   719  		{
   720  			name:               "[capacity not equals 4*n] quarter of recent events outside eventFreshDuration cause cache shrinking with given lowerBoundCapacity",
   721  			eventCount:         5,
   722  			cacheCapacity:      5,
   723  			lowerBoundCapacity: 3,
   724  			upperBoundCapacity: 5 * 2,
   725  			interval:           eventFreshDuration + time.Second,
   726  			expectCapacity:     3,
   727  			expectStartIndex:   2,
   728  		},
   729  		{
   730  			name:               "[capacity not equals 4*n] events inside eventFreshDuration cause cache expanding with given upperBoundCapacity",
   731  			eventCount:         5,
   732  			cacheCapacity:      5,
   733  			lowerBoundCapacity: 5 / 2,
   734  			upperBoundCapacity: 8,
   735  			interval:           eventFreshDuration / 6,
   736  			expectCapacity:     8,
   737  			expectStartIndex:   0,
   738  		},
   739  		{
   740  			name:               "[capacity not equals 4*n] [startIndex not equal 0] events inside eventFreshDuration cause cache expanding",
   741  			eventCount:         5,
   742  			cacheCapacity:      5,
   743  			startIndex:         3,
   744  			lowerBoundCapacity: 5 / 2,
   745  			upperBoundCapacity: 5 * 2,
   746  			interval:           eventFreshDuration / 6,
   747  			expectCapacity:     10,
   748  			expectStartIndex:   3,
   749  		},
   750  		{
   751  			name:               "[capacity not equals 4*n] [startIndex not equal 0] events outside eventFreshDuration without change cache capacity",
   752  			eventCount:         5,
   753  			cacheCapacity:      5,
   754  			startIndex:         3,
   755  			lowerBoundCapacity: 5 / 2,
   756  			upperBoundCapacity: 5 * 2,
   757  			interval:           eventFreshDuration / 4,
   758  			expectCapacity:     5,
   759  			expectStartIndex:   3,
   760  		},
   761  		{
   762  			name:               "[capacity not equals 4*n] [startIndex not equal 0] quarter of recent events outside eventFreshDuration cause cache shrinking",
   763  			eventCount:         5,
   764  			cacheCapacity:      5,
   765  			startIndex:         3,
   766  			lowerBoundCapacity: 5 / 2,
   767  			upperBoundCapacity: 5 * 2,
   768  			interval:           eventFreshDuration + time.Second,
   769  			expectCapacity:     2,
   770  			expectStartIndex:   6,
   771  		},
   772  		{
   773  			name:               "[capacity not equals 4*n] [startIndex not equal 0] quarter of recent events outside eventFreshDuration cause cache shrinking with given lowerBoundCapacity",
   774  			eventCount:         5,
   775  			cacheCapacity:      5,
   776  			startIndex:         3,
   777  			lowerBoundCapacity: 3,
   778  			upperBoundCapacity: 5 * 2,
   779  			interval:           eventFreshDuration + time.Second,
   780  			expectCapacity:     3,
   781  			expectStartIndex:   5,
   782  		},
   783  		{
   784  			name:               "[capacity not equals 4*n] [startIndex not equal 0] events inside eventFreshDuration cause cache expanding with given upperBoundCapacity",
   785  			eventCount:         5,
   786  			cacheCapacity:      5,
   787  			startIndex:         3,
   788  			lowerBoundCapacity: 5 / 2,
   789  			upperBoundCapacity: 8,
   790  			interval:           eventFreshDuration / 6,
   791  			expectCapacity:     8,
   792  			expectStartIndex:   3,
   793  		},
   794  		{
   795  			name:               "[capacity equals 4*n] events inside eventFreshDuration cause cache expanding",
   796  			eventCount:         8,
   797  			cacheCapacity:      8,
   798  			lowerBoundCapacity: 8 / 2,
   799  			upperBoundCapacity: 8 * 2,
   800  			interval:           eventFreshDuration / 9,
   801  			expectCapacity:     16,
   802  			expectStartIndex:   0,
   803  		},
   804  		{
   805  			name:               "[capacity equals 4*n] events outside eventFreshDuration without change cache capacity",
   806  			eventCount:         8,
   807  			cacheCapacity:      8,
   808  			lowerBoundCapacity: 8 / 2,
   809  			upperBoundCapacity: 8 * 2,
   810  			interval:           eventFreshDuration / 8,
   811  			expectCapacity:     8,
   812  			expectStartIndex:   0,
   813  		},
   814  		{
   815  			name:               "[capacity equals 4*n] quarter of recent events outside eventFreshDuration cause cache shrinking",
   816  			eventCount:         8,
   817  			cacheCapacity:      8,
   818  			lowerBoundCapacity: 8 / 2,
   819  			upperBoundCapacity: 8 * 2,
   820  			interval:           eventFreshDuration/2 + time.Second,
   821  			expectCapacity:     4,
   822  			expectStartIndex:   4,
   823  		},
   824  		{
   825  			name:               "[capacity equals 4*n] quarter of recent events outside eventFreshDuration cause cache shrinking with given lowerBoundCapacity",
   826  			eventCount:         8,
   827  			cacheCapacity:      8,
   828  			lowerBoundCapacity: 7,
   829  			upperBoundCapacity: 8 * 2,
   830  			interval:           eventFreshDuration/2 + time.Second,
   831  			expectCapacity:     7,
   832  			expectStartIndex:   1,
   833  		},
   834  		{
   835  			name:               "[capacity equals 4*n] events inside eventFreshDuration cause cache expanding with given upperBoundCapacity",
   836  			eventCount:         8,
   837  			cacheCapacity:      8,
   838  			lowerBoundCapacity: 8 / 2,
   839  			upperBoundCapacity: 10,
   840  			interval:           eventFreshDuration / 9,
   841  			expectCapacity:     10,
   842  			expectStartIndex:   0,
   843  		},
   844  		{
   845  			name:               "[capacity equals 4*n] [startIndex not equal 0] events inside eventFreshDuration cause cache expanding",
   846  			eventCount:         8,
   847  			cacheCapacity:      8,
   848  			startIndex:         3,
   849  			lowerBoundCapacity: 8 / 2,
   850  			upperBoundCapacity: 8 * 2,
   851  			interval:           eventFreshDuration / 9,
   852  			expectCapacity:     16,
   853  			expectStartIndex:   3,
   854  		},
   855  		{
   856  			name:               "[capacity equals 4*n] [startIndex not equal 0] events outside eventFreshDuration without change cache capacity",
   857  			eventCount:         8,
   858  			cacheCapacity:      8,
   859  			startIndex:         3,
   860  			lowerBoundCapacity: 8 / 2,
   861  			upperBoundCapacity: 8 * 2,
   862  			interval:           eventFreshDuration / 8,
   863  			expectCapacity:     8,
   864  			expectStartIndex:   3,
   865  		},
   866  		{
   867  			name:               "[capacity equals 4*n] [startIndex not equal 0] quarter of recent events outside eventFreshDuration cause cache shrinking",
   868  			eventCount:         8,
   869  			cacheCapacity:      8,
   870  			startIndex:         3,
   871  			lowerBoundCapacity: 8 / 2,
   872  			upperBoundCapacity: 8 * 2,
   873  			interval:           eventFreshDuration/2 + time.Second,
   874  			expectCapacity:     4,
   875  			expectStartIndex:   7,
   876  		},
   877  		{
   878  			name:               "[capacity equals 4*n] [startIndex not equal 0] quarter of recent events outside eventFreshDuration cause cache shrinking with given lowerBoundCapacity",
   879  			eventCount:         8,
   880  			cacheCapacity:      8,
   881  			startIndex:         3,
   882  			lowerBoundCapacity: 7,
   883  			upperBoundCapacity: 8 * 2,
   884  			interval:           eventFreshDuration/2 + time.Second,
   885  			expectCapacity:     7,
   886  			expectStartIndex:   4,
   887  		},
   888  		{
   889  			name:               "[capacity equals 4*n] [startIndex not equal 0] events inside eventFreshDuration cause cache expanding with given upperBoundCapacity",
   890  			eventCount:         8,
   891  			cacheCapacity:      8,
   892  			startIndex:         3,
   893  			lowerBoundCapacity: 8 / 2,
   894  			upperBoundCapacity: 10,
   895  			interval:           eventFreshDuration / 9,
   896  			expectCapacity:     10,
   897  			expectStartIndex:   3,
   898  		},
   899  	}
   900  
   901  	for _, test := range tests {
   902  		t.Run(test.name, func(t *testing.T) {
   903  			store := newTestWatchCache(test.cacheCapacity, &cache.Indexers{})
   904  			defer store.Stop()
   905  			store.cache = make([]*watchCacheEvent, test.cacheCapacity)
   906  			store.startIndex = test.startIndex
   907  			store.lowerBoundCapacity = test.lowerBoundCapacity
   908  			store.upperBoundCapacity = test.upperBoundCapacity
   909  			loadEventWithDuration(store, test.eventCount, test.interval)
   910  			nextInterval := store.clock.Now().Add(time.Duration(test.interval.Nanoseconds() * int64(test.eventCount)))
   911  			store.resizeCacheLocked(nextInterval)
   912  			if store.capacity != test.expectCapacity {
   913  				t.Errorf("expect capacity %d, but get %d", test.expectCapacity, store.capacity)
   914  			}
   915  
   916  			// check cache's startIndex, endIndex and all elements.
   917  			if store.startIndex != test.expectStartIndex {
   918  				t.Errorf("expect startIndex %d, but get %d", test.expectStartIndex, store.startIndex)
   919  			}
   920  			if store.endIndex != test.startIndex+test.eventCount {
   921  				t.Errorf("expect endIndex %d get %d", test.startIndex+test.eventCount, store.endIndex)
   922  			}
   923  			if !checkCacheElements(store) {
   924  				t.Errorf("some elements locations in cache is wrong")
   925  			}
   926  		})
   927  	}
   928  }
   929  
   930  func loadEventWithDuration(cache *testWatchCache, count int, interval time.Duration) {
   931  	for i := 0; i < count; i++ {
   932  		event := &watchCacheEvent{
   933  			Key:        fmt.Sprintf("event-%d", i+cache.startIndex),
   934  			RecordTime: cache.clock.Now().Add(time.Duration(interval.Nanoseconds() * int64(i))),
   935  		}
   936  		cache.cache[(i+cache.startIndex)%cache.capacity] = event
   937  	}
   938  	cache.endIndex = cache.startIndex + count
   939  }
   940  
   941  func checkCacheElements(cache *testWatchCache) bool {
   942  	for i := cache.startIndex; i < cache.endIndex; i++ {
   943  		location := i % cache.capacity
   944  		if cache.cache[location].Key != fmt.Sprintf("event-%d", i) {
   945  			return false
   946  		}
   947  	}
   948  	return true
   949  }
   950  
   951  func TestCacheIncreaseDoesNotBreakWatch(t *testing.T) {
   952  	store := newTestWatchCache(2, &cache.Indexers{})
   953  	defer store.Stop()
   954  
   955  	now := store.clock.Now()
   956  	addEvent := func(key string, rv uint64, t time.Time) {
   957  		event := &watchCacheEvent{
   958  			Key:             key,
   959  			ResourceVersion: rv,
   960  			RecordTime:      t,
   961  		}
   962  		store.updateCache(event)
   963  	}
   964  
   965  	// Initial LIST comes from the moment of RV=10.
   966  	store.Replace(nil, "10")
   967  
   968  	addEvent("key1", 20, now)
   969  
   970  	// Force "key1" to rotate our of cache.
   971  	later := now.Add(2 * eventFreshDuration)
   972  	addEvent("key2", 30, later)
   973  	addEvent("key3", 40, later)
   974  
   975  	// Force cache resize.
   976  	addEvent("key4", 50, later.Add(time.Second))
   977  
   978  	_, err := store.getAllEventsSince(15)
   979  	if err == nil || !strings.Contains(err.Error(), "too old resource version") {
   980  		t.Errorf("unexpected error: %v", err)
   981  	}
   982  }
   983  
   984  func TestSuggestedWatchChannelSize(t *testing.T) {
   985  	testCases := []struct {
   986  		name        string
   987  		capacity    int
   988  		indexExists bool
   989  		triggerUsed bool
   990  		expected    int
   991  	}{
   992  		{
   993  			name:        "capacity=100, indexExists, triggerUsed",
   994  			capacity:    100,
   995  			indexExists: true,
   996  			triggerUsed: true,
   997  			expected:    10,
   998  		},
   999  		{
  1000  			name:        "capacity=100, indexExists, !triggerUsed",
  1001  			capacity:    100,
  1002  			indexExists: true,
  1003  			triggerUsed: false,
  1004  			expected:    10,
  1005  		},
  1006  		{
  1007  			name:        "capacity=100, !indexExists",
  1008  			capacity:    100,
  1009  			indexExists: false,
  1010  			triggerUsed: false,
  1011  			expected:    10,
  1012  		},
  1013  		{
  1014  			name:        "capacity=750, indexExists, triggerUsed",
  1015  			capacity:    750,
  1016  			indexExists: true,
  1017  			triggerUsed: true,
  1018  			expected:    10,
  1019  		},
  1020  		{
  1021  			name:        "capacity=750, indexExists, !triggerUsed",
  1022  			capacity:    750,
  1023  			indexExists: true,
  1024  			triggerUsed: false,
  1025  			expected:    10,
  1026  		},
  1027  		{
  1028  			name:        "capacity=750, !indexExists",
  1029  			capacity:    750,
  1030  			indexExists: false,
  1031  			triggerUsed: false,
  1032  			expected:    10,
  1033  		},
  1034  		{
  1035  			name:        "capacity=7500, indexExists, triggerUsed",
  1036  			capacity:    7500,
  1037  			indexExists: true,
  1038  			triggerUsed: true,
  1039  			expected:    10,
  1040  		},
  1041  		{
  1042  			name:        "capacity=7500, indexExists, !triggerUsed",
  1043  			capacity:    7500,
  1044  			indexExists: true,
  1045  			triggerUsed: false,
  1046  			expected:    100,
  1047  		},
  1048  		{
  1049  			name:        "capacity=7500, !indexExists",
  1050  			capacity:    7500,
  1051  			indexExists: false,
  1052  			triggerUsed: false,
  1053  			expected:    100,
  1054  		},
  1055  		{
  1056  			name:        "capacity=75000, indexExists, triggerUsed",
  1057  			capacity:    75000,
  1058  			indexExists: true,
  1059  			triggerUsed: true,
  1060  			expected:    10,
  1061  		},
  1062  		{
  1063  			name:        "capacity=75000, indexExists, !triggerUsed",
  1064  			capacity:    75000,
  1065  			indexExists: true,
  1066  			triggerUsed: false,
  1067  			expected:    1000,
  1068  		},
  1069  		{
  1070  			name:        "capacity=75000, !indexExists",
  1071  			capacity:    75000,
  1072  			indexExists: false,
  1073  			triggerUsed: false,
  1074  			expected:    100,
  1075  		},
  1076  		{
  1077  			name:        "capacity=750000, indexExists, triggerUsed",
  1078  			capacity:    750000,
  1079  			indexExists: true,
  1080  			triggerUsed: true,
  1081  			expected:    10,
  1082  		},
  1083  		{
  1084  			name:        "capacity=750000, indexExists, !triggerUsed",
  1085  			capacity:    750000,
  1086  			indexExists: true,
  1087  			triggerUsed: false,
  1088  			expected:    1000,
  1089  		},
  1090  		{
  1091  			name:        "capacity=750000, !indexExists",
  1092  			capacity:    750000,
  1093  			indexExists: false,
  1094  			triggerUsed: false,
  1095  			expected:    100,
  1096  		},
  1097  	}
  1098  
  1099  	for _, test := range testCases {
  1100  		t.Run(test.name, func(t *testing.T) {
  1101  			store := newTestWatchCache(test.capacity, &cache.Indexers{})
  1102  			defer store.Stop()
  1103  			got := store.suggestedWatchChannelSize(test.indexExists, test.triggerUsed)
  1104  			if got != test.expected {
  1105  				t.Errorf("unexpected channel size got: %v, expected: %v", got, test.expected)
  1106  			}
  1107  		})
  1108  	}
  1109  }
  1110  
  1111  func BenchmarkWatchCache_updateCache(b *testing.B) {
  1112  	store := newTestWatchCache(defaultUpperBoundCapacity, &cache.Indexers{})
  1113  	defer store.Stop()
  1114  	store.cache = store.cache[:0]
  1115  	store.upperBoundCapacity = defaultUpperBoundCapacity
  1116  	loadEventWithDuration(store, defaultUpperBoundCapacity, 0)
  1117  	add := &watchCacheEvent{
  1118  		Key:        fmt.Sprintf("event-%d", defaultUpperBoundCapacity),
  1119  		RecordTime: store.clock.Now(),
  1120  	}
  1121  	b.ResetTimer()
  1122  	for i := 0; i < b.N; i++ {
  1123  		store.updateCache(add)
  1124  	}
  1125  }