Checkpoint
This commit is contained in:
105
stats/timeseries.go
Normal file
105
stats/timeseries.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type timeseries struct {
|
||||
sync.Mutex
|
||||
now time.Time
|
||||
size int
|
||||
interval time.Duration
|
||||
total metric
|
||||
samples []metric
|
||||
}
|
||||
|
||||
func (ts *timeseries) Reset() {
|
||||
ts.total.Reset()
|
||||
for _, s := range ts.samples {
|
||||
s.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
func (ts *timeseries) roll() {
|
||||
t := time.Now()
|
||||
roll := int((t.Round(ts.interval).Sub(ts.now.Round(ts.interval))) / ts.interval)
|
||||
ts.now = t
|
||||
n := len(ts.samples)
|
||||
if roll <= 0 {
|
||||
return
|
||||
}
|
||||
if roll >= len(ts.samples) {
|
||||
ts.Reset()
|
||||
} else {
|
||||
for i := 0; i < roll; i++ {
|
||||
tmp := ts.samples[n-1]
|
||||
for j := n - 1; j > 0; j-- {
|
||||
ts.samples[j] = ts.samples[j-1]
|
||||
}
|
||||
ts.samples[0] = tmp
|
||||
ts.samples[0].Reset()
|
||||
}
|
||||
ts.total.Aggregate(roll, ts.samples)
|
||||
}
|
||||
}
|
||||
|
||||
func (ts *timeseries) Add(n float64) {
|
||||
ts.Lock()
|
||||
defer ts.Unlock()
|
||||
ts.roll()
|
||||
ts.total.Add(n)
|
||||
ts.samples[0].Add(n)
|
||||
}
|
||||
|
||||
func (ts *timeseries) MarshalJSON() ([]byte, error) {
|
||||
ts.Lock()
|
||||
defer ts.Unlock()
|
||||
ts.roll()
|
||||
return json.Marshal(struct {
|
||||
Interval float64 `json:"interval"`
|
||||
Total Metric `json:"total"`
|
||||
Samples []metric `json:"samples"`
|
||||
}{float64(ts.interval) / float64(time.Second), ts.total, ts.samples})
|
||||
}
|
||||
|
||||
func (ts *timeseries) String() string {
|
||||
ts.Lock()
|
||||
defer ts.Unlock()
|
||||
ts.roll()
|
||||
return ts.total.String()
|
||||
}
|
||||
|
||||
func newTimeseries(builder func() metric, frame string) *timeseries {
|
||||
var (
|
||||
totalNum, intervalNum int
|
||||
totalUnit, intervalUnit rune
|
||||
)
|
||||
units := map[rune]time.Duration{
|
||||
's': time.Second,
|
||||
'm': time.Minute,
|
||||
'h': time.Hour,
|
||||
'd': time.Hour * 24,
|
||||
'w': time.Hour * 24 * 7,
|
||||
'M': time.Hour * 24 * 30,
|
||||
'y': time.Hour * 24 * 365,
|
||||
}
|
||||
fmt.Sscanf(frame, "%d%c%d%c", &totalNum, &totalUnit, &intervalNum, &intervalUnit)
|
||||
interval := units[intervalUnit] * time.Duration(intervalNum)
|
||||
if interval == 0 {
|
||||
interval = time.Minute
|
||||
}
|
||||
totalDuration := units[totalUnit] * time.Duration(totalNum)
|
||||
if totalDuration == 0 {
|
||||
totalDuration = interval * 15
|
||||
}
|
||||
n := int(totalDuration / interval)
|
||||
samples := make([]metric, n, n)
|
||||
for i := 0; i < n; i++ {
|
||||
samples[i] = builder()
|
||||
}
|
||||
totalMetric := builder()
|
||||
return ×eries{interval: interval, total: totalMetric, samples: samples}
|
||||
}
|
Reference in New Issue
Block a user