| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- 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)
- }
- }
|