metrics

package
v0.0.0-...-c77258e Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 11, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package metrics provides high-performance atomic counters and Prometheus metrics for SRT connection statistics and monitoring.

Index

Constants

View Source
const (
	// LockTimingSamples is the number of recent samples to keep for lock timing
	LockTimingSamples = 10
)

Variables

This section is empty.

Functions

func CountNAKEntries

func CountNAKEntries(m *ConnectionMetrics, list []circular.Number, counterType NAKCounterType) uint64

CountNAKEntries iterates through a NAK loss list and increments the appropriate single/range/total counters. This function is used by BOTH:

  • Receiver when generating NAKs (NAKCounterSend)
  • Sender when receiving NAKs (NAKCounterRecv)

Using the same function for both paths ensures counters are 100% consistent between endpoints, which is critical for NAK delivery rate calculations.

The loss list format is pairs of [start, end] circular.Number values:

  • If start == end: Single packet NAK entry (1 packet)
  • If start != end: Range NAK entry (end.Distance(start) + 1 packets)

RFC SRT Appendix A:

  • Figure 21: Single packet entry (4 bytes on wire)
  • Figure 22: Range entry (8 bytes on wire)

Returns the total number of packets requested by the NAK list.

func GetConnections

func GetConnections() map[uint32]*ConnectionInfo

GetConnections returns a snapshot of all registered connections with their metadata. Returns: map of socketId -> ConnectionInfo (contains metrics + metadata) This is safe to call concurrently (uses RLock for concurrent reads).

func GetProgramStartTime

func GetProgramStartTime() time.Time

GetProgramStartTime returns the time when the program started. Useful for testing and external uptime calculations.

func IncrementRecvDataDrop

func IncrementRecvDataDrop(m *ConnectionMetrics, reason DropReason, pktLen uint64)

IncrementRecvDataDrop increments both granular and aggregate drop counters for receiver This ensures granular and aggregate counters stay in sync Metrics are guaranteed to be non-nil (initialized in connection.go before NewReceiver)

func IncrementRecvErrorDrop

func IncrementRecvErrorDrop(m *ConnectionMetrics, p packet.Packet, reason DropReason, isData bool)

IncrementRecvErrorDrop increments granular error counters for receive path For DATA packets, also increments the aggregate PktRecvDataError counter Note: For receive path, we may not have a packet object when errors occur Metrics are guaranteed to be non-nil (initialized in connection.go before NewReceiver)

func IncrementRecvErrorMetrics

func IncrementRecvErrorMetrics(m *ConnectionMetrics, isIoUring bool, errorType DropReason)

IncrementRecvErrorMetrics increments error metrics for cases where we don't have a packet Exported for use in receive paths

func IncrementRecvMetrics

func IncrementRecvMetrics(m *ConnectionMetrics, p packet.Packet, isIoUring bool, success bool, dropReason DropReason)

IncrementRecvMetrics increments the appropriate receive metrics based on packet type and outcome This is a helper function to reduce code duplication in receive paths Exported for use in receive paths (listen_linux.go, dial_linux.go, etc.) dropReason should be DropReason(0) for success cases

func IncrementSendControlMetric

func IncrementSendControlMetric(m *ConnectionMetrics, controlType packet.CtrlType)

IncrementSendControlMetric increments the appropriate send counter for a control packet type. This is used by the io_uring path where control packets are decommissioned before IncrementSendMetrics can classify them.

func IncrementSendDataDrop

func IncrementSendDataDrop(m *ConnectionMetrics, reason DropReason, pktLen uint64)

IncrementSendDataDrop increments both granular and aggregate drop counters for sender This ensures granular and aggregate counters stay in sync Metrics are guaranteed to be non-nil (initialized in connection.go before NewSender)

func IncrementSendErrorDrop

func IncrementSendErrorDrop(m *ConnectionMetrics, p packet.Packet, reason DropReason, pktLen uint64)

IncrementSendErrorDrop increments granular error counters and aggregate for DATA packets For control packets, only increments granular counter (not included in aggregate) Metrics are guaranteed to be non-nil (initialized in connection.go before NewSender)

func IncrementSendErrorMetrics

func IncrementSendErrorMetrics(m *ConnectionMetrics, isIoUring bool, errorType DropReason)

IncrementSendErrorMetrics increments error metrics for cases where we don't have a packet Exported for use in send paths

func IncrementSendMetrics

func IncrementSendMetrics(m *ConnectionMetrics, p packet.Packet, isIoUring bool, success bool, dropReason DropReason)

IncrementSendMetrics increments the appropriate send metrics based on packet type and outcome This is a helper function to reduce code duplication in send paths Exported for use in send paths (connection_linux.go, connection.go, etc.) dropReason should be DropReason(0) for success cases

func MetricsHandler

func MetricsHandler() http.Handler

MetricsHandler returns an HTTP handler that serves Prometheus-formatted metrics Performance is optimized by using a sync.Pool for strings.Builder and a scratch buffer for number formatting. Remember - when adding metrics, also update handler_test.go Remember to also run "make audit-metrics" to verify all metrics are defined, used, and exported to Prometheus

func RegisterConnection

func RegisterConnection(socketId uint32, info *ConnectionInfo)

RegisterConnection registers a connection's metrics with metadata. The ConnectionInfo contains all information needed for Prometheus labeling.

func StabilizationHandler

func StabilizationHandler() http.Handler

StabilizationHandler returns an HTTP handler for the /stabilize endpoint. The output format is simple key=value lines, one per metric:

data_sent=12345
data_recv=12340
ack_sent=500
ack_recv=498
nak_sent=5
nak_recv=3

This is optimized for fast generation (6 atomic loads) and fast parsing. Reuses metricsBuilderPool from handler.go to minimize memory overhead.

func UnregisterConnection

func UnregisterConnection(socketId uint32, reason CloseReason)

UnregisterConnection removes a connection's metrics and increments the appropriate close counter based on the reason.

func WithLockTiming

func WithLockTiming(metrics *LockTimingMetrics, mutex interface {
	Lock()
	Unlock()
}, fn func())

WithLockTiming executes a function while measuring lock hold and wait times for a regular Mutex

func WithRLockTiming

func WithRLockTiming(metrics *LockTimingMetrics, mutex interface {
	RLock()
	RUnlock()
}, fn func())

WithRLockTiming executes a function while measuring read lock hold and wait times for an RWMutex

func WithWLockTiming

func WithWLockTiming(metrics *LockTimingMetrics, mutex interface {
	Lock()
	Unlock()
}, fn func())

WithWLockTiming executes a function while measuring write lock hold and wait times for an RWMutex

Types

type CloseReason

type CloseReason int

CloseReason indicates why a connection was closed. Used for tracking connection lifecycle in metrics.

const (
	CloseReasonGraceful      CloseReason = iota // Normal shutdown (Close() called)
	CloseReasonPeerIdle                         // Peer idle timeout expired
	CloseReasonContextCancel                    // Parent context canceled
	CloseReasonError                            // Error during operation
)

type ConnectionInfo

type ConnectionInfo struct {
	Metrics      *ConnectionMetrics
	InstanceName string    // Instance name for Prometheus labeling (e.g., "baseline-server")
	RemoteAddr   string    // Remote IP:port (e.g., "10.1.1.2:45678")
	StreamId     string    // Stream ID (e.g., "publish:/test-stream")
	PeerType     string    // "publisher", "subscriber", or "unknown"
	PeerSocketID uint32    // Remote peer's socket ID (for cross-process connection correlation)
	StartTime    time.Time // When connection was established (for age metrics)
}

ConnectionInfo holds metadata for a registered connection. This enables richer Prometheus labels for connection identification.

type ConnectionMetrics

type ConnectionMetrics struct {
	// Single counter for all successful receives (for peer idle timeout)
	PktRecvSuccess atomic.Uint64

	// Edge case tracking (should never increment, but defensive programming)
	PktRecvNil            atomic.Uint64 // Nil packet edge case
	PktRecvControlUnknown atomic.Uint64 // Unknown control packet types
	PktRecvSubTypeUnknown atomic.Uint64 // Unknown USER packet subtypes

	// Packet counters (atomic for performance)
	PktRecvDataSuccess atomic.Uint64
	PktRecvDataDropped atomic.Uint64
	PktRecvDataError   atomic.Uint64
	PktSentDataSuccess atomic.Uint64
	// PktSentDataDropped atomic.Uint64 // Not implemented - send drops tracked via CongestionSendPktDrop
	PktSentDataError atomic.Uint64

	// Control packet counters - ACK
	PktRecvACKSuccess atomic.Uint64
	// PktRecvACKDropped atomic.Uint64 // Not implemented - control packets currently never dropped
	// PktRecvACKError   atomic.Uint64 // Not implemented - control packets currently never error
	PktSentACKSuccess atomic.Uint64

	// Control packet counters - ACK (Light vs Full breakdown) - Phase 5: ACK Optimization
	PktSentACKLiteSuccess atomic.Uint64 // Light ACKs sent (sequence only, no RTT)
	PktSentACKFullSuccess atomic.Uint64 // Full ACKs sent (includes RTT, triggers ACKACK)
	PktRecvACKLiteSuccess atomic.Uint64 // Light ACKs received
	PktRecvACKFullSuccess atomic.Uint64 // Full ACKs received

	// Control packet counters - ACKACK
	PktRecvACKACKSuccess atomic.Uint64
	// PktRecvACKACKDropped atomic.Uint64 // Not implemented - control packets currently never dropped
	// PktRecvACKACKError   atomic.Uint64 // Not implemented - control packets currently never error
	PktSentACKACKSuccess atomic.Uint64

	// Control packet counters - NAK
	PktRecvNAKSuccess atomic.Uint64
	// PktRecvNAKDropped atomic.Uint64 // Not implemented - control packets currently never dropped
	// PktRecvNAKError   atomic.Uint64 // Not implemented - control packets currently never error
	PktSentNAKSuccess atomic.Uint64

	// Control packet counters - KM
	PktRecvKMSuccess atomic.Uint64
	// PktRecvKMDropped atomic.Uint64 // Not implemented - control packets currently never dropped
	// PktRecvKMError   atomic.Uint64 // Not implemented - control packets currently never error
	PktSentKMSuccess atomic.Uint64

	// Control packet counters - Keepalive
	PktRecvKeepaliveSuccess atomic.Uint64
	// PktRecvKeepaliveDropped atomic.Uint64 // Not implemented - control packets currently never dropped
	// PktRecvKeepaliveError   atomic.Uint64 // Not implemented - control packets currently never error
	PktSentKeepaliveSuccess atomic.Uint64

	// Control packet counters - Shutdown
	PktRecvShutdownSuccess atomic.Uint64
	// PktRecvShutdownDropped atomic.Uint64 // Not implemented - control packets currently never dropped
	// PktRecvShutdownError   atomic.Uint64 // Not implemented - control packets currently never error
	PktSentShutdownSuccess atomic.Uint64

	// Control packet counters - Handshake
	PktRecvHandshakeSuccess atomic.Uint64
	// PktRecvHandshakeDropped atomic.Uint64 // Not implemented - control packets currently never dropped
	// PktRecvHandshakeError   atomic.Uint64 // Not implemented - control packets currently never error
	PktSentHandshakeSuccess atomic.Uint64

	// Path-specific counters
	PktRecvIoUring   atomic.Uint64
	PktRecvReadFrom  atomic.Uint64
	PktSentIoUring   atomic.Uint64
	PktSentWriteTo   atomic.Uint64
	PktSentSubmitted atomic.Uint64 // io_uring submissions (for detecting lost completions)

	// Error counters (detailed)
	PktRecvErrorIoUring atomic.Uint64
	PktRecvErrorParse   atomic.Uint64
	PktRecvErrorRoute   atomic.Uint64
	PktRecvErrorEmpty   atomic.Uint64
	PktRecvErrorUnknown atomic.Uint64 // Unknown/unexpected error (unrecognized drop reason)
	PktSentErrorIoUring atomic.Uint64
	PktSentErrorMarshal atomic.Uint64
	PktSentErrorSubmit  atomic.Uint64
	PktSentErrorUnknown atomic.Uint64 // Unknown/unexpected error (unrecognized drop reason)

	// Crypto operation error counters
	CryptoErrorEncrypt     atomic.Uint64 // Encryption/decryption payload errors
	CryptoErrorGenerateSEK atomic.Uint64 // SEK generation errors
	CryptoErrorMarshalKM   atomic.Uint64 // Key material marshaling errors

	// Routing failure counters
	PktRecvUnknownSocketId atomic.Uint64
	PktRecvNilConnection   atomic.Uint64
	PktRecvWrongPeer       atomic.Uint64
	PktRecvBacklogFull     atomic.Uint64

	// Resource exhaustion counters
	PktSentRingFull  atomic.Uint64
	PktRecvQueueFull atomic.Uint64

	// Lock timing (see Lock Timing Metrics section)
	HandlePacketLockTiming *LockTimingMetrics
	ReceiverLockTiming     *LockTimingMetrics
	SenderLockTiming       *LockTimingMetrics

	// Byte counters (for completeness)
	ByteRecvDataSuccess atomic.Uint64
	ByteRecvDataDropped atomic.Uint64
	ByteSentDataSuccess atomic.Uint64

	// Special counters (from connStats migration)
	PktRecvUndecrypt  atomic.Uint64
	ByteRecvUndecrypt atomic.Uint64
	PktRecvInvalid    atomic.Uint64
	PktRetransFromNAK atomic.Uint64
	HeaderSize        atomic.Uint64
	MbpsLinkCapacity  atomic.Uint64 // Stored as uint64 (Mbps * 1000)

	// Congestion control - Receiver statistics
	CongestionRecvPkt              atomic.Uint64
	CongestionRecvByte             atomic.Uint64
	CongestionRecvPktUnique        atomic.Uint64
	CongestionRecvByteUnique       atomic.Uint64
	CongestionRecvPktLoss          atomic.Uint64
	CongestionRecvByteLoss         atomic.Uint64
	CongestionRecvPktRetrans       atomic.Uint64
	CongestionRecvByteRetrans      atomic.Uint64
	CongestionRecvPktBelated       atomic.Uint64
	CongestionRecvByteBelated      atomic.Uint64
	CongestionRecvPktDrop          atomic.Uint64
	CongestionRecvByteDrop         atomic.Uint64
	CongestionRecvPktBuf           atomic.Uint64
	CongestionRecvByteBuf          atomic.Uint64
	CongestionRecvMsBuf            atomic.Uint64
	CongestionRecvBytePayload      atomic.Uint64
	CongestionRecvMbpsBandwidth    atomic.Uint64 // Mbps * 1000
	CongestionRecvMbpsLinkCapacity atomic.Uint64 // Mbps * 1000
	CongestionRecvPktRetransRate   atomic.Uint64 // Retransmission rate: bytesRetrans/bytesRecv * 100 (NOT loss rate)

	// Duplicate packet tracking (defensive check in btree Insert)
	// Primary duplicate detection is in push.go; this catches edge cases
	CongestionRecvPktDuplicate  atomic.Uint64 // Duplicate data packets detected by btree
	CongestionRecvByteDuplicate atomic.Uint64 // Duplicate data bytes

	// NAK generation counters - Receiver sends NAKs to request retransmission
	// RFC SRT Appendix A defines two NAK encoding formats:
	// - Single packet (Figure 21): 4 bytes, bit 0 = 0
	// - Range of packets (Figure 22): 8 bytes, bit 0 of first = 1
	// These counters track PACKETS requested, not entries:
	//   NAKSingle + NAKRange = NAKPktsTotal = expected retransmissions
	CongestionRecvNAKSingle    atomic.Uint64 // Packets requested via single NAK entries (1 per entry)
	CongestionRecvNAKRange     atomic.Uint64 // Packets requested via range NAK entries (sum of range sizes)
	CongestionRecvNAKPktsTotal atomic.Uint64 // Total packets requested = NAKSingle + NAKRange

	// Congestion control - Sender statistics
	CongestionSendPkt                atomic.Uint64
	CongestionSendByte               atomic.Uint64
	CongestionSendPktUnique          atomic.Uint64
	CongestionSendByteUnique         atomic.Uint64
	CongestionSendPktLoss            atomic.Uint64
	CongestionSendByteLoss           atomic.Uint64
	CongestionSendPktRetrans         atomic.Uint64
	CongestionSendByteRetrans        atomic.Uint64
	CongestionSendUsSndDuration      atomic.Uint64
	CongestionSendPktDrop            atomic.Uint64
	CongestionSendByteDrop           atomic.Uint64
	CongestionSendPktBuf             atomic.Uint64
	CongestionSendByteBuf            atomic.Uint64
	CongestionSendMsBuf              atomic.Uint64
	CongestionSendPktFlightSize      atomic.Uint64
	CongestionSendUsPktSndPeriod     atomic.Uint64
	CongestionSendBytePayload        atomic.Uint64
	CongestionSendMbpsInputBandwidth atomic.Uint64 // Mbps * 1000
	CongestionSendMbpsSentBandwidth  atomic.Uint64 // Mbps * 1000
	CongestionSendPktRetransRate     atomic.Uint64 // Retransmission rate: bytesRetrans/bytesSent * 100 (NOT loss rate)

	// Additional error/drop counters for congestion control
	CongestionRecvPktNil               atomic.Uint64 // Nil packets received
	CongestionRecvPktStoreInsertFailed atomic.Uint64 // Packet store insertion failures

	// Defensive counters for "should never happen" conditions (ISSUE-001)
	// These track programming errors or invalid state - should always be 0
	NakBtreeNilWhenEnabled    atomic.Uint64 // nakBtree nil when useNakBtree=true
	CongestionSendNAKNotFound atomic.Uint64 // NAK requests for packets not in lossList
	NakBeforeACKCount         atomic.Uint64 // NAK requests for already-ACK'd sequences (receiver bug indicator)

	// NAK receive counters - Sender receives NAKs and retransmits
	// RFC SRT Appendix A defines two NAK encoding formats:
	// - Single packet (Figure 21): 4 bytes, bit 0 = 0
	// - Range of packets (Figure 22): 8 bytes, bit 0 of first = 1
	// These counters track PACKETS requested, not entries:
	//   NAKSingleRecv + NAKRangeRecv = NAKPktsRecv = expected retransmissions
	CongestionSendNAKSingleRecv   atomic.Uint64 // Packets requested via single NAK entries (1 per entry)
	CongestionSendNAKRangeRecv    atomic.Uint64 // Packets requested via range NAK entries (sum of range sizes)
	CongestionSendNAKPktsRecv     atomic.Uint64 // Total packets requested = NAKSingleRecv + NAKRangeRecv
	CongestionSendNAKHonoredOrder atomic.Uint64 // NAK processing runs that honored receiver priority order

	// Granular drop counters - Congestion control (DATA packets only)
	CongestionRecvDataDropTooOld            atomic.Uint64 // Belated, past play time
	CongestionRecvDataDropAlreadyAcked      atomic.Uint64 // Already acknowledged
	CongestionRecvDataDropDuplicate         atomic.Uint64 // Duplicate (already in store)
	CongestionRecvDataDropStoreInsertFailed atomic.Uint64 // Store insert failed
	CongestionSendDataDropTooOld            atomic.Uint64 // Exceed drop threshold

	// TSBPD skip counters - Packets that NEVER arrived and were skipped when ACK advanced
	// These are distinct from "drops" which track packets that ARRIVED but were discarded
	CongestionRecvPktSkippedTSBPD        atomic.Uint64 // Packets skipped at TSBPD time (never arrived)
	CongestionRecvByteSkippedTSBPD       atomic.Uint64 // Bytes skipped (estimated from avgPayloadSize)
	ContiguousPointTSBPDAdvancements     atomic.Uint64 // Count of times contiguousPoint advanced due to TSBPD expiry
	ContiguousPointTSBPDSkippedPktsTotal atomic.Uint64 // Total packets skipped across all TSBPD advancements

	// Periodic timer tick counters - Track that timer routines are running
	// Used for health monitoring: should grow linearly with test duration
	// Expected: ACK ~100/sec (10ms interval), NAK ~50/sec (20ms interval)
	CongestionRecvPeriodicACKRuns atomic.Uint64 // Times periodicACK() actually ran
	CongestionRecvPeriodicNAKRuns atomic.Uint64 // Times periodicNAK() actually ran

	// NAK btree metrics - Core operations
	NakBtreeInserts        atomic.Uint64 // Sequences added to NAK btree
	NakBtreeDeletes        atomic.Uint64 // Sequences removed (packet arrived)
	NakBtreeExpired        atomic.Uint64 // Sequences removed (sequence-based expiry, fallback)
	NakBtreeExpiredEarly   atomic.Uint64 // Sequences removed RTT before TSBPD (time-based)
	NakBtreeSkippedExpired atomic.Uint64 // Sequences not inserted (already expired at gap detection)
	NakBtreeSize           atomic.Uint64 // Current size (gauge, updated each periodic NAK)

	// NAK TSBPD estimation metrics (nak_btree_expiry_optimization.md)
	NakTsbpdEstBoundary     atomic.Uint64 // TSBPD estimated using boundary packets (linear interpolation)
	NakTsbpdEstEWMA         atomic.Uint64 // TSBPD estimated using inter-packet EWMA (warm fallback)
	NakTsbpdEstColdFallback atomic.Uint64 // TSBPD estimated during EWMA warm-up (conservative)
	NakBtreeScanPackets     atomic.Uint64 // Packets scanned in periodicNakBtree()
	NakBtreeScanGaps        atomic.Uint64 // Gaps found during scan

	// NAK btree metrics - Periodic NAK execution
	NakPeriodicOriginalRuns atomic.Uint64 // Times periodicNakOriginal() executed
	NakPeriodicBtreeRuns    atomic.Uint64 // Times periodicNakBtree() executed
	NakPeriodicSkipped      atomic.Uint64 // Times skipped (interval not elapsed)

	// NAK btree metrics - Consolidation
	NakConsolidationRuns    atomic.Uint64 // Times consolidateNakBtree() ran
	NakConsolidationEntries atomic.Uint64 // Total entries produced by consolidation
	NakConsolidationMerged  atomic.Uint64 // Times adjacent sequences merged into ranges
	NakConsolidationTimeout atomic.Uint64 // Times consolidation hit time budget

	// Suppression metrics (future implementation - RTO-based)
	// These are placeholders for retransmission_and_nak_suppression_design.md
	NakSuppressedSeqs atomic.Uint64 // NAK entries skipped (already NAK'd recently, awaiting RTO)
	NakAllowedSeqs    atomic.Uint64 // NAK entries that passed RTO threshold
	RetransSuppressed atomic.Uint64 // Sender retransmissions skipped (already in flight)
	RetransAllowed    atomic.Uint64 // Sender retransmissions that passed RTO threshold
	RetransFirstTime  atomic.Uint64 // NAK retransmits where TransmitCount was 0 (edge case - packet never first-sent)

	// NAK btree metrics - FastNAK
	NakFastTriggers       atomic.Uint64 // Times FastNAK triggered after silence
	NakFastRecentInserts  atomic.Uint64 // Sequences added by FastNAKRecent jump detection
	NakFastRecentSkipped  atomic.Uint64 // FastNAKRecent skipped (gap too small)
	NakFastRecentOverflow atomic.Uint64 // FastNAKRecent gap too large (capped)

	// NAK packet splitting metrics (FR-11: MSS overflow handling)
	NakPacketsSplit atomic.Uint64 // Extra NAK packets needed due to MSS overflow

	// Granular error counters - Connection-level receive (DATA and Control packets)
	PktRecvDataErrorParse      atomic.Uint64 // DATA packet parse errors
	PktRecvControlErrorParse   atomic.Uint64 // Control packet parse errors
	PktRecvDataErrorIoUring    atomic.Uint64 // DATA packet io_uring errors
	PktRecvControlErrorIoUring atomic.Uint64 // Control packet io_uring errors
	PktRecvDataErrorEmpty      atomic.Uint64 // DATA packet empty datagrams
	PktRecvControlErrorEmpty   atomic.Uint64 // Control packet empty datagrams
	PktRecvDataErrorRoute      atomic.Uint64 // DATA packet routing failures
	PktRecvControlErrorRoute   atomic.Uint64 // Control packet routing failures

	// Granular error counters - Connection-level send (DATA and Control packets)
	PktSentDataErrorMarshal    atomic.Uint64 // DATA packet marshal errors
	PktSentControlErrorMarshal atomic.Uint64 // Control packet marshal errors
	PktSentDataRingFull        atomic.Uint64 // DATA packet ring full
	PktSentControlRingFull     atomic.Uint64 // Control packet ring full
	PktSentDataErrorSubmit     atomic.Uint64 // DATA packet submit errors
	PktSentControlErrorSubmit  atomic.Uint64 // Control packet submit errors
	PktSentDataErrorIoUring    atomic.Uint64 // DATA packet io_uring completion errors
	PktSentControlErrorIoUring atomic.Uint64 // Control packet io_uring completion errors

	RingDropsTotal       atomic.Uint64 // Packets dropped due to ring full (after backoff)
	RingDrainedPackets   atomic.Uint64 // Packets successfully drained from ring to btree
	RingPacketsProcessed atomic.Uint64 // Total packets consumed from ring (for delta calculation)

	SendTickRuns             atomic.Uint64 // Number of Tick() invocations
	SendTickDeliveredPackets atomic.Uint64 // Packets delivered during Tick() calls

	SendRingPushed      atomic.Uint64 // Packets successfully pushed to sender ring
	SendRingDropped     atomic.Uint64 // Packets dropped due to sender ring full
	SendSeqAssigned     atomic.Uint64 // Sequence numbers assigned (atomic 31-bit)
	SendSeqWraparound   atomic.Uint64 // Times sequence wrapped past MAX_SEQUENCENUMBER
	SendFirstTransmit   atomic.Uint64 // Packets sent with TransmitCount 0→1 (first send)
	SendAlreadySent     atomic.Uint64 // Packets skipped in delivery (TransmitCount>=1)
	SendRingDrained     atomic.Uint64 // Packets drained from sender ring to btree
	SendBtreeInserted   atomic.Uint64 // Packets inserted into sender btree
	SendBtreeDuplicates atomic.Uint64 // Duplicate packets detected in sender btree
	SendRingDrainSeqGap atomic.Uint64 // Sequence gaps detected during ring→btree drain (sender bug indicator)

	SendControlRingPushedACK    atomic.Uint64 // ACKs successfully pushed to control ring
	SendControlRingPushedNAK    atomic.Uint64 // NAKs successfully pushed to control ring
	SendControlRingDroppedACK   atomic.Uint64 // ACKs dropped due to control ring full
	SendControlRingDroppedNAK   atomic.Uint64 // NAKs dropped due to control ring full
	SendControlRingDrained      atomic.Uint64 // Control packets drained by EventLoop
	SendControlRingProcessed    atomic.Uint64 // Control packets processed by EventLoop (total)
	SendControlRingProcessedACK atomic.Uint64 // ACKs processed by EventLoop
	SendControlRingProcessedNAK atomic.Uint64 // NAKs processed by EventLoop

	RecvControlRingPushedACKACK       atomic.Uint64 // ACKACKs successfully pushed to control ring
	RecvControlRingPushedKEEPALIVE    atomic.Uint64 // KEEPALIVEs successfully pushed to control ring
	RecvControlRingDroppedACKACK      atomic.Uint64 // ACKACKs dropped due to control ring full (fallback to locked path)
	RecvControlRingDroppedKEEPALIVE   atomic.Uint64 // KEEPALIVEs dropped due to control ring full (fallback to locked path)
	RecvControlRingDrained            atomic.Uint64 // Control packets drained by EventLoop
	RecvControlRingProcessed          atomic.Uint64 // Control packets processed by EventLoop (total)
	RecvControlRingProcessedACKACK    atomic.Uint64 // ACKACKs processed by EventLoop
	RecvControlRingProcessedKEEPALIVE atomic.Uint64 // KEEPALIVEs processed by EventLoop

	// Startup diagnostics (debug intermittent failures)
	SendEventLoopStartAttempts   atomic.Uint64 // Times EventLoop() was called
	SendEventLoopSkippedDisabled atomic.Uint64 // Times EventLoop returned early (useEventLoop=false)
	SendEventLoopStarted         atomic.Uint64 // Times EventLoop entered main loop

	SendEventLoopIterations      atomic.Uint64 // Total EventLoop iterations
	SendEventLoopDefaultRuns     atomic.Uint64 // Default case runs (no timer fired)
	SendEventLoopDropFires       atomic.Uint64 // Drop ticker fires
	SendEventLoopDataDrained     atomic.Uint64 // Data packets drained from ring
	SendEventLoopControlDrained  atomic.Uint64 // Control packets drained from ring
	SendEventLoopACKsProcessed   atomic.Uint64 // ACKs processed by EventLoop
	SendEventLoopNAKsProcessed   atomic.Uint64 // NAKs processed by EventLoop
	SendEventLoopIdleBackoffs    atomic.Uint64 // Times EventLoop entered idle backoff
	SendEventLoopTightCapReached atomic.Uint64 // Times MaxDataPerIteration cap was hit (tight loop mode)
	// Diagnostic metrics for drain debugging
	SendEventLoopDrainAttempts       atomic.Uint64 // Times drain was called
	SendEventLoopDrainRingNil        atomic.Uint64 // Times packetRing was nil
	SendEventLoopDrainRingEmpty      atomic.Uint64 // Times TryPop returned empty (first try)
	SendEventLoopDrainRingHadData    atomic.Uint64 // Times ring.Len() > 0 before drain
	SendEventLoopTsbpdSleeps         atomic.Uint64 // Times EventLoop used TSBPD-aware sleep
	SendEventLoopEmptyBtreeSleeps    atomic.Uint64 // Times EventLoop slept due to empty btree
	SendEventLoopSleepClampedMin     atomic.Uint64 // Times sleep was clamped to minimum
	SendEventLoopSleepClampedMax     atomic.Uint64 // Times sleep was clamped to maximum
	SendEventLoopSleepTotalUs        atomic.Uint64 // Total sleep time in microseconds
	SendEventLoopNextDeliveryTotalUs atomic.Uint64 // Total next delivery time in microseconds
	SendDeliveryPackets              atomic.Uint64 // Packets delivered by EventLoop
	SendBtreeLen                     atomic.Uint64 // Current btree length (updated per iteration)
	// Delivery debugging metrics
	SendDeliveryAttempts      atomic.Uint64 // Times deliverReadyPacketsEventLoop was called
	SendDeliveryBtreeEmpty    atomic.Uint64 // Times btree was empty when trying to deliver
	SendDeliveryIterStarted   atomic.Uint64 // Times IterateFrom called callback (had packets)
	SendDeliveryTsbpdNotReady atomic.Uint64 // Times first packet had tsbpdTime > nowUs
	SendDeliveryLastNowUs     atomic.Uint64 // Last nowUs value (for debugging)
	SendDeliveryLastTsbpd     atomic.Uint64 // Last first packet's tsbpdTime (for debugging)
	SendDeliveryStartSeq      atomic.Uint64 // Last deliveryStartPoint value (for debugging)
	SendDeliveryBtreeMinSeq   atomic.Uint64 // Btree min sequence (for debugging IterateFrom)
	SendDropAheadOfDelivery   atomic.Uint64 // Packets dropped that were ahead of deliveryStartPoint (head-of-line blocking)

	// ========================================================================
	// Zero-Copy Payload Pool Metrics (Phase 5: Lockless Sender)
	// ========================================================================
	// Tracks payload validation and buffer pool usage.
	SendPayloadSizeErrors atomic.Uint64 // Payloads rejected due to size validation

	// Receiver rate counters - accumulate between rate calculations
	RecvRatePeriodUs     atomic.Uint64 // Rate calculation period (microseconds), default 1s
	RecvRateLastUs       atomic.Uint64 // Last rate calculation time (microseconds since epoch)
	RecvRatePackets      atomic.Uint64 // Packets received in current period
	RecvRateBytes        atomic.Uint64 // Bytes received in current period
	RecvRateBytesRetrans atomic.Uint64 // Retransmitted bytes in current period

	// Receiver computed rates - updated atomically during rate calculation
	// Stored as uint64 using math.Float64bits(), read with math.Float64frombits()
	RecvRatePacketsPerSec  atomic.Uint64 // Packets per second (float64 bits)
	RecvRateBytesPerSec    atomic.Uint64 // Bytes per second (float64 bits)
	RecvRatePktRetransRate atomic.Uint64 // Retransmission rate percentage (float64 bits)

	// Sender rate counters - accumulate between rate calculations
	SendRatePeriodUs     atomic.Uint64 // Rate calculation period (microseconds), default 1s
	SendRateLastUs       atomic.Uint64 // Last rate calculation time (microseconds since epoch)
	SendRateBytes        atomic.Uint64 // Bytes input in current period
	SendRateBytesSent    atomic.Uint64 // Bytes actually sent in current period
	SendRateBytesRetrans atomic.Uint64 // Retransmitted bytes in current period

	// Sender computed rates - updated atomically during rate calculation
	// Stored as uint64 using math.Float64bits(), read with math.Float64frombits()
	SendRateEstInputBW     atomic.Uint64 // Estimated input bandwidth bytes/sec (float64 bits)
	SendRateEstSentBW      atomic.Uint64 // Estimated sent bandwidth bytes/sec (float64 bits)
	SendRatePktRetransRate atomic.Uint64 // Retransmission rate percentage (float64 bits)

	// Light ACK threshold counter - replaces nPackets in receiver
	// Used to determine when to send a "light" ACK vs full ACK
	RecvLightACKCounter atomic.Uint64 // Packets since last ACK (for light ACK threshold)

	EventLoopIterations       atomic.Uint64 // Total loop iterations
	EventLoopFullACKFires     atomic.Uint64 // Times fullACKTicker.C case executed
	EventLoopNAKFires         atomic.Uint64 // Times nakTicker.C case executed
	EventLoopRateFires        atomic.Uint64 // Times rateTicker.C case executed
	EventLoopDefaultRuns      atomic.Uint64 // Times default case executed
	EventLoopIdleBackoffs     atomic.Uint64 // Times idle backoff sleep triggered
	EventLoopControlProcessed atomic.Uint64 // Connection control packets processed (ACKACK, KEEPALIVE)

	// Startup diagnostics (debug intermittent failures)
	EventLoopEntered      atomic.Uint64 // Times EventLoop() was called
	EventLoopExitedEarly  atomic.Uint64 // Times EventLoop returned early (useEventLoop=false)
	EventLoopExitedNoRing atomic.Uint64 // Times EventLoop returned early (packetRing=nil)

	AckBtreeSize           atomic.Uint64 // Current size of ack btree (gauge)
	AckBtreeEntriesExpired atomic.Uint64 // Entries expired by ExpireOlderThan
	AckBtreeUnknownACKACK  atomic.Uint64 // ACKACK received for unknown ackNum

	RTTMicroseconds           atomic.Uint64 // Current RTT value in microseconds (EWMA smoothed)
	RTTVarMicroseconds        atomic.Uint64 // Current RTT variance in microseconds (EWMA smoothed)
	RTTLastSampleMicroseconds atomic.Uint64 // Last RTT sample in microseconds (NO smoothing, raw value)

	// Per-ring metrics for send path (connection_linux.go)
	IoUringSendRingMetrics []*IoUringRingMetrics
	IoUringSendRingCount   int // Number of send rings

	// Per-ring metrics for dialer recv path (dial_linux.go)
	IoUringDialerRecvRingMetrics []*IoUringRingMetrics
	IoUringDialerRecvRingCount   int // Number of dialer recv rings
}

ConnectionMetrics holds all metrics for a single connection All counters use atomic.Uint64 for lock-free, high-performance increments

func NewConnectionMetrics

func NewConnectionMetrics() *ConnectionMetrics

NewConnectionMetrics creates a new ConnectionMetrics with LockTiming initialized. All atomic counters are zero-initialized automatically.

func (*ConnectionMetrics) GetRecvRateBytesPerSec

func (m *ConnectionMetrics) GetRecvRateBytesPerSec() float64

GetRecvRateBytesPerSec returns bytes per second as float64

func (*ConnectionMetrics) GetRecvRateMbps

func (m *ConnectionMetrics) GetRecvRateMbps() float64

GetRecvRateMbps returns receive rate in megabits per second

func (*ConnectionMetrics) GetRecvRatePacketsPerSec

func (m *ConnectionMetrics) GetRecvRatePacketsPerSec() float64

GetRecvRatePacketsPerSec returns packets per second as float64

func (*ConnectionMetrics) GetRecvRateRetransPercent

func (m *ConnectionMetrics) GetRecvRateRetransPercent() float64

GetRecvRateRetransPercent returns retransmission percentage

func (*ConnectionMetrics) GetSendRateEstInputBW

func (m *ConnectionMetrics) GetSendRateEstInputBW() float64

GetSendRateEstInputBW returns estimated input bandwidth in bytes/sec

func (*ConnectionMetrics) GetSendRateEstSentBW

func (m *ConnectionMetrics) GetSendRateEstSentBW() float64

GetSendRateEstSentBW returns estimated sent bandwidth in bytes/sec

func (*ConnectionMetrics) GetSendRateMbps

func (m *ConnectionMetrics) GetSendRateMbps() float64

GetSendRateMbps returns sent rate in megabits per second

func (*ConnectionMetrics) GetSendRateRetransPercent

func (m *ConnectionMetrics) GetSendRateRetransPercent() float64

GetSendRateRetransPercent returns sender retransmission percentage

type DropReason

type DropReason uint8

DropReason represents the reason for dropping a packet Using an enum type instead of strings for better performance

const (
	// Receiver drop reasons
	DropReasonTooOld DropReason = iota
	DropReasonAlreadyAcked
	DropReasonDuplicate
	DropReasonStoreInsertFailed

	// Sender drop reasons
	DropReasonTooOldSend // Same as TooOld but for sender path

	// Error drop reasons (both receive and send)
	DropReasonMarshal
	DropReasonRingFull
	DropReasonSubmit
	DropReasonIoUring
	DropReasonParse
	DropReasonRoute
	DropReasonEmpty
	DropReasonWrite         // Write error (generic I/O error)
	DropReasonWrongPeer     // Wrong peer address
	DropReasonUnknownSocket // Unknown socket ID
	DropReasonNilConnection // Nil connection
	DropReasonBacklogFull   // Handshake backlog full
	DropReasonQueueFull     // Receive queue full
)

func (DropReason) String

func (r DropReason) String() string

String returns the string representation of the drop reason (for Prometheus labels)

type IoUringRingMetrics

type IoUringRingMetrics struct {
	// Submit metrics
	SubmitSuccess  atomic.Uint64 // Submit() succeeded
	SubmitRingFull atomic.Uint64 // GetSQE returned nil (ring full)
	SubmitError    atomic.Uint64 // Submit() failed
	GetSQERetries  atomic.Uint64 // GetSQE required retry
	SubmitRetries  atomic.Uint64 // Submit() required retry (EINTR/EAGAIN)

	// Completion metrics
	CompletionSuccess      atomic.Uint64 // WaitCQETimeout returned a completion
	CompletionTimeout      atomic.Uint64 // ETIME: timeout expired (healthy)
	CompletionEBADF        atomic.Uint64 // Ring closed (normal shutdown)
	CompletionEINTR        atomic.Uint64 // Interrupted by signal
	CompletionError        atomic.Uint64 // Other unexpected errors
	CompletionCtxCancelled atomic.Uint64 // Context canceled (shutdown)

	// Packet processing
	PacketsProcessed atomic.Uint64 // Packets successfully processed
	BytesProcessed   atomic.Uint64 // Bytes processed
}

IoUringRingMetrics holds metrics for a single io_uring ring. Each ring has its own instance to avoid cache-line contention across cores. See multi_iouring_design.md Section 5.2 for design rationale.

func NewIoUringRingMetrics

func NewIoUringRingMetrics(ringCount int) []*IoUringRingMetrics

NewIoUringRingMetrics creates metrics for the specified number of rings. Returns nil if ringCount <= 1 (use existing single-ring metrics for backward compatibility). NewIoUringRingMetrics creates metrics for the specified number of rings. ALWAYS creates the array, even for ringCount=1 (unified approach). This simplifies code by removing conditional "use old vs new" logic.

type ListenerMetrics

type ListenerMetrics struct {

	// RecvConnLookupNotFound increments when listen.go reader() receives a packet
	// with a DestinationSocketId that doesn't match any known connection.
	RecvConnLookupNotFound atomic.Uint64

	// RecvConnLookupNotFoundIoUring increments when listen_linux.go io_uring path
	// receives a packet with a socket ID that doesn't match any known connection.
	RecvConnLookupNotFoundIoUring atomic.Uint64

	// HandshakeRejectNotFound increments when Reject() is called on a connection
	// request that doesn't exist in connReqs map. This is a programming error.
	HandshakeRejectNotFound atomic.Uint64

	// HandshakeAcceptNotFound increments when Accept() is called on a connection
	// request that doesn't exist in connReqs map. This is a programming error.
	HandshakeAcceptNotFound atomic.Uint64

	// HandshakeDuplicateRequest increments when a duplicate handshake request
	// is received (same SRTSocketId as an existing pending request).
	HandshakeDuplicateRequest atomic.Uint64

	// SocketIdCollision increments when generateSocketId() finds a randomly
	// generated socket ID is already in use in the conns map.
	SocketIdCollision atomic.Uint64

	// SendConnLookupNotFound increments when listener.send() tries to look up
	// a connection by DestinationSocketId and fails. This indicates Bug 3.
	// Should always be 0 with the closure-based fix in place.
	SendConnLookupNotFound atomic.Uint64

	// ConnectionsActive is a gauge tracking current active connections.
	// Uses Int64 to support Add(-1) for decrements. Lock-free atomic.
	ConnectionsActive atomic.Int64

	// ConnectionsEstablished increments when RegisterConnection() is called.
	// This is the total count of connections established over the listener lifetime.
	ConnectionsEstablished atomic.Uint64

	// ConnectionsClosedTotal is the total count of all closed connections.
	// Should equal ConnectionsEstablished at test end (no leaks).
	ConnectionsClosedTotal atomic.Uint64

	// ConnectionsClosed tracks connections closed by reason.
	// These help identify unexpected connection terminations during tests.
	// Sum of all reasons should equal ConnectionsClosedTotal.
	ConnectionsClosedGraceful      atomic.Uint64 // Normal shutdown (Close() called)
	ConnectionsClosedPeerIdle      atomic.Uint64 // Peer idle timeout expired
	ConnectionsClosedContextCancel atomic.Uint64 // Parent context canceled
	ConnectionsClosedError         atomic.Uint64 // Error during operation

	// Per-ring metrics for listener recv path (listen_linux.go)
	IoUringRecvRingMetrics []*IoUringRingMetrics
	IoUringRecvRingCount   int // Number of listener recv rings (for gauge export)
}

ListenerMetrics holds listener-level metrics (not per-connection). These track events that happen before a connection is established or after a connection is closed, where we cannot associate with a specific connection.

Use cases: - Detect silent failures in map lookups (Bug 3 style issues) - Monitor for attacks or malformed packets targeting unknown connections - Track handshake anomalies

func GetListenerMetrics

func GetListenerMetrics() *ListenerMetrics

GetListenerMetrics returns the global listener metrics instance. This is safe to call concurrently - all fields are atomic.

func NewListenerMetrics

func NewListenerMetrics() *ListenerMetrics

NewListenerMetrics creates a new ListenerMetrics instance with all counters at zero.

func (*ListenerMetrics) InitListenerRecvRingMetrics

func (lm *ListenerMetrics) InitListenerRecvRingMetrics(ringCount int)

InitListenerRecvRingMetrics initializes per-ring metrics for the listener. ALWAYS creates the per-ring array, even for ringCount=1 (unified approach). Call this from io_uring initialization. Safe to call multiple times.

type LockTimingMetrics

type LockTimingMetrics struct {
	// contains filtered or unexported fields
}

LockTimingMetrics tracks lock hold and wait times Uses lock-free ring buffer with atomic values for maximum performance

func (*LockTimingMetrics) GetStats

func (ltm *LockTimingMetrics) GetStats() (holdAvg, holdMax, waitAvg, waitMax float64)

GetStats returns average and max hold/wait times All reads are lock-free (atomic operations only)

Performance note: For 10 values, a simple loop is optimal. The Go compiler may auto-vectorize this, and the overhead of SIMD setup (if we used assembly/cgo) would likely exceed the benefit.

func (*LockTimingMetrics) GetTotalAcquisitions

func (ltm *LockTimingMetrics) GetTotalAcquisitions() uint64

GetTotalAcquisitions returns the total number of lock acquisitions Uses holdTimeIndex as a proxy (saves an atomic operation)

func (*LockTimingMetrics) RecordHoldTime

func (ltm *LockTimingMetrics) RecordHoldTime(duration time.Duration)

RecordHoldTime records a lock hold time measurement

func (*LockTimingMetrics) RecordWaitTime

func (ltm *LockTimingMetrics) RecordWaitTime(duration time.Duration)

RecordWaitTime records a lock wait time measurement

func (*LockTimingMetrics) SnapshotHoldTimes

func (ltm *LockTimingMetrics) SnapshotHoldTimes() [LockTimingSamples]int64

SnapshotHoldTimes returns a snapshot of all hold time samples (for debugging)

func (*LockTimingMetrics) SnapshotWaitTimes

func (ltm *LockTimingMetrics) SnapshotWaitTimes() [LockTimingSamples]int64

SnapshotWaitTimes returns a snapshot of all wait time samples (for debugging)

type MetricsGetter

type MetricsGetter func(ctx context.Context) (StabilizationMetrics, error)

MetricsGetter is a function that returns current stabilization metrics. Used to abstract over different ways of getting metrics (HTTP/UDS fetch). The context allows cancellation of in-flight requests during shutdown.

func NewHTTPGetter

func NewHTTPGetter(url string) MetricsGetter

NewHTTPGetter creates a MetricsGetter that fetches from an HTTP URL. The URL should point to the /stabilize endpoint.

Example:

getter := metrics.NewHTTPGetter("http://localhost:5101/stabilize")

func NewUDSGetter

func NewUDSGetter(socketPath string) MetricsGetter

NewUDSGetter creates a MetricsGetter that fetches from a Unix Domain Socket. The socketPath should be the path to the UDS file.

Example:

getter := metrics.NewUDSGetter("/tmp/server-metrics.sock")

type MetricsRegistry

type MetricsRegistry struct {
	// contains filtered or unexported fields
}

MetricsRegistry holds all connection metrics

type NAKCounterType

type NAKCounterType int

NAKCounterType specifies which counter set to increment.

const (
	// NAKCounterSend is used when the RECEIVER generates and sends NAKs.
	// Increments: CongestionRecvNAKSingle, CongestionRecvNAKRange, CongestionRecvNAKPktsTotal
	NAKCounterSend NAKCounterType = iota

	// NAKCounterRecv is used when the SENDER receives NAKs.
	// Increments: CongestionSendNAKSingleRecv, CongestionSendNAKRangeRecv, CongestionSendNAKPktsRecv
	NAKCounterRecv
)

type ProcStat

type ProcStat struct {
	UserTime   uint64 // Field 14: utime - user mode jiffies
	SystemTime uint64 // Field 15: stime - kernel mode jiffies
}

ProcStat holds parsed CPU time values from /proc/self/stat. Times are in jiffies (typically 1/100th of a second on Linux).

func ReadProcStat

func ReadProcStat() (ProcStat, error)

ReadProcStat reads and parses /proc/self/stat to extract CPU time. Returns user and system time in jiffies (1/100th second on most Linux). Zero allocations in the parsing hot path.

type StabilizationConfig

type StabilizationConfig struct {
	// PollInterval is how often to check metrics (default: 100ms)
	PollInterval time.Duration

	// StableCount is how many consecutive unchanged polls required (default: 2)
	StableCount int

	// MaxWait is the maximum time to wait before giving up (default: 5s)
	MaxWait time.Duration
}

StabilizationConfig configures the stabilization detection behavior.

func DefaultStabilizationConfig

func DefaultStabilizationConfig() StabilizationConfig

DefaultStabilizationConfig returns sensible defaults for stabilization detection.

type StabilizationMetrics

type StabilizationMetrics struct {
	DataSent uint64 // Packets sent (data)
	DataRecv uint64 // Packets received (data)
	AckSent  uint64 // ACKs sent
	AckRecv  uint64 // ACKs received
	NakSent  uint64 // NAKs sent (loss reports)
	NakRecv  uint64 // NAKs received (retransmit requests)
}

StabilizationMetrics holds the 6 key counters we monitor for stabilization. These are the metrics that change during active data transfer and should stop changing once the pipeline has drained.

func AggregateStabilizationMetrics

func AggregateStabilizationMetrics(all ...StabilizationMetrics) StabilizationMetrics

AggregateStabilizationMetrics combines metrics from multiple sources into one. This is useful when you want to check if all components have stabilized together.

func GetStabilizationMetricsFromRegistry

func GetStabilizationMetricsFromRegistry() StabilizationMetrics

GetStabilizationMetricsFromRegistry extracts stabilization-relevant metrics aggregated across all connections in the registry.

func ParseStabilizationResponse

func ParseStabilizationResponse(response string) (StabilizationMetrics, error)

ParseStabilizationResponse parses the response from /stabilize endpoint. Returns error if any required metric is missing or malformed.

func (StabilizationMetrics) Equal

Equal returns true if two StabilizationMetrics are identical. This is used to detect when metrics have stopped changing.

func (StabilizationMetrics) String

func (s StabilizationMetrics) String() string

String returns a human-readable representation of the metrics.

type StabilizationResult

type StabilizationResult struct {
	// Stable indicates whether stabilization was achieved
	Stable bool

	// Elapsed is the time spent waiting
	Elapsed time.Duration

	// Iterations is the number of poll cycles performed
	Iterations int

	// FinalMetrics contains the last metrics snapshot from each getter
	FinalMetrics []StabilizationMetrics

	// Error is set if stabilization failed due to an error (not timeout)
	Error error
}

StabilizationResult contains the result of waiting for stabilization.

func WaitForStabilization

func WaitForStabilization(ctx context.Context, cfg StabilizationConfig, getters ...MetricsGetter) StabilizationResult

WaitForStabilization waits until metrics from all getters stop changing. It polls each getter at the configured interval and checks if all metrics are unchanged for the required number of consecutive polls.

Returns a StabilizationResult indicating whether stabilization was achieved. The context can be used to cancel the wait early.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL
⌂ 换网址