Files
p2p/tracker/resource_tracker.go

178 lines
4.0 KiB
Go

// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package tracker
import (
"sync"
"time"
"github.com/luxfi/ids"
)
// ResourceManager provides system resource usage metrics
type ResourceManager interface {
// CPUUsage returns the current CPU usage percentage
CPUUsage() float64
// DiskUsage returns the current disk usage percentage
DiskUsage() float64
// Shutdown stops the resource manager
Shutdown()
}
// resourceTracker implements ResourceTracker
type resourceTracker struct {
cpuTracker Tracker
diskTracker Tracker
}
// NewResourceTracker creates a new ResourceTracker
func NewResourceTracker(
manager ResourceManager,
frequency time.Duration,
) (ResourceTracker, error) {
cpuTracker := newUsageTracker(manager.CPUUsage, frequency)
diskTracker := newUsageTracker(manager.DiskUsage, frequency)
return &resourceTracker{
cpuTracker: cpuTracker,
diskTracker: diskTracker,
}, nil
}
func (rt *resourceTracker) CPUTracker() Tracker {
return rt.cpuTracker
}
func (rt *resourceTracker) DiskTracker() Tracker {
return rt.diskTracker
}
// usageTracker implements Tracker for a specific resource
type usageTracker struct {
mu sync.RWMutex
usageFunc func() float64
nodeUsage map[ids.NodeID]*nodeUsageEntry
totalUsage float64
updatePeriod time.Duration
}
type nodeUsageEntry struct {
usage float64
lastUpdate time.Time
}
func newUsageTracker(usageFunc func() float64, updatePeriod time.Duration) Tracker {
return &usageTracker{
usageFunc: usageFunc,
nodeUsage: make(map[ids.NodeID]*nodeUsageEntry),
updatePeriod: updatePeriod,
}
}
func (ut *usageTracker) Usage(nodeID ids.NodeID, now time.Time) float64 {
ut.mu.RLock()
defer ut.mu.RUnlock()
entry, exists := ut.nodeUsage[nodeID]
if !exists {
// If node doesn't exist, return current system usage
return ut.usageFunc()
}
// Decay usage over time
elapsed := now.Sub(entry.lastUpdate)
decayFactor := 1.0 - (elapsed.Seconds() / ut.updatePeriod.Seconds())
if decayFactor < 0 {
decayFactor = 0
}
return entry.usage * decayFactor
}
func (ut *usageTracker) TimeUntilUsage(nodeID ids.NodeID, now time.Time, target float64) time.Duration {
ut.mu.RLock()
defer ut.mu.RUnlock()
currentUsage := ut.Usage(nodeID, now)
if currentUsage <= target {
return 0
}
// Calculate time for usage to decay to target
// Using exponential decay model: usage(t) = usage(0) * e^(-t/τ)
// where τ is the time constant (updatePeriod)
timeToTarget := -ut.updatePeriod.Seconds() *
(target / currentUsage)
if timeToTarget < 0 {
timeToTarget = 0
}
return time.Duration(timeToTarget * float64(time.Second))
}
func (ut *usageTracker) TotalUsage() float64 {
ut.mu.RLock()
defer ut.mu.RUnlock()
return ut.totalUsage
}
// UpdateUsage updates the usage for a specific node
func (ut *usageTracker) UpdateUsage(nodeID ids.NodeID, usage float64, now time.Time) {
ut.mu.Lock()
defer ut.mu.Unlock()
if entry, exists := ut.nodeUsage[nodeID]; exists {
ut.totalUsage -= entry.usage
entry.usage = usage
entry.lastUpdate = now
} else {
ut.nodeUsage[nodeID] = &nodeUsageEntry{
usage: usage,
lastUpdate: now,
}
}
ut.totalUsage += usage
}
// RemoveNode removes a node from tracking
func (ut *usageTracker) RemoveNode(nodeID ids.NodeID) {
ut.mu.Lock()
defer ut.mu.Unlock()
if entry, exists := ut.nodeUsage[nodeID]; exists {
ut.totalUsage -= entry.usage
delete(ut.nodeUsage, nodeID)
}
}
// Manager creates a ResourceManager using the resource package
func Manager() ResourceManager {
// For now, return a simple mock implementation
// The actual resource.NewManager requires many parameters
return &manager{}
}
type manager struct {
}
func (m *manager) CPUUsage() float64 {
// Mock implementation - return default value
return 0.5
}
func (m *manager) DiskUsage() float64 {
// Mock implementation - return default value
return 0.3
}
func (m *manager) Shutdown() {
// Mock implementation - no-op
}