package metrics import ( "math" "sort" "strings" "sync" ) // RecentLatencyByKey tracks a bounded in-memory latency window per logical key. // It is intentionally small and process-local; Prometheus histograms keep the // lifetime view, while this gives capacity-check a recent-window signal. type RecentLatencyByKey struct { mu sync.Mutex size int windows map[string]*recentLatencyWindow } type recentLatencyWindow struct { values []float64 next int count int } func NewRecentLatencyByKey(size int) *RecentLatencyByKey { if size <= 0 { size = 1 } return &RecentLatencyByKey{ size: size, windows: map[string]*recentLatencyWindow{}, } } func (r *RecentLatencyByKey) Observe(key string, value float64) (p99 float64, samples int) { if r == nil { return 0, 0 } key = strings.TrimSpace(key) if key == "" { key = "unknown" } if value < 0 || math.IsNaN(value) { value = 0 } if math.IsInf(value, 1) { value = math.MaxFloat64 } r.mu.Lock() defer r.mu.Unlock() window := r.windows[key] if window == nil { window = &recentLatencyWindow{values: make([]float64, r.size)} r.windows[key] = window } window.values[window.next] = value window.next = (window.next + 1) % len(window.values) if window.count < len(window.values) { window.count++ } return window.quantileLocked(0.99), window.count } func (w *recentLatencyWindow) quantileLocked(q float64) float64 { if w == nil || w.count == 0 { return 0 } snapshot := append([]float64(nil), w.values[:w.count]...) sort.Float64s(snapshot) index := int(math.Ceil(q*float64(len(snapshot)))) - 1 if index < 0 { index = 0 } if index >= len(snapshot) { index = len(snapshot) - 1 } return snapshot[index] }