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  }