k8s.io/apiserver@v0.31.1/pkg/storage/cacher/watch_progress_test.go (about)

     1  /*
     2  Copyright 2023 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  	"sync/atomic"
    22  	"testing"
    23  	"time"
    24  
    25  	"k8s.io/apimachinery/pkg/util/wait"
    26  
    27  	"k8s.io/klog/v2"
    28  	"k8s.io/klog/v2/ktesting"
    29  	"k8s.io/utils/clock"
    30  	testingclock "k8s.io/utils/clock/testing"
    31  )
    32  
    33  var (
    34  	pollPeriod      = time.Millisecond
    35  	minimalNoChange = 20 * time.Millisecond
    36  	pollTimeout     = 5 * time.Second
    37  )
    38  
    39  func TestConditionalProgressRequester(t *testing.T) {
    40  	_, ctx := ktesting.NewTestContext(t)
    41  	logger := klog.FromContext(ctx)
    42  
    43  	clock := testingclock.NewFakeClock(time.Now())
    44  	pr := newTestConditionalProgressRequester(clock)
    45  	stopCh := make(chan struct{})
    46  	go pr.Run(stopCh)
    47  	var wantRequestsSent int32
    48  	var requestsSent int32
    49  
    50  	logger.Info("Wait for ticker to be created")
    51  	for !clock.HasWaiters() {
    52  		time.Sleep(pollPeriod)
    53  	}
    54  
    55  	logger.Info("No progress requests if no-one is waiting")
    56  	clock.Step(progressRequestPeriod * 2)
    57  
    58  	if err := pollConditionNoChange(pollPeriod, minimalNoChange, pollTimeout, func() bool {
    59  		requestsSent = pr.progressRequestsSentCount.Load()
    60  		return requestsSent == wantRequestsSent
    61  	}); err != nil {
    62  		t.Fatalf("Failed to wait progress requests, err: %s, want: %d , got %d", err, wantRequestsSent, requestsSent)
    63  	}
    64  
    65  	logger.Info("Adding waiters allows progress request to be sent")
    66  	pr.Add()
    67  	wantRequestsSent++
    68  	if err := pollConditionNoChange(pollPeriod, minimalNoChange, pollTimeout, func() bool {
    69  		requestsSent = pr.progressRequestsSentCount.Load()
    70  		return requestsSent == wantRequestsSent
    71  	}); err != nil {
    72  		t.Fatalf("Failed to wait progress requests, err: %s, want: %d , got %d", err, wantRequestsSent, requestsSent)
    73  	}
    74  
    75  	logger.Info("Periodically request progress to be sent every period")
    76  	for wantRequestsSent < 10 {
    77  		clock.Step(progressRequestPeriod)
    78  		wantRequestsSent++
    79  
    80  		if err := pollConditionNoChange(pollPeriod, minimalNoChange, pollTimeout, func() bool {
    81  			requestsSent = pr.progressRequestsSentCount.Load()
    82  			return requestsSent == wantRequestsSent
    83  		}); err != nil {
    84  			t.Fatalf("Failed to wait progress requests, err: %s, want: %d , got %d", err, wantRequestsSent, requestsSent)
    85  		}
    86  	}
    87  	pr.Remove()
    88  
    89  	logger.Info("No progress requests if no-one is waiting")
    90  	clock.Step(progressRequestPeriod * 2)
    91  	if err := pollConditionNoChange(pollPeriod, minimalNoChange, pollTimeout, func() bool {
    92  		requestsSent = pr.progressRequestsSentCount.Load()
    93  		return requestsSent == wantRequestsSent
    94  	}); err != nil {
    95  		t.Fatalf("Failed to wait progress requests, err: %s, want: %d , got %d", err, wantRequestsSent, requestsSent)
    96  	}
    97  
    98  	logger.Info("No progress after stopping")
    99  	close(stopCh)
   100  	if err := pollConditionNoChange(pollPeriod, minimalNoChange, pollTimeout, func() bool {
   101  		requestsSent = pr.progressRequestsSentCount.Load()
   102  		return requestsSent == wantRequestsSent
   103  	}); err != nil {
   104  		t.Fatalf("Failed to wait progress requests, err: %s, want: %d , got %d", err, wantRequestsSent, requestsSent)
   105  	}
   106  	pr.Add()
   107  	clock.Step(progressRequestPeriod * 2)
   108  	if err := pollConditionNoChange(pollPeriod, minimalNoChange, pollTimeout, func() bool {
   109  		requestsSent = pr.progressRequestsSentCount.Load()
   110  		return requestsSent == wantRequestsSent
   111  	}); err != nil {
   112  		t.Fatalf("Failed to wait progress requests, err: %s, want: %d , got %d", err, wantRequestsSent, requestsSent)
   113  	}
   114  }
   115  
   116  func newTestConditionalProgressRequester(clock clock.WithTicker) *testConditionalProgressRequester {
   117  	pr := &testConditionalProgressRequester{}
   118  	pr.conditionalProgressRequester = newConditionalProgressRequester(pr.RequestWatchProgress, clock, nil)
   119  	return pr
   120  }
   121  
   122  type testConditionalProgressRequester struct {
   123  	*conditionalProgressRequester
   124  	progressRequestsSentCount atomic.Int32
   125  }
   126  
   127  func (pr *testConditionalProgressRequester) RequestWatchProgress(ctx context.Context) error {
   128  	pr.progressRequestsSentCount.Add(1)
   129  	return nil
   130  }
   131  
   132  func pollConditionNoChange(interval, stable, timeout time.Duration, condition func() bool) error {
   133  	passCounter := 0
   134  	requiredNumberOfPasses := int(stable/interval) + 1
   135  	return wait.Poll(interval, timeout, func() (done bool, err error) {
   136  		if condition() {
   137  			passCounter++
   138  		} else {
   139  			passCounter = 0
   140  		}
   141  		return passCounter >= requiredNumberOfPasses, nil
   142  	})
   143  }