github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/backoff.go (about) 1 /* 2 Copyright 2015 The Kubernetes Authors All rights reserved. 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 util 18 19 import ( 20 "math" 21 "sync" 22 "time" 23 ) 24 25 type backoffEntry struct { 26 backoff time.Duration 27 lastUpdate time.Time 28 } 29 30 type Backoff struct { 31 sync.Mutex 32 Clock Clock 33 defaultDuration time.Duration 34 maxDuration time.Duration 35 perItemBackoff map[string]*backoffEntry 36 } 37 38 func NewBackOff(initial, max time.Duration) *Backoff { 39 return &Backoff{ 40 perItemBackoff: map[string]*backoffEntry{}, 41 Clock: RealClock{}, 42 defaultDuration: initial, 43 maxDuration: max, 44 } 45 } 46 47 // Get the current backoff Duration 48 func (p *Backoff) Get(id string) time.Duration { 49 p.Lock() 50 defer p.Unlock() 51 var delay time.Duration 52 entry, ok := p.perItemBackoff[id] 53 if ok { 54 delay = entry.backoff 55 } 56 return delay 57 } 58 59 // move backoff to the next mark, capping at maxDuration 60 func (p *Backoff) Next(id string, eventTime time.Time) { 61 p.Lock() 62 defer p.Unlock() 63 entry, ok := p.perItemBackoff[id] 64 if !ok || hasExpired(eventTime, entry.lastUpdate, p.maxDuration) { 65 entry = p.initEntryUnsafe(id) 66 } else { 67 delay := entry.backoff * 2 // exponential 68 entry.backoff = time.Duration(math.Min(float64(delay), float64(p.maxDuration))) 69 } 70 entry.lastUpdate = p.Clock.Now() 71 } 72 73 // Returns True if the elapsed time since eventTime is smaller than the current backoff window 74 func (p *Backoff) IsInBackOffSince(id string, eventTime time.Time) bool { 75 p.Lock() 76 defer p.Unlock() 77 entry, ok := p.perItemBackoff[id] 78 if !ok { 79 return false 80 } 81 if hasExpired(eventTime, entry.lastUpdate, p.maxDuration) { 82 return false 83 } 84 return p.Clock.Now().Sub(eventTime) < entry.backoff 85 } 86 87 // Returns True if time since lastupdate is less than the current backoff window. 88 func (p *Backoff) IsInBackOffSinceUpdate(id string, eventTime time.Time) bool { 89 p.Lock() 90 defer p.Unlock() 91 entry, ok := p.perItemBackoff[id] 92 if !ok { 93 return false 94 } 95 if hasExpired(eventTime, entry.lastUpdate, p.maxDuration) { 96 return false 97 } 98 return eventTime.Sub(entry.lastUpdate) < entry.backoff 99 } 100 101 // Garbage collect records that have aged past maxDuration. Backoff users are expected 102 // to invoke this periodically. 103 func (p *Backoff) GC() { 104 p.Lock() 105 defer p.Unlock() 106 now := p.Clock.Now() 107 for id, entry := range p.perItemBackoff { 108 if now.Sub(entry.lastUpdate) > p.maxDuration*2 { 109 // GC when entry has not been updated for 2*maxDuration 110 delete(p.perItemBackoff, id) 111 } 112 } 113 } 114 115 // Take a lock on *Backoff, before calling initEntryUnsafe 116 func (p *Backoff) initEntryUnsafe(id string) *backoffEntry { 117 entry := &backoffEntry{backoff: p.defaultDuration} 118 p.perItemBackoff[id] = entry 119 return entry 120 } 121 122 // After 2*maxDuration we restart the backoff factor to the begining 123 func hasExpired(eventTime time.Time, lastUpdate time.Time, maxDuration time.Duration) bool { 124 return eventTime.Sub(lastUpdate) > maxDuration*2 // consider stable if it's ok for twice the maxDuration 125 }