feat: build vehicle data platform and production pipeline
This commit is contained in:
79
go/vehicle-gateway/internal/metrics/recent_latency.go
Normal file
79
go/vehicle-gateway/internal/metrics/recent_latency.go
Normal file
@@ -0,0 +1,79 @@
|
||||
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]
|
||||
}
|
||||
Reference in New Issue
Block a user