mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-08-16 20:26:45 +00:00
The dashboard charted one summed disk-usage line, so a step in the total gave no hint which cluster moved. A new panel stacks each cluster's daily size as its own band: the top of the stack is the fleet total, each band is one cluster, and the clusters past the twentieth are summed into an "other" band so the stack still adds up to the total. The series is built from the per-cluster daily histories and served by /api/cluster-sizes. Clusters report roughly once a day at no fixed hour, so a day with no report carries the previous value forward — dropping it to zero would sag the total every day as the clusters that have not reported yet fall out from under it. A cluster that stops reporting past the active window ends at its last sample instead of holding capacity forever. Ranking is by the most recent day, tie-broken on cluster id so the colors do not shuffle between refreshes. Hover and click resolve to the band under the pointer: Chart.js's builtin interaction modes match the nearest line, which on a stack of thin bands is rarely the band being pointed at. Clicking one fills the per-cluster history lookup below it.
123 lines
3.9 KiB
Go
123 lines
3.9 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) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
for _, offset := range dayOffsets {
|
|
s.histories[id] = append(s.histories[id], HistorySample{
|
|
Ts: time.Now().AddDate(0, 0, offset).Unix(),
|
|
TotalDiskBytes: disk,
|
|
})
|
|
}
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
series := s.GetClusterSizeSeries(3, 0)
|
|
if len(series.Clusters) != 1 {
|
|
t.Fatalf("clusters = %+v, want 1", series.Clusters)
|
|
}
|
|
if got := series.Clusters[0].Disk; !equal(got, []uint64{0, 0, 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
|
|
}
|