2017-02-07 21:33:23 +00:00
|
|
|
package memmetrics
|
|
|
|
|
|
|
|
import (
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/mailgun/timetools"
|
|
|
|
)
|
|
|
|
|
|
|
|
type ratioOptSetter func(r *RatioCounter) error
|
|
|
|
|
2018-07-11 08:08:03 +00:00
|
|
|
// RatioClock sets a clock
|
2017-02-07 21:33:23 +00:00
|
|
|
func RatioClock(clock timetools.TimeProvider) ratioOptSetter {
|
|
|
|
return func(r *RatioCounter) error {
|
|
|
|
r.clock = clock
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// RatioCounter calculates a ratio of a/a+b over a rolling window of predefined buckets
|
|
|
|
type RatioCounter struct {
|
|
|
|
clock timetools.TimeProvider
|
|
|
|
a *RollingCounter
|
|
|
|
b *RollingCounter
|
|
|
|
}
|
|
|
|
|
2018-07-11 08:08:03 +00:00
|
|
|
// NewRatioCounter creates a new RatioCounter
|
2017-02-07 21:33:23 +00:00
|
|
|
func NewRatioCounter(buckets int, resolution time.Duration, options ...ratioOptSetter) (*RatioCounter, error) {
|
|
|
|
rc := &RatioCounter{}
|
|
|
|
|
|
|
|
for _, o := range options {
|
|
|
|
if err := o(rc); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if rc.clock == nil {
|
|
|
|
rc.clock = &timetools.RealTime{}
|
|
|
|
}
|
|
|
|
|
|
|
|
a, err := NewCounter(buckets, resolution, CounterClock(rc.clock))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
b, err := NewCounter(buckets, resolution, CounterClock(rc.clock))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
rc.a = a
|
|
|
|
rc.b = b
|
|
|
|
return rc, nil
|
|
|
|
}
|
|
|
|
|
2018-07-11 08:08:03 +00:00
|
|
|
// Reset reset the counter
|
2017-02-07 21:33:23 +00:00
|
|
|
func (r *RatioCounter) Reset() {
|
|
|
|
r.a.Reset()
|
|
|
|
r.b.Reset()
|
|
|
|
}
|
|
|
|
|
2018-07-11 08:08:03 +00:00
|
|
|
// IsReady returns true if the counter is ready
|
2017-02-07 21:33:23 +00:00
|
|
|
func (r *RatioCounter) IsReady() bool {
|
|
|
|
return r.a.countedBuckets+r.b.countedBuckets >= len(r.a.values)
|
|
|
|
}
|
|
|
|
|
2018-07-11 08:08:03 +00:00
|
|
|
// CountA gets count A
|
2017-02-07 21:33:23 +00:00
|
|
|
func (r *RatioCounter) CountA() int64 {
|
|
|
|
return r.a.Count()
|
|
|
|
}
|
|
|
|
|
2018-07-11 08:08:03 +00:00
|
|
|
// CountB gets count B
|
2017-02-07 21:33:23 +00:00
|
|
|
func (r *RatioCounter) CountB() int64 {
|
|
|
|
return r.b.Count()
|
|
|
|
}
|
|
|
|
|
2018-07-11 08:08:03 +00:00
|
|
|
// Resolution gets resolution
|
2017-02-07 21:33:23 +00:00
|
|
|
func (r *RatioCounter) Resolution() time.Duration {
|
|
|
|
return r.a.Resolution()
|
|
|
|
}
|
|
|
|
|
2018-07-11 08:08:03 +00:00
|
|
|
// Buckets gets buckets
|
2017-02-07 21:33:23 +00:00
|
|
|
func (r *RatioCounter) Buckets() int {
|
|
|
|
return r.a.Buckets()
|
|
|
|
}
|
|
|
|
|
2018-07-11 08:08:03 +00:00
|
|
|
// WindowSize gets windows size
|
2017-02-07 21:33:23 +00:00
|
|
|
func (r *RatioCounter) WindowSize() time.Duration {
|
|
|
|
return r.a.WindowSize()
|
|
|
|
}
|
|
|
|
|
2018-07-11 08:08:03 +00:00
|
|
|
// ProcessedCount gets processed count
|
2017-02-07 21:33:23 +00:00
|
|
|
func (r *RatioCounter) ProcessedCount() int64 {
|
|
|
|
return r.CountA() + r.CountB()
|
|
|
|
}
|
|
|
|
|
2018-07-11 08:08:03 +00:00
|
|
|
// Ratio gets ratio
|
2017-02-07 21:33:23 +00:00
|
|
|
func (r *RatioCounter) Ratio() float64 {
|
|
|
|
a := r.a.Count()
|
|
|
|
b := r.b.Count()
|
|
|
|
// No data, return ok
|
|
|
|
if a+b == 0 {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
return float64(a) / float64(a+b)
|
|
|
|
}
|
|
|
|
|
2018-07-11 08:08:03 +00:00
|
|
|
// IncA increment counter A
|
2017-02-07 21:33:23 +00:00
|
|
|
func (r *RatioCounter) IncA(v int) {
|
|
|
|
r.a.Inc(v)
|
|
|
|
}
|
|
|
|
|
2018-07-11 08:08:03 +00:00
|
|
|
// IncB increment counter B
|
2017-02-07 21:33:23 +00:00
|
|
|
func (r *RatioCounter) IncB(v int) {
|
|
|
|
r.b.Inc(v)
|
|
|
|
}
|
|
|
|
|
2018-07-11 08:08:03 +00:00
|
|
|
// TestMeter a test meter
|
2017-02-07 21:33:23 +00:00
|
|
|
type TestMeter struct {
|
|
|
|
Rate float64
|
|
|
|
NotReady bool
|
|
|
|
WindowSize time.Duration
|
|
|
|
}
|
|
|
|
|
2018-07-11 08:08:03 +00:00
|
|
|
// GetWindowSize gets windows size
|
2017-02-07 21:33:23 +00:00
|
|
|
func (tm *TestMeter) GetWindowSize() time.Duration {
|
|
|
|
return tm.WindowSize
|
|
|
|
}
|
|
|
|
|
2018-07-11 08:08:03 +00:00
|
|
|
// IsReady returns true if the meter is ready
|
2017-02-07 21:33:23 +00:00
|
|
|
func (tm *TestMeter) IsReady() bool {
|
|
|
|
return !tm.NotReady
|
|
|
|
}
|
|
|
|
|
2018-07-11 08:08:03 +00:00
|
|
|
// GetRate gets rate
|
2017-02-07 21:33:23 +00:00
|
|
|
func (tm *TestMeter) GetRate() float64 {
|
|
|
|
return tm.Rate
|
|
|
|
}
|