/** * Performance Tracker * * Records HTTP request metrics in a fixed-size ring buffer and computes * percentile-based performance statistics over configurable time windows. * Used by the monitoring dashboard to display response time distributions, * error rates, and throughput. */ import type { PerformanceEntry, PerformanceMetrics } from './types' const PERIOD_MS: Record = { '1h': 3_600_000, '6h': 21_600_000, '24h': 86_400_000, '7d': 604_800_000, } const EMPTY_METRICS: PerformanceMetrics = { avgResponseTimeMs: 0, p95ResponseTimeMs: 0, p99ResponseTimeMs: 0, errorRate: 0, requestsPerMinute: 0, } export class PerformanceTracker { private readonly buffer: PerformanceEntry[] private pointer: number = 0 private count: number = 0 private readonly capacity: number constructor(capacity: number = 10_000) { this.capacity = capacity this.buffer = new Array(capacity) } track(method: string, path: string, statusCode: number, durationMs: number): void { this.buffer[this.pointer] = { timestamp: Date.now(), method, path, statusCode, durationMs, } this.pointer = (this.pointer + 1) % this.capacity if (this.count < this.capacity) { this.count++ } } getMetrics(period: '1h' | '6h' | '24h' | '7d' = '1h'): PerformanceMetrics { const cutoff = Date.now() - (PERIOD_MS[period] ?? PERIOD_MS['1h']) const entries: PerformanceEntry[] = [] for (let i = 0; i < this.count; i++) { const entry = this.buffer[i] if (entry && entry.timestamp >= cutoff) { entries.push(entry) } } if (entries.length === 0) { return { ...EMPTY_METRICS } } const durations = entries.map((e) => e.durationMs).sort((a, b) => a - b) const avg = durations.reduce((sum, d) => sum + d, 0) / durations.length const p95 = percentile(durations, 0.95) const p99 = percentile(durations, 0.99) const errorCount = entries.filter((e) => e.statusCode >= 500).length const errorRate = errorCount / entries.length const earliestTimestamp = Math.min(...entries.map((e) => e.timestamp)) const windowMinutes = Math.max((Date.now() - earliestTimestamp) / 60_000, 1) const requestsPerMinute = entries.length / windowMinutes return { avgResponseTimeMs: Math.round(avg), p95ResponseTimeMs: p95, p99ResponseTimeMs: p99, errorRate: Math.round(errorRate * 1000) / 1000, requestsPerMinute: Math.round(requestsPerMinute * 10) / 10, } } } function percentile(sorted: number[], p: number): number { const index = Math.floor(sorted.length * p) return sorted[Math.min(index, sorted.length - 1)] } /** Singleton instance used across the application. */ export const performanceTracker = new PerformanceTracker(10_000)