github.com/haraldrudell/parl@v0.4.176/counter/counters.go (about)

     1  /*
     2  © 2022–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
     3  ISC License
     4  */
     5  
     6  package counter
     7  
     8  import (
     9  	"sync"
    10  	"time"
    11  
    12  	"github.com/haraldrudell/parl"
    13  	"github.com/haraldrudell/parl/perrors"
    14  	"golang.org/x/exp/maps"
    15  	"golang.org/x/exp/slices"
    16  )
    17  
    18  // Counters is a container for counters, rate-counters and datapoints. Thread-Safe.
    19  //   - a counter is Inc-Dec with: value running max
    20  //   - a rate-counter is a counter with addtional measuring over short time periods:
    21  //   - — value: rate of increase: current/max/average
    22  //   - — running: rate up or down, max increase/decrease rate,
    23  type Counters struct {
    24  	lock    sync.RWMutex
    25  	ordered []parl.CounterID       // behind lock: ordered list of counters and datapoints
    26  	m       map[parl.CounterID]any // behind lock
    27  	RateRunner
    28  }
    29  
    30  var _ parl.CounterSet = &Counters{}     // Counters is a parl.CounterSet
    31  var _ parl.CounterSetData = &Counters{} // Counters is a parl.CounterSet
    32  var _ parl.CounterStore = &Counters{}   // Counters is a parl.CounterStore
    33  
    34  func newCounters(g0 parl.GoGen) (counters parl.Counters) {
    35  	return &Counters{
    36  		m:          map[parl.CounterID]any{},
    37  		RateRunner: *NewRateRunner(g0),
    38  	}
    39  }
    40  
    41  func (cs *Counters) GetOrCreateCounter(name parl.CounterID, period ...time.Duration) (counter parl.Counter) {
    42  	counter = cs.getOrCreate(false, name, period...).(parl.Counter)
    43  	return
    44  }
    45  
    46  func (cs *Counters) GetCounter(name parl.CounterID) (counter parl.Counter) {
    47  	counter = cs.getOrCreate(false, name).(parl.Counter)
    48  	return
    49  }
    50  
    51  func (cs *Counters) GetOrCreateDatapoint(name parl.CounterID, period time.Duration) (datapoint parl.Datapoint) {
    52  	datapoint = cs.getOrCreate(true, name, period).(parl.Datapoint)
    53  	return
    54  }
    55  
    56  func (cs *Counters) GetCounters() (list []parl.CounterID, m map[parl.CounterID]any) {
    57  	cs.lock.RLock()
    58  	defer cs.lock.RUnlock()
    59  
    60  	list = slices.Clone(cs.ordered)
    61  	m = maps.Clone(cs.m)
    62  
    63  	return
    64  }
    65  
    66  func (cs *Counters) ResetCounters(stopRateCounters bool) {
    67  	cs.lock.Lock()
    68  	defer cs.lock.Unlock()
    69  
    70  	_, m := cs.GetCounters()
    71  	for _, item := range m {
    72  		if counter, ok := item.(parl.CounterValues); ok {
    73  			counter.GetReset()
    74  		} else if datapoint, ok := item.(parl.DatapointValue); ok {
    75  			datapoint.CloneDatapointReset()
    76  		} else {
    77  			panic(perrors.ErrorfPF("Bad item in map: %T", item))
    78  		}
    79  	}
    80  }
    81  
    82  func (cs *Counters) Exists(name parl.CounterID) (exists bool) {
    83  	exists = cs.GetNamedCounter(name) != nil
    84  	return
    85  }
    86  
    87  func (cs *Counters) Value(name parl.CounterID) (value uint64) {
    88  	if counter, ok := cs.GetNamedCounter(name).(interface{ Value() (value uint64) }); ok {
    89  		value = counter.Value()
    90  	}
    91  	return
    92  }
    93  
    94  func (cs *Counters) Get(name parl.CounterID) (value, running, max uint64) {
    95  	if counter, ok := cs.GetNamedCounter(name).(interface {
    96  		Get() (value, running, max uint64)
    97  	}); ok {
    98  		value, running, max = counter.Get()
    99  	}
   100  	return
   101  }
   102  
   103  func (cs *Counters) Rates(name parl.CounterID) (rates map[parl.RateType]float64) {
   104  	if counter, ok := cs.GetNamedCounter(name).(interface {
   105  		Rates() (rates map[parl.RateType]float64)
   106  	}); ok {
   107  		rates = counter.Rates()
   108  	}
   109  	return
   110  }
   111  
   112  func (cs *Counters) DatapointValue(name parl.CounterID) (datapointValue uint64) {
   113  	if counter, ok := cs.GetNamedCounter(name).(interface {
   114  		DatapointValue() (datapointValue uint64)
   115  	}); ok {
   116  		datapointValue = counter.DatapointValue()
   117  	}
   118  	return
   119  }
   120  
   121  func (cs *Counters) DatapointMax(name parl.CounterID) (datapointMax uint64) {
   122  	if counter, ok := cs.GetNamedCounter(name).(interface {
   123  		DatapointMax() (datapointMax uint64)
   124  	}); ok {
   125  		datapointMax = counter.DatapointMax()
   126  	}
   127  	return
   128  }
   129  
   130  func (cs *Counters) DatapointMin(name parl.CounterID) (datapointMin uint64) {
   131  	if counter, ok := cs.GetNamedCounter(name).(interface {
   132  		DatapointMin() (datapointMin uint64)
   133  	}); ok {
   134  		datapointMin = counter.DatapointMin()
   135  	}
   136  	return
   137  }
   138  
   139  func (cs *Counters) GetDatapoint(name parl.CounterID) (value, max, min uint64, isValid bool, average float64, n uint64) {
   140  	if counter, ok := cs.GetNamedCounter(name).(interface {
   141  		GetDatapoint() (value, max, min uint64, isValid bool, average float64, n uint64)
   142  	}); ok {
   143  		value, max, min, isValid, average, n = counter.GetDatapoint()
   144  	}
   145  	return
   146  }
   147  
   148  func (cs *Counters) GetNamedCounter(name parl.CounterID) (counter any) {
   149  	cs.lock.RLock()
   150  	defer cs.lock.RUnlock()
   151  
   152  	counter = cs.m[name]
   153  	return
   154  }
   155  
   156  func (cs *Counters) getOrCreate(isDatapoint bool, name parl.CounterID, period ...time.Duration) (item any) {
   157  	cs.lock.Lock()
   158  	defer cs.lock.Unlock()
   159  
   160  	// check for existing counter or datapoint
   161  	var ok bool
   162  	if item, ok = cs.m[name]; ok {
   163  		return // counter exists return
   164  	}
   165  
   166  	// instantiate counter or datapoint
   167  	var period0 time.Duration
   168  	if len(period) > 0 {
   169  		period0 = period[0]
   170  	}
   171  	if !isDatapoint {
   172  		if period0 == 0 {
   173  			item = newCounter() // non-rate counter
   174  		} else {
   175  			var r = newRateCounter()
   176  			item = r
   177  			cs.AddTask(period0, r)
   178  		}
   179  	} else {
   180  		item = newDatapoint(period0)
   181  	}
   182  
   183  	// store the new counter or datapoint
   184  	cs.ordered = append(cs.ordered, name)
   185  	cs.m[name] = item
   186  
   187  	return
   188  }