k8s.io/apiserver@v0.29.3/pkg/storage/cacher/watch_progress.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"
    22  	"time"
    23  
    24  	"google.golang.org/grpc/metadata"
    25  
    26  	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
    27  	"k8s.io/apimachinery/pkg/util/wait"
    28  
    29  	"k8s.io/klog/v2"
    30  	"k8s.io/utils/clock"
    31  )
    32  
    33  const (
    34  	// progressRequestPeriod determines period of requesting progress
    35  	// from etcd when there is a request waiting for watch cache to be fresh.
    36  	progressRequestPeriod = 100 * time.Millisecond
    37  )
    38  
    39  func newConditionalProgressRequester(requestWatchProgress WatchProgressRequester, clock TickerFactory, contextMetadata metadata.MD) *conditionalProgressRequester {
    40  	pr := &conditionalProgressRequester{
    41  		clock:                clock,
    42  		requestWatchProgress: requestWatchProgress,
    43  		contextMetadata:      contextMetadata,
    44  	}
    45  	pr.cond = sync.NewCond(pr.mux.RLocker())
    46  	return pr
    47  }
    48  
    49  type WatchProgressRequester func(ctx context.Context) error
    50  
    51  type TickerFactory interface {
    52  	NewTicker(time.Duration) clock.Ticker
    53  }
    54  
    55  // conditionalProgressRequester will request progress notification if there
    56  // is a request waiting for watch cache to be fresh.
    57  type conditionalProgressRequester struct {
    58  	clock                TickerFactory
    59  	requestWatchProgress WatchProgressRequester
    60  	contextMetadata      metadata.MD
    61  
    62  	mux     sync.RWMutex
    63  	cond    *sync.Cond
    64  	waiting int
    65  	stopped bool
    66  }
    67  
    68  func (pr *conditionalProgressRequester) Run(stopCh <-chan struct{}) {
    69  	ctx := wait.ContextForChannel(stopCh)
    70  	if pr.contextMetadata != nil {
    71  		ctx = metadata.NewOutgoingContext(ctx, pr.contextMetadata)
    72  	}
    73  	go func() {
    74  		defer utilruntime.HandleCrash()
    75  		<-stopCh
    76  		pr.mux.Lock()
    77  		defer pr.mux.Unlock()
    78  		pr.stopped = true
    79  		pr.cond.Signal()
    80  	}()
    81  	ticker := pr.clock.NewTicker(progressRequestPeriod)
    82  	defer ticker.Stop()
    83  	for {
    84  		stopped := func() bool {
    85  			pr.mux.RLock()
    86  			defer pr.mux.RUnlock()
    87  			for pr.waiting == 0 && !pr.stopped {
    88  				pr.cond.Wait()
    89  			}
    90  			return pr.stopped
    91  		}()
    92  		if stopped {
    93  			return
    94  		}
    95  
    96  		select {
    97  		case <-ticker.C():
    98  			shouldRequest := func() bool {
    99  				pr.mux.RLock()
   100  				defer pr.mux.RUnlock()
   101  				return pr.waiting > 0 && !pr.stopped
   102  			}()
   103  			if !shouldRequest {
   104  				continue
   105  			}
   106  			err := pr.requestWatchProgress(ctx)
   107  			if err != nil {
   108  				klog.V(4).InfoS("Error requesting bookmark", "err", err)
   109  			}
   110  		case <-stopCh:
   111  			return
   112  		}
   113  	}
   114  }
   115  
   116  func (pr *conditionalProgressRequester) Add() {
   117  	pr.mux.Lock()
   118  	defer pr.mux.Unlock()
   119  	pr.waiting += 1
   120  	pr.cond.Signal()
   121  }
   122  
   123  func (pr *conditionalProgressRequester) Remove() {
   124  	pr.mux.Lock()
   125  	defer pr.mux.Unlock()
   126  	pr.waiting -= 1
   127  	pr.cond.Signal()
   128  }