github.com/intfoundation/intchain@v0.0.0-20220727031208-4316ad31ca73/metrics/resetting_timer.go (about)

     1  package metrics
     2  
     3  import (
     4  	"math"
     5  	"sort"
     6  	"sync"
     7  	"time"
     8  )
     9  
    10  // Initial slice capacity for the values stored in a ResettingTimer
    11  const InitialResettingTimerSliceCap = 10
    12  
    13  // ResettingTimer is used for storing aggregated values for timers, which are reset on every flush interval.
    14  type ResettingTimer interface {
    15  	Values() []int64
    16  	Snapshot() ResettingTimer
    17  	Percentiles([]float64) []int64
    18  	Mean() float64
    19  	Time(func())
    20  	Update(time.Duration)
    21  	UpdateSince(time.Time)
    22  }
    23  
    24  // GetOrRegisterResettingTimer returns an existing ResettingTimer or constructs and registers a
    25  // new StandardResettingTimer.
    26  func GetOrRegisterResettingTimer(name string, r Registry) ResettingTimer {
    27  	if nil == r {
    28  		r = DefaultRegistry
    29  	}
    30  	return r.GetOrRegister(name, NewResettingTimer).(ResettingTimer)
    31  }
    32  
    33  // NewRegisteredResettingTimer constructs and registers a new StandardResettingTimer.
    34  func NewRegisteredResettingTimer(name string, r Registry) ResettingTimer {
    35  	c := NewResettingTimer()
    36  	if nil == r {
    37  		r = DefaultRegistry
    38  	}
    39  	r.Register(name, c)
    40  	return c
    41  }
    42  
    43  // NewResettingTimer constructs a new StandardResettingTimer
    44  func NewResettingTimer() ResettingTimer {
    45  	if !Enabled {
    46  		return NilResettingTimer{}
    47  	}
    48  	return &StandardResettingTimer{
    49  		values: make([]int64, 0, InitialResettingTimerSliceCap),
    50  	}
    51  }
    52  
    53  // NilResettingTimer is a no-op ResettingTimer.
    54  type NilResettingTimer struct {
    55  }
    56  
    57  // Values is a no-op.
    58  func (NilResettingTimer) Values() []int64 { return nil }
    59  
    60  // Snapshot is a no-op.
    61  func (NilResettingTimer) Snapshot() ResettingTimer { return NilResettingTimer{} }
    62  
    63  // Time is a no-op.
    64  func (NilResettingTimer) Time(func()) {}
    65  
    66  // Update is a no-op.
    67  func (NilResettingTimer) Update(time.Duration) {}
    68  
    69  // Percentiles panics.
    70  func (NilResettingTimer) Percentiles([]float64) []int64 {
    71  	panic("Percentiles called on a NilResettingTimer")
    72  }
    73  
    74  // Mean panics.
    75  func (NilResettingTimer) Mean() float64 {
    76  	panic("Mean called on a NilResettingTimer")
    77  }
    78  
    79  // UpdateSince is a no-op.
    80  func (NilResettingTimer) UpdateSince(time.Time) {}
    81  
    82  // StandardResettingTimer is the standard implementation of a ResettingTimer.
    83  // and Meter.
    84  type StandardResettingTimer struct {
    85  	values []int64
    86  	mutex  sync.Mutex
    87  }
    88  
    89  // Values returns a slice with all measurements.
    90  func (t *StandardResettingTimer) Values() []int64 {
    91  	return t.values
    92  }
    93  
    94  // Snapshot resets the timer and returns a read-only copy of its contents.
    95  func (t *StandardResettingTimer) Snapshot() ResettingTimer {
    96  	t.mutex.Lock()
    97  	defer t.mutex.Unlock()
    98  	currentValues := t.values
    99  	t.values = make([]int64, 0, InitialResettingTimerSliceCap)
   100  
   101  	return &ResettingTimerSnapshot{
   102  		values: currentValues,
   103  	}
   104  }
   105  
   106  // Percentiles panics.
   107  func (t *StandardResettingTimer) Percentiles([]float64) []int64 {
   108  	panic("Percentiles called on a StandardResettingTimer")
   109  }
   110  
   111  // Mean panics.
   112  func (t *StandardResettingTimer) Mean() float64 {
   113  	panic("Mean called on a StandardResettingTimer")
   114  }
   115  
   116  // Record the duration of the execution of the given function.
   117  func (t *StandardResettingTimer) Time(f func()) {
   118  	ts := time.Now()
   119  	f()
   120  	t.Update(time.Since(ts))
   121  }
   122  
   123  // Record the duration of an event.
   124  func (t *StandardResettingTimer) Update(d time.Duration) {
   125  	t.mutex.Lock()
   126  	defer t.mutex.Unlock()
   127  	t.values = append(t.values, int64(d))
   128  }
   129  
   130  // Record the duration of an event that started at a time and ends now.
   131  func (t *StandardResettingTimer) UpdateSince(ts time.Time) {
   132  	t.mutex.Lock()
   133  	defer t.mutex.Unlock()
   134  	t.values = append(t.values, int64(time.Since(ts)))
   135  }
   136  
   137  // ResettingTimerSnapshot is a point-in-time copy of another ResettingTimer.
   138  type ResettingTimerSnapshot struct {
   139  	values              []int64
   140  	mean                float64
   141  	thresholdBoundaries []int64
   142  	calculated          bool
   143  }
   144  
   145  // Snapshot returns the snapshot.
   146  func (t *ResettingTimerSnapshot) Snapshot() ResettingTimer { return t }
   147  
   148  // Time panics.
   149  func (*ResettingTimerSnapshot) Time(func()) {
   150  	panic("Time called on a ResettingTimerSnapshot")
   151  }
   152  
   153  // Update panics.
   154  func (*ResettingTimerSnapshot) Update(time.Duration) {
   155  	panic("Update called on a ResettingTimerSnapshot")
   156  }
   157  
   158  // UpdateSince panics.
   159  func (*ResettingTimerSnapshot) UpdateSince(time.Time) {
   160  	panic("UpdateSince called on a ResettingTimerSnapshot")
   161  }
   162  
   163  // Values returns all values from snapshot.
   164  func (t *ResettingTimerSnapshot) Values() []int64 {
   165  	return t.values
   166  }
   167  
   168  // Percentiles returns the boundaries for the input percentiles.
   169  func (t *ResettingTimerSnapshot) Percentiles(percentiles []float64) []int64 {
   170  	t.calc(percentiles)
   171  
   172  	return t.thresholdBoundaries
   173  }
   174  
   175  // Mean returns the mean of the snapshotted values
   176  func (t *ResettingTimerSnapshot) Mean() float64 {
   177  	if !t.calculated {
   178  		t.calc([]float64{})
   179  	}
   180  
   181  	return t.mean
   182  }
   183  
   184  func (t *ResettingTimerSnapshot) calc(percentiles []float64) {
   185  	sort.Sort(Int64Slice(t.values))
   186  
   187  	count := len(t.values)
   188  	if count > 0 {
   189  		min := t.values[0]
   190  		max := t.values[count-1]
   191  
   192  		cumulativeValues := make([]int64, count)
   193  		cumulativeValues[0] = min
   194  		for i := 1; i < count; i++ {
   195  			cumulativeValues[i] = t.values[i] + cumulativeValues[i-1]
   196  		}
   197  
   198  		t.thresholdBoundaries = make([]int64, len(percentiles))
   199  
   200  		thresholdBoundary := max
   201  
   202  		for i, pct := range percentiles {
   203  			if count > 1 {
   204  				var abs float64
   205  				if pct >= 0 {
   206  					abs = pct
   207  				} else {
   208  					abs = 100 + pct
   209  				}
   210  				// poor man's math.Round(x):
   211  				// math.Floor(x + 0.5)
   212  				indexOfPerc := int(math.Floor(((abs / 100.0) * float64(count)) + 0.5))
   213  				if pct >= 0 {
   214  					indexOfPerc -= 1 // index offset=0
   215  				}
   216  				thresholdBoundary = t.values[indexOfPerc]
   217  			}
   218  
   219  			t.thresholdBoundaries[i] = thresholdBoundary
   220  		}
   221  
   222  		sum := cumulativeValues[count-1]
   223  		t.mean = float64(sum) / float64(count)
   224  	} else {
   225  		t.thresholdBoundaries = make([]int64, len(percentiles))
   226  		t.mean = 0
   227  	}
   228  
   229  	t.calculated = true
   230  }
   231  
   232  // Int64Slice attaches the methods of sort.Interface to []int64, sorting in increasing order.
   233  type Int64Slice []int64
   234  
   235  func (s Int64Slice) Len() int           { return len(s) }
   236  func (s Int64Slice) Less(i, j int) bool { return s[i] < s[j] }
   237  func (s Int64Slice) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }