Files
seaweedfs/telemetry/server/storage/sizes_test.go
T
Chris Lu c8cafd8a1a telemetry: fix total disk usage over time (#10476)
* telemetry: total disk usage over time counted each cluster on one day

GetMetrics aggregated s.instances, which holds only each cluster's most
recent report. Every cluster therefore landed in a single date bucket --
the day it last reported on -- so the chart plotted the disk usage of
clusters that went silent that day, and piled the whole live fleet onto
today. Aggregate the daily histories instead, reusing the day alignment
that the per-cluster size series already does.

* telemetry: don't pad the charts with days the server has no history for

The dashboard asks for 30 days, but daily history only starts when a
server first collects it, so the charts opened on a run of zeros and then
jumped -- reading as a fleet that appeared overnight. Start the window at
the oldest sample on hand when it is younger than the requested range.
2026-07-28 16:22:29 -07:00

132 lines
4.4 KiB
Go

package storage
import (
"testing"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/seaweedfs/seaweedfs/telemetry/proto"
)
// seedHistory gives a cluster one sample per listed day offset (0 is today).
func seedHistory(s *PrometheusStorage, id string, disk uint64, dayOffsets ...int) {
seedSamples(s, id, HistorySample{TotalDiskBytes: disk}, dayOffsets...)
}
// seedSamples is seedHistory for tests that care about more than disk usage.
// The sample's Ts is filled in per day offset.
func seedSamples(s *PrometheusStorage, id string, sample HistorySample, dayOffsets ...int) {
s.mu.Lock()
defer s.mu.Unlock()
for _, offset := range dayOffsets {
sample.Ts = time.Now().AddDate(0, 0, offset).Unix()
s.histories[id] = append(s.histories[id], sample)
}
}
func TestClusterSizeSeries(t *testing.T) {
s := newPrometheusStorage(prometheus.NewRegistry())
// Reported every day of the window.
seedHistory(s, "daily", 300, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0)
// Reported two days ago and not since: still active, so its size is held
// to the right edge instead of dropping out of the stack.
seedHistory(s, "lagging", 200, -2)
// Stopped reporting past the active window: its own days only.
seedHistory(s, "gone", 900, -9, -8)
series := s.GetClusterSizeSeries(10, 0)
if len(series.Dates) != 10 {
t.Fatalf("dates = %v, want 10 days", series.Dates)
}
if series.ClusterCount != 3 {
t.Fatalf("cluster_count = %d, want 3", series.ClusterCount)
}
byId := map[string][]uint64{}
for _, c := range series.Clusters {
byId[c.ClusterId] = c.Disk
}
if got := byId["daily"]; !equal(got, []uint64{300, 300, 300, 300, 300, 300, 300, 300, 300, 300}) {
t.Errorf("daily = %v, want 300 every day", got)
}
if got := byId["lagging"]; !equal(got, []uint64{0, 0, 0, 0, 0, 0, 0, 200, 200, 200}) {
t.Errorf("lagging = %v, want its size carried to the right edge", got)
}
if got := byId["gone"]; !equal(got, []uint64{900, 900, 0, 0, 0, 0, 0, 0, 0, 0}) {
t.Errorf("gone = %v, want no capacity after its last report", got)
}
// The total is the last day's stack height: daily + lagging, not gone.
if series.TotalDisk != 500 {
t.Errorf("total_disk = %d, want 500", series.TotalDisk)
}
// Largest on the last day comes first so the stack reads top-down.
if series.Clusters[0].ClusterId != "daily" {
t.Errorf("order = %s first, want daily", series.Clusters[0].ClusterId)
}
// Clusters past the limit are summed into "other", per day.
series = s.GetClusterSizeSeries(10, 1)
if len(series.Clusters) != 1 || series.Clusters[0].ClusterId != "daily" {
t.Fatalf("limited clusters = %+v, want just daily", series.Clusters)
}
if series.Other == nil || series.Other.Count != 2 {
t.Fatalf("other = %+v, want 2 clusters", series.Other)
}
if !equal(series.Other.Disk, []uint64{900, 900, 0, 0, 0, 0, 0, 200, 200, 200}) {
t.Errorf("other = %v, want lagging+gone summed per day", series.Other.Disk)
}
if series.ClusterCount != 3 || series.TotalDisk != 500 {
t.Errorf("limit changed totals: count=%d disk=%d, want 3/500", series.ClusterCount, series.TotalDisk)
}
}
// A report that lands after an earlier one on the same day replaces it, so the
// series shows one value per cluster per day.
func TestClusterSizeSeriesUsesLatestDailySample(t *testing.T) {
s := newPrometheusStorage(prometheus.NewRegistry())
data := &proto.TelemetryData{
TopologyId: "aaaaaaaa-0000-0000-0000-000000000001",
Version: "4.40",
Os: "linux/amd64",
TotalDiskBytes: 100,
}
if err := s.StoreTelemetry(data); err != nil {
t.Fatal(err)
}
data.TotalDiskBytes = 700
if err := s.StoreTelemetry(data); err != nil {
t.Fatal(err)
}
// Nothing was reported before today, so the axis is today alone rather
// than the full 3 days padded out with zeros.
series := s.GetClusterSizeSeries(3, 0)
if len(series.Clusters) != 1 {
t.Fatalf("clusters = %+v, want 1", series.Clusters)
}
if today := time.Now().UTC().Format("2006-01-02"); len(series.Dates) != 1 || series.Dates[0] != today {
t.Errorf("dates = %v, want %s only", series.Dates, today)
}
if got := series.Clusters[0].Disk; !equal(got, []uint64{700}) {
t.Errorf("disk = %v, want today's latest sample only", got)
}
if series.TotalDisk != 700 {
t.Errorf("total_disk = %d, want 700", series.TotalDisk)
}
}
func equal(a, b []uint64) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}