Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lx9FNLG2CqeBbVGGd9bFsP
77 lines
1.7 KiB
Go
77 lines
1.7 KiB
Go
package rollup
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"math"
|
|
"strconv"
|
|
)
|
|
|
|
// point is one stored sample; ts is Unix milliseconds.
|
|
type point struct {
|
|
ts int64
|
|
v float64
|
|
}
|
|
|
|
// series is one result row of a matrix (pts) or vector (one pt) response.
|
|
type series struct {
|
|
labels map[string]string
|
|
pts []point
|
|
}
|
|
|
|
type vmResponse struct {
|
|
Status string `json:"status"`
|
|
Error string `json:"error"`
|
|
Data struct {
|
|
Result []struct {
|
|
Metric map[string]string `json:"metric"`
|
|
Value []any `json:"value"`
|
|
Values [][]any `json:"values"`
|
|
} `json:"result"`
|
|
} `json:"data"`
|
|
}
|
|
|
|
// parseResult decodes an instant-query response of either shape. Non-finite
|
|
// values are dropped: a NaN is "no data", not a zero.
|
|
func parseResult(body []byte) ([]series, error) {
|
|
var r vmResponse
|
|
if err := json.Unmarshal(body, &r); err != nil {
|
|
return nil, fmt.Errorf("rollup: decode store response: %w", err)
|
|
}
|
|
if r.Status != "success" {
|
|
return nil, fmt.Errorf("rollup: store query failed: %s", r.Error)
|
|
}
|
|
out := make([]series, 0, len(r.Data.Result))
|
|
for _, row := range r.Data.Result {
|
|
s := series{labels: row.Metric}
|
|
if len(row.Value) == 2 {
|
|
if p, ok := decodePoint(row.Value); ok {
|
|
s.pts = append(s.pts, p)
|
|
}
|
|
}
|
|
for _, v := range row.Values {
|
|
if p, ok := decodePoint(v); ok {
|
|
s.pts = append(s.pts, p)
|
|
}
|
|
}
|
|
out = append(out, s)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func decodePoint(raw []any) (point, bool) {
|
|
if len(raw) != 2 {
|
|
return point{}, false
|
|
}
|
|
t, ok := raw[0].(float64)
|
|
str, ok2 := raw[1].(string)
|
|
if !ok || !ok2 {
|
|
return point{}, false
|
|
}
|
|
v, err := strconv.ParseFloat(str, 64)
|
|
if err != nil || math.IsNaN(v) || math.IsInf(v, 0) {
|
|
return point{}, false
|
|
}
|
|
return point{ts: int64(math.Round(t * 1000)), v: v}, true
|
|
}
|