mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-08 15:41:15 +02:00
* telemetry: tidy the server module after the protobuf bump Claude-Session: https://claude.ai/code/session_01VGiphDxpsKMwu9XpUFhybQ * telemetry: keep only clusters that store at least 10 GiB Fresh weed server runs, CI jobs and throwaway containers each mint their own cluster id. They came in at tens of thousands a day, were most of the counted clusters and held almost none of the bytes, and the state file and the metrics page grew with every one of them. Reports under the floor are counted and dropped, and a state file written before the floor sheds them on the first restart. Claude-Session: https://claude.ai/code/session_01VGiphDxpsKMwu9XpUFhybQ * master: report telemetry only once the cluster stores 10 GiB A throwaway cluster no longer registers itself with its first report a minute after start; a real one begins reporting at the first daily tick after it crosses the floor. Claude-Session: https://claude.ai/code/session_01VGiphDxpsKMwu9XpUFhybQ
158 lines
5.9 KiB
Go
158 lines
5.9 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)
|
|
// Stopped reporting two days ago: 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},
|
|
-8, -7, -6, -5, -4, -3, -2)
|
|
// Stopped reporting past the active window: its own days only.
|
|
seedSamples(s, "gone", HistorySample{TotalDiskBytes: 900, VolumeServerCount: 9},
|
|
-14, -13, -12, -11, -10, -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, 200, 200, 200, 200, 200, 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, 2, 2, 2, 2, 2, 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, 1100, 200, 200, 200, 200, 200, 200, 200, 200}) {
|
|
t.Errorf("other = %v, want lagging+gone summed per day", series.Other.Disk)
|
|
}
|
|
if !equal(series.Other.Servers, []uint64{9, 11, 2, 2, 2, 2, 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: proto.MinDiskBytes + 100,
|
|
VolumeServerCount: 4,
|
|
}
|
|
if err := s.StoreTelemetry(data); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
data.TotalDiskBytes = proto.MinDiskBytes + 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{proto.MinDiskBytes + 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 != proto.MinDiskBytes+700 || series.TotalServers != 6 {
|
|
t.Errorf("totals = %d disk / %d servers, want %d/6", series.TotalDisk, series.TotalServers, proto.MinDiskBytes+700)
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|