enki 7c92aa3ded
Some checks are pending
CI Pipeline / Run Tests (push) Waiting to run
CI Pipeline / Lint Code (push) Waiting to run
CI Pipeline / Security Scan (push) Waiting to run
CI Pipeline / E2E Tests (push) Blocked by required conditions
Major DHT and Torrent fixes.
2025-08-29 21:18:36 -07:00

277 lines
8.2 KiB
Go

package stats
import (
"sync"
"time"
)
// ComponentStats represents statistics for a specific component
type ComponentStats struct {
Component string `json:"component"`
Timestamp time.Time `json:"timestamp"`
Queries int64 `json:"queries"`
Errors int64 `json:"errors"`
ResponseTime float64 `json:"response_time_ms"`
Connections int64 `json:"connections"`
BytesServed int64 `json:"bytes_served"`
Metadata map[string]interface{} `json:"metadata"`
}
// DHTStats represents DHT-specific statistics
type DHTStats struct {
QueriesSent int64 `json:"queries_sent"`
QueriesReceived int64 `json:"queries_received"`
NodesInTable int `json:"nodes_in_routing_table"`
StoredPeers int `json:"stored_peers"`
AnnouncesSent int64 `json:"announces_sent"`
PeersSeen int64 `json:"peers_seen"`
LastAnnounce time.Time `json:"last_announce"`
ErrorRate float64 `json:"error_rate"`
}
// TrackerStats represents tracker-specific statistics
type TrackerStats struct {
ActiveTorrents int `json:"active_torrents"`
TotalPeers int `json:"total_peers"`
AnnouncesPerMin float64 `json:"announces_per_minute"`
ScrapeRequests int64 `json:"scrape_requests"`
AverageSwarmSize float64 `json:"average_swarm_size"`
LastActivity time.Time `json:"last_activity"`
}
// GatewayStats represents gateway-specific statistics
type GatewayStats struct {
UploadsPerHour float64 `json:"uploads_per_hour"`
DownloadsPerHour float64 `json:"downloads_per_hour"`
BandwidthUsed int64 `json:"bandwidth_used_bytes"`
ActiveUploads int `json:"active_uploads"`
ActiveDownloads int `json:"active_downloads"`
CacheHitRate float64 `json:"cache_hit_rate"`
AverageFileSize int64 `json:"average_file_size"`
}
// WebSocketStats represents WebSocket tracker statistics
type WebSocketStats struct {
ActiveConnections int `json:"active_connections"`
WebRTCPeers int `json:"webrtc_peers"`
MessagesPerSec float64 `json:"messages_per_second"`
ConnectionErrors int64 `json:"connection_errors"`
PeerExchanges int64 `json:"peer_exchanges"`
AverageLatency float64 `json:"average_latency_ms"`
}
// SystemStats represents overall system performance
type SystemStats struct {
CPUUsage float64 `json:"cpu_usage_percent"`
MemoryUsage int64 `json:"memory_usage_bytes"`
GoroutineCount int `json:"goroutine_count"`
ResponseTime float64 `json:"avg_response_time_ms"`
RequestsPerSec float64 `json:"requests_per_second"`
}
// BandwidthStats represents bandwidth usage tracking
type BandwidthStats struct {
TorrentHash string `json:"torrent_hash"`
BytesServed int64 `json:"bytes_served"`
BytesFromPeers int64 `json:"bytes_from_peers"`
P2POffloadPercent float64 `json:"p2p_offload_percent"`
PeerCount int `json:"peer_count"`
Timestamp time.Time `json:"timestamp"`
}
// TimeSeriesPoint represents a single point in time-series data
type TimeSeriesPoint struct {
Timestamp time.Time `json:"timestamp"`
Value float64 `json:"value"`
Component string `json:"component"`
Metric string `json:"metric"`
}
// StatsCollector manages collection and aggregation of statistics
type StatsCollector struct {
mutex sync.RWMutex
dhtStats *DHTStats
trackerStats *TrackerStats
gatewayStats *GatewayStats
wsStats *WebSocketStats
systemStats *SystemStats
bandwidthMap map[string]*BandwidthStats
// Rate tracking
requestCounts map[string]int64
errorCounts map[string]int64
lastReset time.Time
// Performance tracking
responseTimes map[string][]float64
activeConns int64
}
// NewStatsCollector creates a new statistics collector
func NewStatsCollector() *StatsCollector {
return &StatsCollector{
dhtStats: &DHTStats{},
trackerStats: &TrackerStats{},
gatewayStats: &GatewayStats{},
wsStats: &WebSocketStats{},
systemStats: &SystemStats{},
bandwidthMap: make(map[string]*BandwidthStats),
requestCounts: make(map[string]int64),
errorCounts: make(map[string]int64),
responseTimes: make(map[string][]float64),
lastReset: time.Now(),
}
}
// RecordRequest increments request count for a component
func (sc *StatsCollector) RecordRequest(component string) {
sc.mutex.Lock()
defer sc.mutex.Unlock()
sc.requestCounts[component]++
}
// RecordError increments error count for a component
func (sc *StatsCollector) RecordError(component string) {
sc.mutex.Lock()
defer sc.mutex.Unlock()
sc.errorCounts[component]++
}
// RecordResponseTime records response time for a component
func (sc *StatsCollector) RecordResponseTime(component string, duration time.Duration) {
sc.mutex.Lock()
defer sc.mutex.Unlock()
ms := float64(duration.Nanoseconds()) / 1e6
if sc.responseTimes[component] == nil {
sc.responseTimes[component] = make([]float64, 0, 100)
}
sc.responseTimes[component] = append(sc.responseTimes[component], ms)
// Keep only last 100 measurements
if len(sc.responseTimes[component]) > 100 {
sc.responseTimes[component] = sc.responseTimes[component][1:]
}
}
// RecordBandwidth records bandwidth usage for a torrent
func (sc *StatsCollector) RecordBandwidth(torrentHash string, bytesServed, bytesFromPeers int64, peerCount int) {
sc.mutex.Lock()
defer sc.mutex.Unlock()
var p2pOffload float64
if bytesServed > 0 {
p2pOffload = float64(bytesFromPeers) / float64(bytesServed) * 100
}
sc.bandwidthMap[torrentHash] = &BandwidthStats{
TorrentHash: torrentHash,
BytesServed: bytesServed,
BytesFromPeers: bytesFromPeers,
P2POffloadPercent: p2pOffload,
PeerCount: peerCount,
Timestamp: time.Now(),
}
}
// GetAverageResponseTime calculates average response time for a component
func (sc *StatsCollector) GetAverageResponseTime(component string) float64 {
sc.mutex.RLock()
defer sc.mutex.RUnlock()
times := sc.responseTimes[component]
if len(times) == 0 {
return 0
}
var sum float64
for _, t := range times {
sum += t
}
return sum / float64(len(times))
}
// GetRequestRate calculates requests per second for a component
func (sc *StatsCollector) GetRequestRate(component string) float64 {
sc.mutex.RLock()
defer sc.mutex.RUnlock()
duration := time.Since(sc.lastReset).Seconds()
if duration == 0 {
return 0
}
return float64(sc.requestCounts[component]) / duration
}
// GetErrorRate calculates error rate percentage for a component
func (sc *StatsCollector) GetErrorRate(component string) float64 {
sc.mutex.RLock()
defer sc.mutex.RUnlock()
requests := sc.requestCounts[component]
if requests == 0 {
return 0
}
return float64(sc.errorCounts[component]) / float64(requests) * 100
}
// ResetCounters resets rate-based counters (called periodically)
func (sc *StatsCollector) ResetCounters() {
sc.mutex.Lock()
defer sc.mutex.Unlock()
// Reset counters but keep running totals for rates
sc.requestCounts = make(map[string]int64)
sc.errorCounts = make(map[string]int64)
sc.lastReset = time.Now()
}
// UpdateDHTStats updates DHT statistics
func (sc *StatsCollector) UpdateDHTStats(stats *DHTStats) {
sc.mutex.Lock()
defer sc.mutex.Unlock()
sc.dhtStats = stats
}
// UpdateTrackerStats updates tracker statistics
func (sc *StatsCollector) UpdateTrackerStats(stats *TrackerStats) {
sc.mutex.Lock()
defer sc.mutex.Unlock()
sc.trackerStats = stats
}
// UpdateGatewayStats updates gateway statistics
func (sc *StatsCollector) UpdateGatewayStats(stats *GatewayStats) {
sc.mutex.Lock()
defer sc.mutex.Unlock()
sc.gatewayStats = stats
}
// UpdateWebSocketStats updates WebSocket statistics
func (sc *StatsCollector) UpdateWebSocketStats(stats *WebSocketStats) {
sc.mutex.Lock()
defer sc.mutex.Unlock()
sc.wsStats = stats
}
// UpdateSystemStats updates system statistics
func (sc *StatsCollector) UpdateSystemStats(stats *SystemStats) {
sc.mutex.Lock()
defer sc.mutex.Unlock()
sc.systemStats = stats
}
// GetSnapshot returns a complete snapshot of current statistics
func (sc *StatsCollector) GetSnapshot() map[string]interface{} {
sc.mutex.RLock()
defer sc.mutex.RUnlock()
return map[string]interface{}{
"timestamp": time.Now().Format(time.RFC3339),
"dht": sc.dhtStats,
"tracker": sc.trackerStats,
"gateway": sc.gatewayStats,
"websocket": sc.wsStats,
"system": sc.systemStats,
"bandwidth": sc.bandwidthMap,
}
}