gitlab.com/aquachain/aquachain@v1.17.16-rc3.0.20221018032414-e3ddf1e1c055/common/metrics/resetting_timer.go (about)

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