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 }