fix for overview panel
This commit is contained in:
parent
21d8e91756
commit
d16f34070d
21 changed files with 920 additions and 40 deletions
88
internal/hoststats/hoststats.go
Normal file
88
internal/hoststats/hoststats.go
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
// Package hoststats samples host-level CPU/RAM/disk usage for the admin
|
||||
// panel's dashboard. It intentionally reports the machine's own resources,
|
||||
// not the Go process's (runtime.MemStats already covers that elsewhere) --
|
||||
// on a single self-hosted box the two are the same box, but the metric an
|
||||
// operator wants here is "is this server about to fall over."
|
||||
package hoststats
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Snapshot is the last successfully sampled host-resource reading. Ready is
|
||||
// false until the first sample completes, so callers can distinguish "0% CPU"
|
||||
// from "no data yet" instead of rendering a misleading zero on startup.
|
||||
type Snapshot struct {
|
||||
CPUPercent float64
|
||||
MemUsedBytes int64
|
||||
MemTotalBytes int64
|
||||
DiskFreeBytes int64
|
||||
DiskTotalBytes int64
|
||||
Ready bool
|
||||
}
|
||||
|
||||
// Poller periodically samples host stats and caches the last snapshot for
|
||||
// lock-cheap reads from HTTP handlers -- the same "background worker
|
||||
// refreshes, handler reads a cached value" split this codebase already uses
|
||||
// for the local blob-storage free-space guard.
|
||||
type Poller struct {
|
||||
diskPath string
|
||||
|
||||
mu sync.RWMutex
|
||||
snap Snapshot
|
||||
|
||||
cpu cpuSampler
|
||||
}
|
||||
|
||||
// NewPoller creates a poller that reports free/total disk space for the
|
||||
// filesystem containing diskPath (pass the server's data/blob directory, or
|
||||
// "." if it doesn't matter which volume).
|
||||
func NewPoller(diskPath string) *Poller {
|
||||
if diskPath == "" {
|
||||
diskPath = "."
|
||||
}
|
||||
return &Poller{diskPath: diskPath}
|
||||
}
|
||||
|
||||
// Snapshot returns the last sample. Safe to call concurrently with Run.
|
||||
func (p *Poller) Snapshot() Snapshot {
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
return p.snap
|
||||
}
|
||||
|
||||
// Run samples immediately, then on every tick of interval, until ctx is
|
||||
// canceled. CPU usage is a delta between successive samples, so the first
|
||||
// sample after startup reports 0% -- expected, not a bug, and Ready still
|
||||
// flips true for the memory/disk figures that don't need a delta.
|
||||
func (p *Poller) Run(ctx context.Context, interval time.Duration) {
|
||||
p.sampleOnce()
|
||||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
p.sampleOnce()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Poller) sampleOnce() {
|
||||
var snap Snapshot
|
||||
snap.CPUPercent = p.cpu.sample()
|
||||
if used, total, err := memStats(); err == nil {
|
||||
snap.MemUsedBytes, snap.MemTotalBytes = used, total
|
||||
}
|
||||
if free, total, err := diskFreeBytes(p.diskPath); err == nil {
|
||||
snap.DiskFreeBytes, snap.DiskTotalBytes = free, total
|
||||
}
|
||||
snap.Ready = true
|
||||
|
||||
p.mu.Lock()
|
||||
p.snap = snap
|
||||
p.mu.Unlock()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue