forked from wrenn/wrenn
Add 5m, 1h, 6h, 12h range filters to metrics endpoint
Maps each user-facing range to the appropriate underlying ring buffer tier and applies a time cutoff filter. No new ring buffers needed — 5m/10m read from the 10m tier, 1h/2h from the 2h tier, 6h/12h/24h from the 24h tier.
This commit is contained in:
@ -3,6 +3,7 @@ package api
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
"connectrpc.com/connect"
|
"connectrpc.com/connect"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
@ -45,8 +46,9 @@ func (h *sandboxMetricsHandler) GetMetrics(w http.ResponseWriter, r *http.Reques
|
|||||||
if rangeTier == "" {
|
if rangeTier == "" {
|
||||||
rangeTier = "10m"
|
rangeTier = "10m"
|
||||||
}
|
}
|
||||||
if rangeTier != "10m" && rangeTier != "2h" && rangeTier != "24h" {
|
validRanges := map[string]bool{"5m": true, "10m": true, "1h": true, "2h": true, "6h": true, "12h": true, "24h": true}
|
||||||
writeError(w, http.StatusBadRequest, "invalid_request", "range must be 10m, 2h, or 24h")
|
if !validRanges[rangeTier] {
|
||||||
|
writeError(w, http.StatusBadRequest, "invalid_request", "range must be one of: 5m, 10m, 1h, 2h, 6h, 12h, 24h")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,23 +104,41 @@ func (h *sandboxMetricsHandler) getFromAgent(w http.ResponseWriter, r *http.Requ
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rangeToDB maps a user-facing range filter to the DB tier and cutoff duration.
|
||||||
|
var rangeToDB = map[string]struct {
|
||||||
|
tier string
|
||||||
|
cutoff time.Duration
|
||||||
|
}{
|
||||||
|
"5m": {"10m", 5 * time.Minute},
|
||||||
|
"10m": {"10m", 10 * time.Minute},
|
||||||
|
"1h": {"2h", 1 * time.Hour},
|
||||||
|
"2h": {"2h", 2 * time.Hour},
|
||||||
|
"6h": {"24h", 6 * time.Hour},
|
||||||
|
"12h": {"24h", 12 * time.Hour},
|
||||||
|
"24h": {"24h", 24 * time.Hour},
|
||||||
|
}
|
||||||
|
|
||||||
func (h *sandboxMetricsHandler) getFromDB(ctx context.Context, w http.ResponseWriter, sandboxID, rangeTier string) {
|
func (h *sandboxMetricsHandler) getFromDB(ctx context.Context, w http.ResponseWriter, sandboxID, rangeTier string) {
|
||||||
|
mapping := rangeToDB[rangeTier]
|
||||||
rows, err := h.db.GetSandboxMetricPoints(ctx, db.GetSandboxMetricPointsParams{
|
rows, err := h.db.GetSandboxMetricPoints(ctx, db.GetSandboxMetricPointsParams{
|
||||||
SandboxID: sandboxID,
|
SandboxID: sandboxID,
|
||||||
Tier: rangeTier,
|
Tier: mapping.tier,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeError(w, http.StatusInternalServerError, "internal_error", "failed to read metrics")
|
writeError(w, http.StatusInternalServerError, "internal_error", "failed to read metrics")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
points := make([]metricPointResponse, len(rows))
|
threshold := time.Now().Add(-mapping.cutoff).Unix()
|
||||||
for i, row := range rows {
|
var points []metricPointResponse
|
||||||
points[i] = metricPointResponse{
|
for _, row := range rows {
|
||||||
|
if row.Ts >= threshold {
|
||||||
|
points = append(points, metricPointResponse{
|
||||||
TimestampUnix: row.Ts,
|
TimestampUnix: row.Ts,
|
||||||
CPUPct: row.CpuPct,
|
CPUPct: row.CpuPct,
|
||||||
MemBytes: row.MemBytes,
|
MemBytes: row.MemBytes,
|
||||||
DiskBytes: row.DiskBytes,
|
DiskBytes: row.DiskBytes,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -782,9 +782,9 @@ paths:
|
|||||||
required: false
|
required: false
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
enum: ["10m", "2h", "24h"]
|
enum: ["5m", "10m", "1h", "2h", "6h", "12h", "24h"]
|
||||||
default: "10m"
|
default: "10m"
|
||||||
description: Time range tier to query
|
description: Time range filter to query
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: Metrics retrieved
|
description: Metrics retrieved
|
||||||
@ -2042,7 +2042,7 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
range:
|
range:
|
||||||
type: string
|
type: string
|
||||||
enum: ["10m", "2h", "24h"]
|
enum: ["5m", "10m", "1h", "2h", "6h", "12h", "24h"]
|
||||||
points:
|
points:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
|
|||||||
@ -1348,16 +1348,44 @@ func (m *Manager) GetMetrics(sandboxID, rangeTier string) ([]MetricPoint, error)
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Map the requested range to the appropriate ring tier and time cutoff.
|
||||||
|
var points []MetricPoint
|
||||||
|
var cutoff time.Duration
|
||||||
switch rangeTier {
|
switch rangeTier {
|
||||||
|
case "5m":
|
||||||
|
points = sb.ring.Get10m()
|
||||||
|
cutoff = 5 * time.Minute
|
||||||
case "10m":
|
case "10m":
|
||||||
return sb.ring.Get10m(), nil
|
points = sb.ring.Get10m()
|
||||||
|
cutoff = 10 * time.Minute
|
||||||
|
case "1h":
|
||||||
|
points = sb.ring.Get2h()
|
||||||
|
cutoff = 1 * time.Hour
|
||||||
case "2h":
|
case "2h":
|
||||||
return sb.ring.Get2h(), nil
|
points = sb.ring.Get2h()
|
||||||
|
cutoff = 2 * time.Hour
|
||||||
|
case "6h":
|
||||||
|
points = sb.ring.Get24h()
|
||||||
|
cutoff = 6 * time.Hour
|
||||||
|
case "12h":
|
||||||
|
points = sb.ring.Get24h()
|
||||||
|
cutoff = 12 * time.Hour
|
||||||
case "24h":
|
case "24h":
|
||||||
return sb.ring.Get24h(), nil
|
points = sb.ring.Get24h()
|
||||||
|
cutoff = 24 * time.Hour
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("invalid range: %s (valid: 10m, 2h, 24h)", rangeTier)
|
return nil, fmt.Errorf("invalid range: %s (valid: 5m, 10m, 1h, 2h, 6h, 12h, 24h)", rangeTier)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Filter points to the requested time window.
|
||||||
|
threshold := time.Now().Add(-cutoff)
|
||||||
|
filtered := points[:0:0]
|
||||||
|
for _, p := range points {
|
||||||
|
if !p.Timestamp.Before(threshold) {
|
||||||
|
filtered = append(filtered, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filtered, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FlushMetrics returns all three tier ring buffers, clears the ring, and
|
// FlushMetrics returns all three tier ring buffers, clears the ring, and
|
||||||
|
|||||||
Reference in New Issue
Block a user