Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
146 lines
3.4 KiB
Go
146 lines
3.4 KiB
Go
package securityaudit
|
|
|
|
import (
|
|
"sort"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
const latencySampleCapacity = 2048
|
|
|
|
type AtomicMetrics struct {
|
|
total atomic.Int64
|
|
allowed atomic.Int64
|
|
flagged atomic.Int64
|
|
blocked atomic.Int64
|
|
unavailable atomic.Int64
|
|
invalid atomic.Int64
|
|
timeouts atomic.Int64
|
|
failovers atomic.Int64
|
|
bulkheadFull atomic.Int64
|
|
recordFailed atomic.Int64
|
|
latencyTotal atomic.Int64
|
|
latencyMax atomic.Int64
|
|
enqueued atomic.Int64
|
|
dropped atomic.Int64
|
|
latencyMu sync.RWMutex
|
|
latencies []int64
|
|
latencyNext int
|
|
}
|
|
|
|
func NewAtomicMetrics() *AtomicMetrics { return &AtomicMetrics{} }
|
|
|
|
func (m *AtomicMetrics) Snapshot() GuardMetricsSnapshot {
|
|
if m == nil {
|
|
return GuardMetricsSnapshot{}
|
|
}
|
|
snapshot := GuardMetricsSnapshot{
|
|
Total: m.total.Load(), Allowed: m.allowed.Load(), Flagged: m.flagged.Load(),
|
|
Blocked: m.blocked.Load(), Unavailable: m.unavailable.Load(), Invalid: m.invalid.Load(),
|
|
Timeouts: m.timeouts.Load(), Failovers: m.failovers.Load(), BulkheadFull: m.bulkheadFull.Load(),
|
|
RecordFailed: m.recordFailed.Load(), LatencyCount: m.total.Load(), LatencyMaxMS: m.latencyMax.Load(),
|
|
}
|
|
if snapshot.LatencyCount > 0 {
|
|
snapshot.LatencyAvgMS = m.latencyTotal.Load() / snapshot.LatencyCount
|
|
}
|
|
m.latencyMu.RLock()
|
|
samples := append([]int64(nil), m.latencies...)
|
|
m.latencyMu.RUnlock()
|
|
if len(samples) > 0 {
|
|
sort.Slice(samples, func(i, j int) bool { return samples[i] < samples[j] })
|
|
snapshot.LatencyP50MS = percentile(samples, 0.50)
|
|
snapshot.LatencyP95MS = percentile(samples, 0.95)
|
|
snapshot.LatencyP99MS = percentile(samples, 0.99)
|
|
}
|
|
return snapshot
|
|
}
|
|
|
|
func (m *AtomicMetrics) AuditSnapshot() AuditMetricsSnapshot {
|
|
if m == nil {
|
|
return AuditMetricsSnapshot{}
|
|
}
|
|
return AuditMetricsSnapshot{Enqueued: m.enqueued.Load(), Dropped: m.dropped.Load()}
|
|
}
|
|
|
|
func (m *AtomicMetrics) Observe(kind DecisionKind, latency time.Duration) {
|
|
if m == nil {
|
|
return
|
|
}
|
|
m.total.Add(1)
|
|
latencyMS := latency.Milliseconds()
|
|
if latencyMS < 0 {
|
|
latencyMS = 0
|
|
}
|
|
m.latencyTotal.Add(latencyMS)
|
|
for current := m.latencyMax.Load(); latencyMS > current && !m.latencyMax.CompareAndSwap(current, latencyMS); current = m.latencyMax.Load() {
|
|
}
|
|
m.latencyMu.Lock()
|
|
if len(m.latencies) < latencySampleCapacity {
|
|
m.latencies = append(m.latencies, latencyMS)
|
|
} else {
|
|
m.latencies[m.latencyNext] = latencyMS
|
|
m.latencyNext = (m.latencyNext + 1) % latencySampleCapacity
|
|
}
|
|
m.latencyMu.Unlock()
|
|
switch kind {
|
|
case DecisionFlag:
|
|
m.flagged.Add(1)
|
|
case DecisionBlock:
|
|
m.blocked.Add(1)
|
|
case DecisionUnavailable:
|
|
m.unavailable.Add(1)
|
|
case DecisionInvalid:
|
|
m.invalid.Add(1)
|
|
default:
|
|
m.allowed.Add(1)
|
|
}
|
|
}
|
|
|
|
func percentile(sorted []int64, quantile float64) int64 {
|
|
if len(sorted) == 0 {
|
|
return 0
|
|
}
|
|
index := int(float64(len(sorted)-1) * quantile)
|
|
if index < 0 {
|
|
index = 0
|
|
}
|
|
if index >= len(sorted) {
|
|
index = len(sorted) - 1
|
|
}
|
|
return sorted[index]
|
|
}
|
|
|
|
func (m *AtomicMetrics) IncEnqueued() {
|
|
if m != nil {
|
|
m.enqueued.Add(1)
|
|
}
|
|
}
|
|
|
|
func (m *AtomicMetrics) IncDropped() {
|
|
if m != nil {
|
|
m.dropped.Add(1)
|
|
}
|
|
}
|
|
|
|
func (m *AtomicMetrics) IncTimeout() {
|
|
if m != nil {
|
|
m.timeouts.Add(1)
|
|
}
|
|
}
|
|
func (m *AtomicMetrics) IncFailover() {
|
|
if m != nil {
|
|
m.failovers.Add(1)
|
|
}
|
|
}
|
|
func (m *AtomicMetrics) IncBulkheadFull() {
|
|
if m != nil {
|
|
m.bulkheadFull.Add(1)
|
|
}
|
|
}
|
|
func (m *AtomicMetrics) IncRecordFailed() {
|
|
if m != nil {
|
|
m.recordFailed.Add(1)
|
|
}
|
|
}
|