github.com/weaviate/weaviate@v1.24.6/entities/cyclemanager/ticker.go (about) 1 // _ _ 2 // __ _____ __ ___ ___ __ _| |_ ___ 3 // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \ 4 // \ V V / __/ (_| |\ V /| | (_| | || __/ 5 // \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___| 6 // 7 // Copyright © 2016 - 2024 Weaviate B.V. All rights reserved. 8 // 9 // CONTACT: hello@weaviate.io 10 // 11 12 package cyclemanager 13 14 import ( 15 "time" 16 ) 17 18 // ===== Tickers ===== 19 20 type CycleTicker interface { 21 Start() 22 Stop() 23 C() <-chan time.Time 24 // called with bool value whenever cycle function finished execution 25 // true - indicates cycle function actually did some processing 26 // false - cycle function returned without doing anything 27 CycleExecuted(executed bool) 28 } 29 30 type cycleTicker struct { 31 intervals CycleIntervals 32 ticker *time.Ticker 33 } 34 35 func newCycleTicker(intervals CycleIntervals) CycleTicker { 36 if intervals == nil { 37 return NewNoopTicker() 38 } 39 ticker := time.NewTicker(time.Second) 40 ticker.Stop() 41 return &cycleTicker{ticker: ticker, intervals: intervals} 42 } 43 44 func (t *cycleTicker) Start() { 45 t.ticker.Reset(t.intervals.Get()) 46 } 47 48 func (t *cycleTicker) Stop() { 49 t.ticker.Stop() 50 } 51 52 func (t *cycleTicker) C() <-chan time.Time { 53 return t.ticker.C 54 } 55 56 func (t *cycleTicker) CycleExecuted(executed bool) { 57 if executed { 58 t.intervals.Reset() 59 } else { 60 t.intervals.Advance() 61 } 62 t.ticker.Reset(t.intervals.Get()) 63 } 64 65 // Creates ticker with fixed interval. Interval is not changed regardless 66 // of execution results reported by cycle function 67 // 68 // If interval <= 0 given, ticker will not fire 69 func NewFixedTicker(interval time.Duration) CycleTicker { 70 return newCycleTicker(NewFixedIntervals(interval)) 71 } 72 73 // Creates ticker with set of interval values. 74 // Ticker starts with intervals[0] value and with every report of executed "false" 75 // changes interval value to next one in given array up until last one. 76 // Report of executed "true" resets interval to interval[0] 77 // 78 // If any of intervals given is <= 0 given, ticker will not fire 79 func NewSeriesTicker(intervals []time.Duration) CycleTicker { 80 return newCycleTicker(NewSeriesIntervals(intervals)) 81 } 82 83 // Creates ticker with intervals between minInterval and maxInterval values. 84 // Number of intervals in-between is determined by steps value. 85 // Ticker starts with minInterval value and with every report of executed "false" 86 // changes interval value to next one, up until maxInterval. 87 // Report of executed "true" resets interval to minInterval 88 // Example: for minInterval = 100ms, maxInterval = 5s, steps = 4, intervals are 89 // 100ms . 1325ms . 2550ms . 3775ms . 5000ms 90 // 91 // If min- or maxInterval is <= 0 or steps = 0 or min > maxInterval, ticker will not fire 92 func NewLinearTicker(minInterval, maxInterval time.Duration, steps uint) CycleTicker { 93 return newCycleTicker(NewLinearIntervals(minInterval, maxInterval, steps)) 94 } 95 96 // Creates ticker with intervals between minInterval and maxInterval values. 97 // Number of intervals in-between is determined by steps value. 98 // Ticker starts with minInterval value and with every report of executed "false" 99 // changes interval value to next one, up until maxInterval. 100 // Report of executed "true" resets interval to minInterval 101 // Example: for minInterval = 100ms, maxInterval = 5s, base = 2, steps = 4, intervals are 102 // 100ms . 427ms .. 1080ms .... 2387ms ........ 5000ms 103 // 104 // If min- or maxInterval is <= 0 or base = 0 or steps = 0 or min > maxInterval, ticker will not fire 105 func NewExpTicker(minInterval, maxInterval time.Duration, base, steps uint) CycleTicker { 106 return newCycleTicker(NewExpIntervals(minInterval, maxInterval, base, steps)) 107 } 108 109 type noopTicker struct { 110 ch chan time.Time 111 } 112 113 func NewNoopTicker() CycleTicker { 114 return &noopTicker{ 115 ch: make(chan time.Time), 116 } 117 } 118 119 func (t *noopTicker) Start() { 120 } 121 122 func (t *noopTicker) Stop() { 123 } 124 125 func (t *noopTicker) C() <-chan time.Time { 126 return t.ch 127 } 128 129 func (t *noopTicker) CycleExecuted(executed bool) { 130 } 131 132 // ===== Intervals ===== 133 134 type CycleIntervals interface { 135 Get() time.Duration 136 Reset() 137 Advance() 138 } 139 140 type fixedIntervals struct { 141 interval time.Duration 142 } 143 144 func (i *fixedIntervals) Get() time.Duration { 145 return i.interval 146 } 147 148 func (i *fixedIntervals) Reset() { 149 } 150 151 func (i *fixedIntervals) Advance() { 152 } 153 154 type seriesIntervals struct { 155 intervals []time.Duration 156 pos int 157 } 158 159 func (i *seriesIntervals) Get() time.Duration { 160 return i.intervals[i.pos] 161 } 162 163 func (i *seriesIntervals) Reset() { 164 i.pos = 0 165 } 166 167 func (i *seriesIntervals) Advance() { 168 if i.pos < len(i.intervals)-1 { 169 i.pos++ 170 } 171 } 172 173 func NewFixedIntervals(interval time.Duration) CycleIntervals { 174 if interval <= 0 { 175 return nil 176 } 177 return &fixedIntervals{interval: interval} 178 } 179 180 func NewSeriesIntervals(intervals []time.Duration) CycleIntervals { 181 if len(intervals) == 0 { 182 return nil 183 } 184 allSame := true 185 for i := range intervals { 186 if intervals[i] <= 0 { 187 return nil 188 } 189 if intervals[i] != intervals[0] { 190 allSame = false 191 } 192 } 193 if allSame { 194 return &fixedIntervals{interval: intervals[0]} 195 } 196 return &seriesIntervals{intervals: intervals, pos: 0} 197 } 198 199 func NewLinearIntervals(minInterval, maxInterval time.Duration, steps uint) CycleIntervals { 200 if minInterval <= 0 || maxInterval <= 0 || steps == 0 || minInterval > maxInterval { 201 return nil 202 } 203 if minInterval == maxInterval { 204 return &fixedIntervals{interval: minInterval} 205 } 206 return &seriesIntervals{intervals: linearToIntervals(minInterval, maxInterval, steps), pos: 0} 207 } 208 209 func NewExpIntervals(minInterval, maxInterval time.Duration, base, steps uint) CycleIntervals { 210 if minInterval <= 0 || maxInterval <= 0 || base == 0 || steps == 0 || minInterval > maxInterval { 211 return nil 212 } 213 if minInterval == maxInterval { 214 return &fixedIntervals{interval: minInterval} 215 } 216 if base == 1 { 217 return &seriesIntervals{intervals: linearToIntervals(minInterval, maxInterval, steps), pos: 0} 218 } 219 return &seriesIntervals{intervals: expToIntervals(minInterval, maxInterval, base, steps), pos: 0} 220 } 221 222 // ===== Helper funcs ===== 223 224 func linearToIntervals(minInterval, maxInterval time.Duration, steps uint) []time.Duration { 225 delta := float64(maxInterval-minInterval) / float64(steps) 226 floatInterval := float64(minInterval) 227 228 intervals := make([]time.Duration, steps+1) 229 intervals[0] = minInterval 230 for i := uint(1); i <= steps; i++ { 231 floatInterval += delta 232 intervals[i] = time.Duration(floatInterval) 233 } 234 return intervals 235 } 236 237 func expToIntervals(minInterval, maxInterval time.Duration, base, steps uint) []time.Duration { 238 sum := uint(1) 239 power := uint(1) 240 for i := uint(1); i < steps; i++ { 241 power *= base 242 sum += power 243 } 244 delta := float64(maxInterval-minInterval) / float64(sum) 245 floatInterval := float64(minInterval) 246 floatBase := float64(base) 247 248 intervals := make([]time.Duration, steps+1) 249 intervals[0] = minInterval 250 for i := uint(1); i <= steps; i++ { 251 floatInterval += delta 252 intervals[i] = time.Duration(floatInterval) 253 if i < steps { 254 delta *= floatBase 255 } 256 } 257 return intervals 258 }