// Package history keeps a fixed-size in-memory ring of metric samples. package history import ( "sync" "time" ) type Sample struct { T int64 `json:"t"` // unix seconds CPU float64 `json:"cpu"` MemUsed uint64 `json:"mem_used"` ARC uint64 `json:"arc"` CPUTemp float64 `json:"cpu_temp"` } type Ring struct { mu sync.RWMutex buf []Sample head int n int } func New(size int) *Ring { return &Ring{buf: make([]Sample, size)} } func (r *Ring) Add(s Sample) { r.mu.Lock() defer r.mu.Unlock() r.buf[r.head] = s r.head = (r.head + 1) % len(r.buf) if r.n < len(r.buf) { r.n++ } } // Since returns samples newer than t in chronological order, thinned to at // most maxPoints entries. func (r *Ring) Since(t time.Time, maxPoints int) []Sample { r.mu.RLock() defer r.mu.RUnlock() ts := t.Unix() out := make([]Sample, 0, r.n) start := (r.head - r.n + len(r.buf)) % len(r.buf) for i := 0; i < r.n; i++ { s := r.buf[(start+i)%len(r.buf)] if s.T >= ts { out = append(out, s) } } if maxPoints > 0 && len(out) > maxPoints { step := (len(out) + maxPoints - 1) / maxPoints thin := out[:0:0] for i := 0; i < len(out); i += step { thin = append(thin, out[i]) } out = thin } return out }