k8s.io/apiserver@v0.31.1/pkg/storage/cacher/time_budget.go (about) 1 /* 2 Copyright 2016 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 "sync" 21 "time" 22 23 "k8s.io/utils/clock" 24 ) 25 26 const ( 27 refreshPerSecond = 50 * time.Millisecond 28 maxBudget = 100 * time.Millisecond 29 ) 30 31 // timeBudget implements a budget of time that you can use and is 32 // periodically being refreshed. The pattern to use it is: 33 // 34 // budget := newTimeBudget(...) 35 // ... 36 // timeout := budget.takeAvailable() 37 // // Now you can spend at most timeout on doing stuff 38 // ... 39 // // If you didn't use all timeout, return what you didn't use 40 // budget.returnUnused(<unused part of timeout>) 41 // 42 // NOTE: It's not recommended to be used concurrently from multiple threads - 43 // if first user takes the whole timeout, the second one will get 0 timeout 44 // even though the first one may return something later. 45 type timeBudget interface { 46 takeAvailable() time.Duration 47 returnUnused(unused time.Duration) 48 } 49 50 type timeBudgetImpl struct { 51 sync.Mutex 52 clock clock.Clock 53 budget time.Duration 54 maxBudget time.Duration 55 refresh time.Duration 56 // last store last access time 57 last time.Time 58 } 59 60 func newTimeBudget() timeBudget { 61 result := &timeBudgetImpl{ 62 clock: clock.RealClock{}, 63 budget: time.Duration(0), 64 refresh: refreshPerSecond, 65 maxBudget: maxBudget, 66 } 67 result.last = result.clock.Now() 68 return result 69 } 70 71 func (t *timeBudgetImpl) takeAvailable() time.Duration { 72 t.Lock() 73 defer t.Unlock() 74 // budget accumulated since last access 75 now := t.clock.Now() 76 acc := now.Sub(t.last).Seconds() * t.refresh.Seconds() 77 if acc < 0 { 78 acc = 0 79 } 80 // update current budget and store the current time 81 if t.budget = t.budget + time.Duration(acc*1e9); t.budget > t.maxBudget { 82 t.budget = t.maxBudget 83 } 84 t.last = now 85 result := t.budget 86 t.budget = time.Duration(0) 87 return result 88 } 89 90 func (t *timeBudgetImpl) returnUnused(unused time.Duration) { 91 t.Lock() 92 defer t.Unlock() 93 if unused < 0 { 94 // We used more than allowed. 95 return 96 } 97 // add the unused time directly to the budget 98 // takeAvailable() will take into account the elapsed time 99 if t.budget = t.budget + unused; t.budget > t.maxBudget { 100 t.budget = t.maxBudget 101 } 102 }