0.2.78
This commit is contained in:
@@ -116,9 +116,9 @@ func adminPreviewData() map[string]any {
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"/admin/api/status": status,
|
||||
"/admin/api/runtime": map[string]any{"goroutines": 84, "heapInuse": 41943040,
|
||||
"sys": 92274688, "numGc": 311, "nextGc": 62914560, "gomaxprocs": 4},
|
||||
"/admin/api/status": status,
|
||||
"/admin/api/runtime": adminPreviewRuntime(now),
|
||||
"/admin/api/runtime/goroutines": adminPreviewGoroutines(now),
|
||||
"/admin/api/accounts": map[string]any{
|
||||
"accounts": []any{
|
||||
map[string]any{"userId": "u-1", "username": "matt", "onboardingCompleted": true,
|
||||
@@ -191,3 +191,123 @@ func adminPreviewData() map[string]any {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// A gateway with a mild, deliberate upward drift in both goroutines and heap, so the
|
||||
// preview shows what a trend that has earned a "watch" actually looks like — the flat case
|
||||
// is the one that draws itself.
|
||||
func adminPreviewRuntime(now time.Time) map[string]any {
|
||||
const points = 90
|
||||
samples := make([]any, 0, points)
|
||||
for index := 0; index < points; index++ {
|
||||
at := now.Add(-time.Duration(points-1-index) * time.Minute)
|
||||
samples = append(samples, map[string]any{
|
||||
"at": at.Format(time.RFC3339),
|
||||
"goroutines": 62 + index/4 + index%3,
|
||||
"heapInuse": 33_000_000 + index*90_000,
|
||||
"sys": 92_274_688,
|
||||
"numGc": 220 + index,
|
||||
})
|
||||
}
|
||||
trend := func(direction string, perHour, first, latest, low, high float64) map[string]any {
|
||||
return map[string]any{
|
||||
"direction": direction, "perHour": perHour, "first": first, "latest": latest,
|
||||
"min": low, "max": high, "spanSeconds": float64((points - 1) * 60), "points": points,
|
||||
}
|
||||
}
|
||||
return map[string]any{
|
||||
"at": now.Format(time.RFC3339), "uptimeSeconds": 61_240.0,
|
||||
"goroutines": 84, "gomaxprocs": 4, "threads": 17, "goVersion": "go1.26",
|
||||
"memory": map[string]any{
|
||||
"heapAlloc": 39_000_000, "heapInuse": 41_943_040, "heapIdle": 24_000_000,
|
||||
"heapReleased": 8_000_000, "stackInuse": 1_800_000, "sys": 223_100_000,
|
||||
"nextGc": 62_914_560, "numGc": 311, "pauseTotalMs": 812.4, "pauseRecentMs": 1.9,
|
||||
"memoryLimit": 402_653_184, "configuredLimit": "384MiB",
|
||||
"heapShare": 0.104, "gcPerHour": 60.0,
|
||||
},
|
||||
"workers": []any{
|
||||
map[string]any{"name": "HTTP listener", "component": "HTTP server",
|
||||
"state": "running", "started": now.Add(-17 * time.Hour).Format(time.RFC3339), "starts": 1},
|
||||
map[string]any{"name": "Emby health probe", "component": "Emby",
|
||||
"state": "running", "started": now.Add(-17 * time.Hour).Format(time.RFC3339), "starts": 1},
|
||||
map[string]any{"name": "Library ingest", "component": "Library sync",
|
||||
"state": "running", "started": now.Add(-17 * time.Hour).Format(time.RFC3339), "starts": 1},
|
||||
map[string]any{"name": "Task scheduler", "component": "Scheduled jobs",
|
||||
"state": "running", "started": now.Add(-17 * time.Hour).Format(time.RFC3339), "starts": 1},
|
||||
map[string]any{"name": "Startup library sync", "component": "Library sync",
|
||||
"state": "finished", "started": now.Add(-17 * time.Hour).Format(time.RFC3339),
|
||||
"stopped": now.Add(-17 * time.Hour).Add(4 * time.Minute).Format(time.RFC3339), "starts": 1},
|
||||
},
|
||||
"process": map[string]any{"pid": 1, "cpuSeconds": 431.8, "cpuPercent": 3.4,
|
||||
"cpuKnown": true, "openFiles": 41, "openSockets": 22, "fileLimit": 1048576,
|
||||
"filesKnown": true},
|
||||
"samples": samples,
|
||||
"goroutineTrend": trend("rising", 14, 62, 84, 62, 86),
|
||||
"heapTrend": trend("rising", 5_400_000, 33_000_000, 41_943_040, 33_000_000, 41_943_040),
|
||||
"reservedTrend": trend("steady", 0, 92_274_688, 92_274_688, 92_274_688, 92_274_688),
|
||||
"sampleEverySeconds": 60.0,
|
||||
"health": map[string]any{
|
||||
"level": "watch",
|
||||
"summary": "2 things to look at, in Goroutines and Memory.",
|
||||
"areas": []any{"Goroutines", "Memory"},
|
||||
"notes": []any{
|
||||
map[string]any{"level": "watch", "area": "Memory",
|
||||
"message": "Heap in use has risen by about 5.1 MB an hour over the last 1.5 hours. If it does not fall after a collection, something is holding on to it."},
|
||||
map[string]any{"level": "watch", "area": "Goroutines",
|
||||
"message": "Goroutines have risen by about 14 an hour over the last 1.5 hours, from 62 to 84. Open the breakdown to see which component they belong to."},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func adminPreviewGoroutines(now time.Time) map[string]any {
|
||||
category := func(key, label, description string, count int, states []any) map[string]any {
|
||||
return map[string]any{"category": key, "label": label, "description": description,
|
||||
"count": count, "states": states}
|
||||
}
|
||||
state := func(name string, count int) any {
|
||||
return map[string]any{"state": name, "count": count}
|
||||
}
|
||||
return map[string]any{
|
||||
"at": now.Format(time.RFC3339), "total": 84, "collectedInMs": 6.2, "dumpBytes": 98_304,
|
||||
"categories": []any{
|
||||
category("running", "Running", "On a processor now, or queued for one.", 3,
|
||||
[]any{state("running", 2), state("runnable", 1)}),
|
||||
category("io", "Network / I/O",
|
||||
"Waiting on a network read or write — Emby, Postgres, Redis, or a television.", 26,
|
||||
[]any{state("IO wait", 26)}),
|
||||
category("waiting", "Waiting / idle",
|
||||
"Parked waiting for work or for a lock. Idle, and costing almost nothing.", 41,
|
||||
[]any{state("select", 29), state("chan receive", 9), state("semacquire", 3)}),
|
||||
category("timers", "Timers / scheduled work", "Asleep until a scheduled time.", 8,
|
||||
[]any{state("sleep", 8)}),
|
||||
category("runtime", "Go runtime / collection",
|
||||
"The Go runtime's own housekeeping. This handful is always present.", 6,
|
||||
[]any{state("GC worker (idle)", 4), state("finalizer wait", 1), state("force gc (idle)", 1)}),
|
||||
},
|
||||
"components": []any{
|
||||
map[string]any{"component": "HTTP server", "count": 24, "longestWaitMinutes": 3},
|
||||
map[string]any{"component": "Database pool", "count": 18, "longestWaitMinutes": 940},
|
||||
map[string]any{"component": "Emby", "count": 11, "longestWaitMinutes": 2},
|
||||
map[string]any{"component": "Gateway API", "count": 9, "longestWaitMinutes": 0},
|
||||
map[string]any{"component": "Redis", "count": 8, "longestWaitMinutes": 940},
|
||||
map[string]any{"component": "Library sync", "count": 6, "longestWaitMinutes": 940},
|
||||
map[string]any{"component": "Go runtime", "count": 6, "longestWaitMinutes": 0},
|
||||
map[string]any{"component": "Scheduled jobs", "count": 2, "longestWaitMinutes": 940},
|
||||
},
|
||||
"groups": []any{
|
||||
map[string]any{"count": 24, "component": "HTTP server", "category": "io",
|
||||
"state": "IO wait", "function": "net/http.(*conn).serve",
|
||||
"file": "/usr/local/go/src/net/http/server.go:2092",
|
||||
"createdBy": "net/http.(*Server).Serve", "longestWaitMinutes": 3},
|
||||
map[string]any{"count": 18, "component": "Database pool", "category": "waiting",
|
||||
"state": "select", "function": "github.com/jackc/puddle/v2.(*Pool).acquire",
|
||||
"file": "/root/go/pkg/mod/github.com/jackc/puddle/v2/pool.go:481",
|
||||
"createdBy": "github.com/jackc/pgx/v5/pgxpool.NewWithConfig", "longestWaitMinutes": 940},
|
||||
map[string]any{"count": 6, "component": "Library sync", "category": "waiting",
|
||||
"state": "chan receive", "function": "memby/server/internal/library.(*Ingester).Run",
|
||||
"file": "/app/internal/library/ingest.go:118",
|
||||
"createdBy": "main.run", "longestWaitMinutes": 940},
|
||||
},
|
||||
"groupsTotal": 21,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user