Add sync duration tracking to client metrics

Introduce `RecordSyncDuration` for measuring sync message processing time. Update all metrics implementations (VictoriaMetrics, no-op) to support the new method. Refactor `ClientMetrics` to use `AgentInfo` for static agent data.
This commit is contained in:
Zoltán Papp
2026-02-11 15:18:45 +01:00
parent ca3e6d93d3
commit bec58b85b1
4 changed files with 47 additions and 14 deletions

View File

@@ -279,7 +279,10 @@ func NewEngine(
connSemaphore: semaphoregroup.NewSemaphoreGroup(connInitLimit),
probeStunTurn: relay.NewStunTurnProbe(relay.DefaultCacheTTL),
jobExecutor: jobexec.NewExecutor(),
clientMetrics: metrics.NewClientMetrics(deploymentType, version.NetbirdVersion(), true),
clientMetrics: metrics.NewClientMetrics(metrics.AgentInfo{
DeploymentType: deploymentType,
Version: version.NetbirdVersion(),
}, true),
}
log.Infof("I am: %s", config.WgPrivateKey.PublicKey().String())
@@ -842,7 +845,9 @@ func (e *Engine) handleAutoUpdateVersion(autoUpdateSettings *mgmProto.AutoUpdate
func (e *Engine) handleSync(update *mgmProto.SyncResponse) error {
started := time.Now()
defer func() {
log.Infof("sync finished in %s", time.Since(started))
duration := time.Since(started)
log.Infof("sync finished in %s", duration)
e.clientMetrics.RecordSyncDuration(e.ctx, duration)
}()
e.syncMsgMux.Lock()
defer e.syncMsgMux.Unlock()

View File

@@ -6,6 +6,12 @@ import (
"time"
)
// AgentInfo holds static information about the agent
type AgentInfo struct {
DeploymentType DeploymentType
Version string
}
// metricsImplementation defines the internal interface for metrics implementations
type metricsImplementation interface {
// RecordConnectionStages records connection stage metrics from timestamps
@@ -16,6 +22,9 @@ type metricsImplementation interface {
timestamps ConnectionStageTimestamps,
)
// RecordSyncDuration records how long it took to process a sync message
RecordSyncDuration(ctx context.Context, duration time.Duration)
// Export exports metrics in Prometheus format
Export(w io.Writer) error
}
@@ -36,12 +45,12 @@ type ConnectionStageTimestamps struct {
// NewClientMetrics creates a new ClientMetrics instance
// If enabled is true, uses an OpenTelemetry implementation
// If enabled is false, uses a no-op implementation
func NewClientMetrics(deploymentType DeploymentType, version string, enabled bool) *ClientMetrics {
func NewClientMetrics(agentInfo AgentInfo, enabled bool) *ClientMetrics {
var impl metricsImplementation
if !enabled {
impl = &noopMetrics{}
} else {
impl = newVictoriaMetrics(deploymentType, version)
impl = newVictoriaMetrics(agentInfo)
}
return &ClientMetrics{impl: impl}
}
@@ -56,6 +65,11 @@ func (c *ClientMetrics) RecordConnectionStages(
c.impl.RecordConnectionStages(ctx, connectionType, isReconnection, timestamps)
}
// RecordSyncDuration records the duration of sync message processing
func (c *ClientMetrics) RecordSyncDuration(ctx context.Context, duration time.Duration) {
c.impl.RecordSyncDuration(ctx, duration)
}
// Export exports metrics to the writer
func (c *ClientMetrics) Export(w io.Writer) error {
return c.impl.Export(w)

View File

@@ -3,6 +3,7 @@ package metrics
import (
"context"
"io"
"time"
)
// noopMetrics is a no-op implementation of metricsImplementation
@@ -17,6 +18,10 @@ func (s *noopMetrics) RecordConnectionStages(
// No-op
}
func (s *noopMetrics) RecordSyncDuration(_ context.Context, _ time.Duration) {
// No-op
}
func (s *noopMetrics) Export(_ io.Writer) error {
return nil
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"io"
"time"
"github.com/VictoriaMetrics/metrics"
log "github.com/sirupsen/logrus"
@@ -11,19 +12,17 @@ import (
// victoriaMetrics is the VictoriaMetrics implementation of ClientMetrics
type victoriaMetrics struct {
// Static attributes applied to all metrics
deploymentType DeploymentType
version string
// Static agent information applied to all metrics
agentInfo AgentInfo
// Metrics set for managing all metrics
set *metrics.Set
}
func newVictoriaMetrics(deploymentType DeploymentType, version string) metricsImplementation {
func newVictoriaMetrics(agentInfo AgentInfo) metricsImplementation {
return &victoriaMetrics{
deploymentType: deploymentType,
version: version,
set: metrics.NewSet(),
agentInfo: agentInfo,
set: metrics.NewSet(),
}
}
@@ -87,7 +86,7 @@ func (m *victoriaMetrics) RecordConnectionStages(
).Update(totalDuration)
log.Tracef("peer connection metrics [%s, %s, %s]: creation→semaphore: %.3fs, semaphore→signaling: %.3fs, signaling→connection: %.3fs, connection→handshake: %.3fs, total: %.3fs",
m.deploymentType.String(), connTypeStr, attemptType,
m.agentInfo.DeploymentType.String(), connTypeStr, attemptType,
creationToSemaphore, semaphoreToSignaling, signalingToConnection, connectionToHandshake,
totalDuration)
}
@@ -96,13 +95,23 @@ func (m *victoriaMetrics) RecordConnectionStages(
func (m *victoriaMetrics) getMetricName(baseName, connectionType, attemptType string) string {
return fmt.Sprintf(`%s{deployment_type=%q,connection_type=%q,attempt_type=%q,version=%q}`,
baseName,
m.deploymentType.String(),
m.agentInfo.DeploymentType.String(),
connectionType,
attemptType,
m.version,
m.agentInfo.Version,
)
}
// RecordSyncDuration records the duration of sync message processing
func (m *victoriaMetrics) RecordSyncDuration(ctx context.Context, duration time.Duration) {
metricName := fmt.Sprintf(`netbird_sync_duration_seconds{deployment_type=%q,version=%q}`,
m.agentInfo.DeploymentType.String(),
m.agentInfo.Version,
)
m.set.GetOrCreateHistogram(metricName).Update(duration.Seconds())
}
// Export writes metrics in Prometheus text format
func (m *victoriaMetrics) Export(w io.Writer) error {
if m.set == nil {