diff options
Diffstat (limited to 'internal/stats/stats.go')
| -rw-r--r-- | internal/stats/stats.go | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/internal/stats/stats.go b/internal/stats/stats.go new file mode 100644 index 0000000..cd850a3 --- /dev/null +++ b/internal/stats/stats.go @@ -0,0 +1,170 @@ +package stats + +import ( + "math" + "sort" + "sync" + "time" +) + +// IOStats represents statistical data for I/O operations +type IOStats struct { + mu sync.RWMutex + + // Basic metrics + Count int64 + TotalBytes int64 + TotalLatency time.Duration + + // Latency statistics + MinLatency time.Duration + MaxLatency time.Duration + MeanLatency time.Duration + MedianLatency time.Duration + P95Latency time.Duration + P99Latency time.Duration + + // Throughput statistics + MinBytes int64 + MaxBytes int64 + MeanBytes float64 + MedianBytes int64 + P95Bytes int64 + P99Bytes int64 + + // Time series data for trend analysis + latencyHistory []time.Duration + bytesHistory []int64 + timestamps []time.Time +} + +// NewIOStats creates a new IOStats instance +func NewIOStats() *IOStats { + return &IOStats{ + MinLatency: time.Duration(math.MaxInt64), + MinBytes: math.MaxInt64, + } +} + +// AddOperation adds a new I/O operation to the statistics +func (s *IOStats) AddOperation(bytes int64, latency time.Duration) { + s.mu.Lock() + defer s.mu.Unlock() + + now := time.Now() + s.Count++ + s.TotalBytes += bytes + s.TotalLatency += latency + + // Update latency statistics + if latency < s.MinLatency { + s.MinLatency = latency + } + if latency > s.MaxLatency { + s.MaxLatency = latency + } + + // Update bytes statistics + if bytes < s.MinBytes { + s.MinBytes = bytes + } + if bytes > s.MaxBytes { + s.MaxBytes = bytes + } + + // Store history + s.latencyHistory = append(s.latencyHistory, latency) + s.bytesHistory = append(s.bytesHistory, bytes) + s.timestamps = append(s.timestamps, now) + + // Calculate percentiles if we have enough data + if len(s.latencyHistory) > 0 { + s.calculatePercentiles() + } +} + +// calculatePercentiles calculates various statistical measures +func (s *IOStats) calculatePercentiles() { + // Sort the data for percentile calculations + latencies := make([]time.Duration, len(s.latencyHistory)) + copy(latencies, s.latencyHistory) + sort.Slice(latencies, func(i, j int) bool { + return latencies[i] < latencies[j] + }) + + bytes := make([]int64, len(s.bytesHistory)) + copy(bytes, s.bytesHistory) + sort.Slice(bytes, func(i, j int) bool { + return bytes[i] < bytes[j] + }) + + // Calculate mean + var totalLatency time.Duration + var totalBytes int64 + for i := range latencies { + totalLatency += latencies[i] + totalBytes += bytes[i] + } + s.MeanLatency = totalLatency / time.Duration(len(latencies)) + s.MeanBytes = float64(totalBytes) / float64(len(bytes)) + + // Calculate median and percentiles + mid := len(latencies) / 2 + s.MedianLatency = latencies[mid] + s.MedianBytes = bytes[mid] + + p95Index := int(float64(len(latencies)) * 0.95) + p99Index := int(float64(len(latencies)) * 0.99) + if p95Index < len(latencies) { + s.P95Latency = latencies[p95Index] + s.P95Bytes = bytes[p95Index] + } + if p99Index < len(latencies) { + s.P99Latency = latencies[p99Index] + s.P99Bytes = bytes[p99Index] + } +} + +// GetStats returns a map of all statistics +func (s *IOStats) GetStats() map[string]interface{} { + s.mu.RLock() + defer s.mu.RUnlock() + + return map[string]interface{}{ + "count": s.Count, + "total_bytes": s.TotalBytes, + "total_latency": s.TotalLatency, + "latency": map[string]interface{}{ + "min": s.MinLatency, + "max": s.MaxLatency, + "mean": s.MeanLatency, + "median": s.MedianLatency, + "p95": s.P95Latency, + "p99": s.P99Latency, + }, + "bytes": map[string]interface{}{ + "min": s.MinBytes, + "max": s.MaxBytes, + "mean": s.MeanBytes, + "median": s.MedianBytes, + "p95": s.P95Bytes, + "p99": s.P99Bytes, + }, + } +} + +// GetTimeSeriesData returns the historical data for trend analysis +func (s *IOStats) GetTimeSeriesData() ([]time.Time, []time.Duration, []int64) { + s.mu.RLock() + defer s.mu.RUnlock() + + timestamps := make([]time.Time, len(s.timestamps)) + latencies := make([]time.Duration, len(s.latencyHistory)) + bytes := make([]int64, len(s.bytesHistory)) + + copy(timestamps, s.timestamps) + copy(latencies, s.latencyHistory) + copy(bytes, s.bytesHistory) + + return timestamps, latencies, bytes +} |
