Documentation
¶
Overview ¶
Package hoststat reads process-tree, network and GPU presence for soak telemetry. It does not signal processes.
Index ¶
- Constants
- func BucketStart(t time.Time, span int, loc *time.Location) time.Time
- func GPU(devRoot string) string
- func NetDev(procRoot string) (rx, tx uint64, err error)
- type FilesystemCapacity
- type GPUProvider
- type GPUReading
- type Metric
- type MetricState
- type NodeOptions
- type NodeReading
- type NodeResources
- type NodeSampler
- type NodeSnapshot
- type Options
- type Resources
- type Sample
- type ScratchSnapshot
- type Telemetry
- type Tree
Constants ¶
const ( SpanYear = 0 SpanMonth = 1 SpanWeek = 2 SpanDay = 3 SpanHour = 4 SpanSec = 6 )
Timespan values match the Plex Dashboard bucket sizes: 6 second, 4 hour, 3 day, 2 ISO week, 1 month, 0 year. Resources only keeps per-second rows on the wire; coarser tiers exist so old samples stay bounded.
Variables ¶
This section is empty.
Functions ¶
func BucketStart ¶
BucketStart is the inclusive start of the timespan bucket that contains t in loc (the server's local zone).
Types ¶
type FilesystemCapacity ¶
type GPUProvider ¶
type GPUProvider interface {
SampleGPU(context.Context) ([]GPUReading, error)
}
type GPUReading ¶
type GPUReading struct {
Device string `json:"device"`
Capabilities []string `json:"capabilities"`
UtilizationPercent Metric[float64] `json:"utilization_percent"`
}
GPUReading is optional provider evidence, not inferred from device presence. Providers must supply bounded device IDs and verified capability strings.
type Metric ¶
type Metric[T any] struct { Value *T `json:"value"` At time.Time `json:"at"` State MetricState `json:"state"` }
Metric retains the original observation time. Stale may have a nil Value when a formerly unlimited local constraint expires. Only Available values may be used for admission; Stale values are diagnostic, never free capacity.
type MetricState ¶
type MetricState string
MetricState distinguishes an unknown reading from a measured zero. Unlimited means the configured cgroup explicitly has no local limit, not infinite host capacity.
const ( Available MetricState = "available" Stale MetricState = "stale" Unlimited MetricState = "unlimited" MaxScratchPaths = 16 MaxGPUDevices = 16 )
type NodeOptions ¶
type NodeOptions struct {
Now func() time.Time
Interval time.Duration // default 5s; minimum 1s
MaxAge time.Duration // default 3 intervals; at least one interval
SampleTimeout time.Duration // default 1s; at most one interval
ScratchPaths []string // explicit absolute paths; no recursive scans
CgroupV2Path string // optional explicit absolute cgroup directory, Linux only
Sampler NodeSampler
GPU GPUProvider
Filesystem func(context.Context, string) (FilesystemCapacity, error)
}
NodeOptions bounds retained data and sampling frequency. SampleTimeout is cooperative, not a syscall preemption guarantee: providers must honor context, and native filesystem sampling expects local paths. A hung remote mount may delay Run; cached Snapshot reads continue and age their observations.
type NodeReading ¶
type NodeReading struct {
HostCPUPercent Metric[float64] `json:"host_cpu_percent"`
ProcessCPUPercent Metric[float64] `json:"process_cpu_percent"`
HostMemoryTotalBytes Metric[uint64] `json:"host_memory_total_bytes"`
HostMemoryAvailableBytes Metric[uint64] `json:"host_memory_available_bytes"`
ProcessRSSBytes Metric[uint64] `json:"process_rss_bytes"`
// Cgroup fields describe ONLY the explicitly configured cgroup-v2 directory.
// Ancestor limits/cpusets are not resolved: these are not effective pod capacity.
CgroupMemoryLimitBytes Metric[uint64] `json:"cgroup_memory_limit_bytes"`
CgroupMemoryCurrentBytes Metric[uint64] `json:"cgroup_memory_current_bytes"`
CgroupCPUQuotaCores Metric[float64] `json:"cgroup_cpu_quota_cores"`
}
NodeReading separates measurement scopes. CPU percentages use total host CPU capacity as denominator; ProcessRSSBytes is this process only (no children). Host readings in a container describe the host view, not pod capacity.
type NodeResources ¶
type NodeResources struct {
// contains filtered or unexported fields
}
NodeResources holds exactly one snapshot, independently of Plex Resources. Construction does no I/O and starts no goroutines. The owner starts Run and cancels its context, then waits for Run to return before releasing providers.
func NewNodeResources ¶
func NewNodeResources(opts NodeOptions) (*NodeResources, error)
func (*NodeResources) Run ¶
func (r *NodeResources) Run(ctx context.Context) error
Run samples serially until cancellation. Callers own the goroutine and must wait for its return. Cancellation and SampleTimeout are cooperative; neither can preempt a native filesystem syscall or a provider that ignores context.
func (*NodeResources) Snapshot ¶
func (r *NodeResources) Snapshot() NodeSnapshot
type NodeSampler ¶
type NodeSampler interface {
Sample(context.Context) (NodeReading, error)
}
NodeSampler and GPUProvider must honor ctx promptly and must not retain or mutate returned data. Run invokes providers serially (no spawned I/O goroutines). A provider that ignores context delays Run shutdown; it is never abandoned in a background goroutine. Snapshot never waits on a provider or performs I/O.
type NodeSnapshot ¶
type NodeSnapshot struct {
NodeReading
Scratch []ScratchSnapshot `json:"scratch"`
GPU []GPUReading `json:"gpu"`
GPUState MetricState `json:"gpu_state"`
GPUAt time.Time `json:"gpu_at"`
}
type Resources ¶
type Resources struct {
// contains filtered or unexported fields
}
Resources holds a bounded ring of host samples for this node. Clustered nodes each sample themselves; a client reads the node it connected to.
func NewResources ¶
NewResources starts empty. Run samples once a second; tests call Add.
type Sample ¶
type Sample struct {
At time.Time
HostCPU float64
ProcessCPU float64
HostMem float64
ProcessMem float64
// contains filtered or unexported fields
}
Sample is one host/process observation at a bucket start. n is the number of observations folded into the averages and is not on the wire.
type ScratchSnapshot ¶
type ScratchSnapshot struct {
Path string `json:"path"`
FilesystemCapacity
}
type Telemetry ¶
type Telemetry struct {
At time.Time `json:"at"`
RSSBytes uint64 `json:"rss_bytes"`
SelfRSS uint64 `json:"self_rss_bytes"`
HeapAlloc uint64 `json:"heap_alloc"`
HeapInuse uint64 `json:"heap_inuse"`
HeapSys uint64 `json:"heap_sys"`
HeapIdle uint64 `json:"heap_idle"`
HeapReleased uint64 `json:"heap_released"`
GOGC int `json:"gogc"`
FDs int `json:"fds"`
SQLiteFDs int `json:"sqlite_fds"`
Goroutines int `json:"goroutines"`
FFmpeg int `json:"ffmpeg"`
Sessions int `json:"sessions"`
Transcodes int `json:"transcodes"`
Children int `json:"children"`
RetryQueue int `json:"retry_queue"`
NetRx uint64 `json:"net_rx_bytes"`
NetTx uint64 `json:"net_tx_bytes"`
GPU string `json:"gpu"`
Valid bool `json:"valid"`
Error string `json:"error,omitempty"`
}
Telemetry is one soak sample. Session counts are supplied by the caller.