Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lx9FNLG2CqeBbVGGd9bFsP
68 lines
2.0 KiB
Go
68 lines
2.0 KiB
Go
package rollup
|
|
|
|
import "math"
|
|
|
|
// wrap32 is the modulus of a 32-bit octet counter.
|
|
const wrap32 = 1 << 32
|
|
|
|
// counterWindow is the result of measuring one counter over one window.
|
|
type counterWindow struct {
|
|
rateBps float64 // average bits per second over the span actually observed
|
|
inside int // samples inside the window; fewer than expected means a gap
|
|
}
|
|
|
|
// measureCounter computes the average rate of a counter over the window
|
|
// (startMs, endMs] from its raw samples, which must be sorted by time and may
|
|
// include samples before the window (the newest of them is the baseline, so a
|
|
// window's rate is the counter's slope across it rather than a guess).
|
|
//
|
|
// The counter is never averaged: the increase is summed between consecutive
|
|
// samples and divided by the time those samples span. A decrease is a 32-bit
|
|
// wrap when wide is false (the increase is d+2^32) and a reset otherwise (the
|
|
// counter restarted from zero, so the increase is the new value), which is
|
|
// what VictoriaMetrics' increase() assumes too. ok is false when fewer than
|
|
// two samples bound the window.
|
|
func measureCounter(pts []point, startMs, endMs int64, wide bool) (counterWindow, bool) {
|
|
var base *point
|
|
var in []point
|
|
for i := range pts {
|
|
switch {
|
|
case pts[i].ts <= startMs:
|
|
base = &pts[i]
|
|
case pts[i].ts <= endMs:
|
|
in = append(in, pts[i])
|
|
}
|
|
}
|
|
if len(in) == 0 {
|
|
return counterWindow{}, false
|
|
}
|
|
seq := in
|
|
if base != nil {
|
|
seq = append([]point{*base}, in...)
|
|
}
|
|
if len(seq) < 2 {
|
|
return counterWindow{inside: len(in)}, false
|
|
}
|
|
var inc float64
|
|
for i := 1; i < len(seq); i++ {
|
|
d := seq[i].v - seq[i-1].v
|
|
if d < 0 {
|
|
if wide {
|
|
d = seq[i].v
|
|
} else {
|
|
d += wrap32
|
|
}
|
|
}
|
|
inc += d
|
|
}
|
|
span := float64(seq[len(seq)-1].ts-seq[0].ts) / 1000
|
|
if span <= 0 {
|
|
return counterWindow{inside: len(in)}, false
|
|
}
|
|
rate := inc * 8 / span
|
|
if math.IsInf(rate, 0) || math.IsNaN(rate) {
|
|
return counterWindow{inside: len(in)}, false
|
|
}
|
|
return counterWindow{rateBps: rate, inside: len(in)}, true
|
|
}
|