package rolling import ( "sync" "time" ) // Number tracks a numberBucket over a bounded number of // time buckets. Currently the buckets are one second long and only the last 10 seconds are kept. type Number struct { Buckets map[int64]*numberBucket Mutex *sync.RWMutex } type numberBucket struct { Value float64 } // NewNumber initializes a RollingNumber struct. func NewNumber() *Number { r := &Number{ Buckets: make(map[int64]*numberBucket), Mutex: &sync.RWMutex{}, } return r } func (r *Number) getCurrentBucket() *numberBucket { now := time.Now().Unix() var bucket *numberBucket var ok bool if bucket, ok = r.Buckets[now]; !ok { bucket = &numberBucket{} r.Buckets[now] = bucket } return bucket } func (r *Number) removeOldBuckets() { now := time.Now().Unix() - 10 for timestamp := range r.Buckets { // TODO: configurable rolling window if timestamp <= now { delete(r.Buckets, timestamp) } } } // Increment increments the number in current timeBucket. func (r *Number) Increment(i float64) { if i == 0 { return } r.Mutex.Lock() defer r.Mutex.Unlock() b := r.getCurrentBucket() b.Value += i r.removeOldBuckets() } // UpdateMax updates the maximum value in the current bucket. func (r *Number) UpdateMax(n float64) { r.Mutex.Lock() defer r.Mutex.Unlock() b := r.getCurrentBucket() if n > b.Value { b.Value = n } r.removeOldBuckets() } // Sum sums the values over the buckets in the last 10 seconds. func (r *Number) Sum(now time.Time) float64 { sum := float64(0) r.Mutex.RLock() defer r.Mutex.RUnlock() for timestamp, bucket := range r.Buckets { // TODO: configurable rolling window if timestamp >= now.Unix()-10 { sum += bucket.Value } } return sum } // Max returns the maximum value seen in the last 10 seconds. func (r *Number) Max(now time.Time) float64 { var max float64 r.Mutex.RLock() defer r.Mutex.RUnlock() for timestamp, bucket := range r.Buckets { // TODO: configurable rolling window if timestamp >= now.Unix()-10 { if bucket.Value > max { max = bucket.Value } } } return max } func (r *Number) Avg(now time.Time) float64 { return r.Sum(now) / 10 }