mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-08-16 12:16:36 +00:00
* telemetry: stack volume servers per cluster over time The fleet-wide server count says how many volume servers reported, but not who they belong to. Carry per-cluster counts in /api/cluster-sizes and draw them the same way as cluster sizes, sharing one cluster ranking so a cluster keeps its colour across both stacks. * telemetry: drop the total disk usage chart from the dashboard The stacked cluster sizes chart right below it has the same fleet total as its stack height, plus the per-cluster breakdown. /api/metrics still serves the aggregate for anyone graphing it elsewhere.
156 lines
5.7 KiB
Go
156 lines
5.7 KiB
Go
package storage
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
"github.com/seaweedfs/seaweedfs/telemetry/proto"
|
|
)
|
|
|
|
// seedSamples gives a cluster one sample per listed day offset (0 is today).
|
|
// 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.
|
|
seedSamples(s, "daily", HistorySample{TotalDiskBytes: 300, VolumeServerCount: 3},
|
|
-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.
|
|
seedSamples(s, "lagging", HistorySample{TotalDiskBytes: 200, VolumeServerCount: 2}, -3, -2)
|
|
// Stopped reporting past the active window: its own days only.
|
|
seedSamples(s, "gone", HistorySample{TotalDiskBytes: 900, VolumeServerCount: 9}, -9, -8)
|
|
// One day of history only: unconfirmed, so it stays out of the stack.
|
|
seedSamples(s, "oneshot", HistorySample{TotalDiskBytes: 400, VolumeServerCount: 4}, -1)
|
|
|
|
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]ClusterSeries{}
|
|
for _, c := range series.Clusters {
|
|
byId[c.ClusterId] = c
|
|
}
|
|
if got := byId["daily"].Disk; !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"].Disk; !equal(got, []uint64{0, 0, 0, 0, 0, 0, 200, 200, 200, 200}) {
|
|
t.Errorf("lagging = %v, want its size carried to the right edge", got)
|
|
}
|
|
if got := byId["gone"].Disk; !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)
|
|
}
|
|
if _, ok := byId["oneshot"]; ok {
|
|
t.Errorf("unconfirmed cluster in the stack: %+v", byId["oneshot"])
|
|
}
|
|
|
|
// Volume servers ride the same axis and the same hold-forward rule.
|
|
if got := byId["daily"].Servers; !equal(got, []uint64{3, 3, 3, 3, 3, 3, 3, 3, 3, 3}) {
|
|
t.Errorf("daily servers = %v, want 3 every day", got)
|
|
}
|
|
if got := byId["lagging"].Servers; !equal(got, []uint64{0, 0, 0, 0, 0, 0, 2, 2, 2, 2}) {
|
|
t.Errorf("lagging servers = %v, want carried to the right edge", got)
|
|
}
|
|
if got := byId["gone"].Servers; !equal(got, []uint64{9, 9, 0, 0, 0, 0, 0, 0, 0, 0}) {
|
|
t.Errorf("gone servers = %v, want none after its last report", got)
|
|
}
|
|
|
|
// The totals are the last day's stack height: daily + lagging, not gone.
|
|
if series.TotalDisk != 500 {
|
|
t.Errorf("total_disk = %d, want 500", series.TotalDisk)
|
|
}
|
|
if series.TotalServers != 5 {
|
|
t.Errorf("total_servers = %d, want 5", series.TotalServers)
|
|
}
|
|
// 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, 200, 200, 200, 200}) {
|
|
t.Errorf("other = %v, want lagging+gone summed per day", series.Other.Disk)
|
|
}
|
|
if !equal(series.Other.Servers, []uint64{9, 9, 0, 0, 0, 0, 2, 2, 2, 2}) {
|
|
t.Errorf("other servers = %v, want lagging+gone summed per day", series.Other.Servers)
|
|
}
|
|
if series.ClusterCount != 3 || series.TotalDisk != 500 || series.TotalServers != 5 {
|
|
t.Errorf("limit changed totals: count=%d disk=%d servers=%d, want 3/500/5",
|
|
series.ClusterCount, series.TotalDisk, series.TotalServers)
|
|
}
|
|
}
|
|
|
|
// 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,
|
|
VolumeServerCount: 4,
|
|
}
|
|
if err := s.StoreTelemetry(data); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
data.TotalDiskBytes = 700
|
|
data.VolumeServerCount = 6
|
|
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 got := series.Clusters[0].Servers; !equal(got, []uint64{6}) {
|
|
t.Errorf("servers = %v, want today's latest sample only", got)
|
|
}
|
|
if series.TotalDisk != 700 || series.TotalServers != 6 {
|
|
t.Errorf("totals = %d disk / %d servers, want 700/6", series.TotalDisk, series.TotalServers)
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|