mirror of https://github.com/libp2p/go-libp2p.git
Browse Source
* autorelay: add metrics metrics added: relay finder status reservation request outcomes current reservations candidate circuit v2 support current candidates relay addresses updated num relay address scheduled work times * autorelay: fix refresh reservations bug * fix max value hack * improve tracking errors in reservation requests * fix config-query in grafana * add candidate loop state panel * fix logging * reset metrics on relayfinder stop * update dashboard * update dashboard0270-changelog
Sukun
2 years ago
committed by
GitHub
9 changed files with 1668 additions and 29 deletions
File diff suppressed because it is too large
@ -0,0 +1,373 @@ |
|||||
|
package autorelay |
||||
|
|
||||
|
import ( |
||||
|
"errors" |
||||
|
|
||||
|
"github.com/libp2p/go-libp2p/p2p/metricshelper" |
||||
|
"github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client" |
||||
|
pbv2 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/pb" |
||||
|
"github.com/prometheus/client_golang/prometheus" |
||||
|
) |
||||
|
|
||||
|
const metricNamespace = "libp2p_autorelay" |
||||
|
|
||||
|
var ( |
||||
|
status = prometheus.NewGauge(prometheus.GaugeOpts{ |
||||
|
Namespace: metricNamespace, |
||||
|
Name: "status", |
||||
|
Help: "relay finder active", |
||||
|
}) |
||||
|
reservationsOpenedTotal = prometheus.NewCounter( |
||||
|
prometheus.CounterOpts{ |
||||
|
Namespace: metricNamespace, |
||||
|
Name: "reservations_opened_total", |
||||
|
Help: "Reservations Opened", |
||||
|
}, |
||||
|
) |
||||
|
reservationsClosedTotal = prometheus.NewCounter( |
||||
|
prometheus.CounterOpts{ |
||||
|
Namespace: metricNamespace, |
||||
|
Name: "reservations_closed_total", |
||||
|
Help: "Reservations Closed", |
||||
|
}, |
||||
|
) |
||||
|
reservationRequestsOutcomeTotal = prometheus.NewCounterVec( |
||||
|
prometheus.CounterOpts{ |
||||
|
Namespace: metricNamespace, |
||||
|
Name: "reservation_requests_outcome_total", |
||||
|
Help: "Reservation Request Outcome", |
||||
|
}, |
||||
|
[]string{"request_type", "outcome"}, |
||||
|
) |
||||
|
|
||||
|
relayAddressesUpdatedTotal = prometheus.NewCounter( |
||||
|
prometheus.CounterOpts{ |
||||
|
Namespace: metricNamespace, |
||||
|
Name: "relay_addresses_updated_total", |
||||
|
Help: "Relay Addresses Updated Count", |
||||
|
}, |
||||
|
) |
||||
|
relayAddressesCount = prometheus.NewGauge( |
||||
|
prometheus.GaugeOpts{ |
||||
|
Namespace: metricNamespace, |
||||
|
Name: "relay_addresses_count", |
||||
|
Help: "Relay Addresses Count", |
||||
|
}, |
||||
|
) |
||||
|
|
||||
|
candidatesCircuitV2SupportTotal = prometheus.NewCounterVec( |
||||
|
prometheus.CounterOpts{ |
||||
|
Namespace: metricNamespace, |
||||
|
Name: "candidates_circuit_v2_support_total", |
||||
|
Help: "Candidiates supporting circuit v2", |
||||
|
}, |
||||
|
[]string{"support"}, |
||||
|
) |
||||
|
candidatesTotal = prometheus.NewCounterVec( |
||||
|
prometheus.CounterOpts{ |
||||
|
Namespace: metricNamespace, |
||||
|
Name: "candidates_total", |
||||
|
Help: "Candidates Total", |
||||
|
}, |
||||
|
[]string{"type"}, |
||||
|
) |
||||
|
candLoopState = prometheus.NewGauge( |
||||
|
prometheus.GaugeOpts{ |
||||
|
Namespace: metricNamespace, |
||||
|
Name: "candidate_loop_state", |
||||
|
Help: "Candidate Loop State", |
||||
|
}, |
||||
|
) |
||||
|
|
||||
|
scheduledWorkTime = prometheus.NewGaugeVec( |
||||
|
prometheus.GaugeOpts{ |
||||
|
Namespace: metricNamespace, |
||||
|
Name: "scheduled_work_time", |
||||
|
Help: "Scheduled Work Times", |
||||
|
}, |
||||
|
[]string{"work_type"}, |
||||
|
) |
||||
|
|
||||
|
desiredReservations = prometheus.NewGauge( |
||||
|
prometheus.GaugeOpts{ |
||||
|
Namespace: metricNamespace, |
||||
|
Name: "desired_reservations", |
||||
|
Help: "Desired Reservations", |
||||
|
}, |
||||
|
) |
||||
|
|
||||
|
collectors = []prometheus.Collector{ |
||||
|
status, |
||||
|
reservationsOpenedTotal, |
||||
|
reservationsClosedTotal, |
||||
|
reservationRequestsOutcomeTotal, |
||||
|
relayAddressesUpdatedTotal, |
||||
|
relayAddressesCount, |
||||
|
candidatesCircuitV2SupportTotal, |
||||
|
candidatesTotal, |
||||
|
candLoopState, |
||||
|
scheduledWorkTime, |
||||
|
desiredReservations, |
||||
|
} |
||||
|
) |
||||
|
|
||||
|
type candidateLoopState int |
||||
|
|
||||
|
const ( |
||||
|
peerSourceRateLimited candidateLoopState = iota |
||||
|
waitingOnPeerChan |
||||
|
waitingForTrigger |
||||
|
stopped |
||||
|
) |
||||
|
|
||||
|
// MetricsTracer is the interface for tracking metrics for autorelay
|
||||
|
type MetricsTracer interface { |
||||
|
RelayFinderStatus(isActive bool) |
||||
|
|
||||
|
ReservationEnded(cnt int) |
||||
|
ReservationOpened(cnt int) |
||||
|
ReservationRequestFinished(isRefresh bool, err error) |
||||
|
|
||||
|
RelayAddressCount(int) |
||||
|
RelayAddressUpdated() |
||||
|
|
||||
|
CandidateChecked(supportsCircuitV2 bool) |
||||
|
CandidateAdded(cnt int) |
||||
|
CandidateRemoved(cnt int) |
||||
|
CandidateLoopState(state candidateLoopState) |
||||
|
|
||||
|
ScheduledWorkUpdated(scheduledWork *scheduledWorkTimes) |
||||
|
|
||||
|
DesiredReservations(int) |
||||
|
} |
||||
|
|
||||
|
type metricsTracer struct{} |
||||
|
|
||||
|
var _ MetricsTracer = &metricsTracer{} |
||||
|
|
||||
|
type metricsTracerSetting struct { |
||||
|
reg prometheus.Registerer |
||||
|
} |
||||
|
|
||||
|
type MetricsTracerOption func(*metricsTracerSetting) |
||||
|
|
||||
|
func WithRegisterer(reg prometheus.Registerer) MetricsTracerOption { |
||||
|
return func(s *metricsTracerSetting) { |
||||
|
if reg != nil { |
||||
|
s.reg = reg |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func NewMetricsTracer(opts ...MetricsTracerOption) MetricsTracer { |
||||
|
setting := &metricsTracerSetting{reg: prometheus.DefaultRegisterer} |
||||
|
for _, opt := range opts { |
||||
|
opt(setting) |
||||
|
} |
||||
|
metricshelper.RegisterCollectors(setting.reg, collectors...) |
||||
|
|
||||
|
// Initialise these counters to 0 otherwise the first reservation requests aren't handled
|
||||
|
// correctly when using promql increse function
|
||||
|
reservationRequestsOutcomeTotal.WithLabelValues("refresh", "success") |
||||
|
reservationRequestsOutcomeTotal.WithLabelValues("new", "success") |
||||
|
candidatesCircuitV2SupportTotal.WithLabelValues("yes") |
||||
|
candidatesCircuitV2SupportTotal.WithLabelValues("no") |
||||
|
return &metricsTracer{} |
||||
|
} |
||||
|
|
||||
|
func (mt *metricsTracer) RelayFinderStatus(isActive bool) { |
||||
|
if isActive { |
||||
|
status.Set(1) |
||||
|
} else { |
||||
|
status.Set(0) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (mt *metricsTracer) ReservationEnded(cnt int) { |
||||
|
reservationsClosedTotal.Add(float64(cnt)) |
||||
|
} |
||||
|
|
||||
|
func (mt *metricsTracer) ReservationOpened(cnt int) { |
||||
|
reservationsOpenedTotal.Add(float64(cnt)) |
||||
|
} |
||||
|
|
||||
|
func (mt *metricsTracer) ReservationRequestFinished(isRefresh bool, err error) { |
||||
|
tags := metricshelper.GetStringSlice() |
||||
|
defer metricshelper.PutStringSlice(tags) |
||||
|
|
||||
|
if isRefresh { |
||||
|
*tags = append(*tags, "refresh") |
||||
|
} else { |
||||
|
*tags = append(*tags, "new") |
||||
|
} |
||||
|
*tags = append(*tags, getReservationRequestStatus(err)) |
||||
|
reservationRequestsOutcomeTotal.WithLabelValues(*tags...).Inc() |
||||
|
|
||||
|
if !isRefresh && err == nil { |
||||
|
reservationsOpenedTotal.Inc() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (mt *metricsTracer) RelayAddressUpdated() { |
||||
|
relayAddressesUpdatedTotal.Inc() |
||||
|
} |
||||
|
|
||||
|
func (mt *metricsTracer) RelayAddressCount(cnt int) { |
||||
|
relayAddressesCount.Set(float64(cnt)) |
||||
|
} |
||||
|
|
||||
|
func (mt *metricsTracer) CandidateChecked(supportsCircuitV2 bool) { |
||||
|
tags := metricshelper.GetStringSlice() |
||||
|
defer metricshelper.PutStringSlice(tags) |
||||
|
if supportsCircuitV2 { |
||||
|
*tags = append(*tags, "yes") |
||||
|
} else { |
||||
|
*tags = append(*tags, "no") |
||||
|
} |
||||
|
candidatesCircuitV2SupportTotal.WithLabelValues(*tags...).Inc() |
||||
|
} |
||||
|
|
||||
|
func (mt *metricsTracer) CandidateAdded(cnt int) { |
||||
|
tags := metricshelper.GetStringSlice() |
||||
|
defer metricshelper.PutStringSlice(tags) |
||||
|
*tags = append(*tags, "added") |
||||
|
candidatesTotal.WithLabelValues(*tags...).Add(float64(cnt)) |
||||
|
} |
||||
|
|
||||
|
func (mt *metricsTracer) CandidateRemoved(cnt int) { |
||||
|
tags := metricshelper.GetStringSlice() |
||||
|
defer metricshelper.PutStringSlice(tags) |
||||
|
*tags = append(*tags, "removed") |
||||
|
candidatesTotal.WithLabelValues(*tags...).Add(float64(cnt)) |
||||
|
} |
||||
|
|
||||
|
func (mt *metricsTracer) CandidateLoopState(state candidateLoopState) { |
||||
|
candLoopState.Set(float64(state)) |
||||
|
} |
||||
|
|
||||
|
func (mt *metricsTracer) ScheduledWorkUpdated(scheduledWork *scheduledWorkTimes) { |
||||
|
tags := metricshelper.GetStringSlice() |
||||
|
defer metricshelper.PutStringSlice(tags) |
||||
|
|
||||
|
*tags = append(*tags, "allowed peer source call") |
||||
|
scheduledWorkTime.WithLabelValues(*tags...).Set(float64(scheduledWork.nextAllowedCallToPeerSource.Unix())) |
||||
|
*tags = (*tags)[:0] |
||||
|
|
||||
|
*tags = append(*tags, "reservation refresh") |
||||
|
scheduledWorkTime.WithLabelValues(*tags...).Set(float64(scheduledWork.nextRefresh.Unix())) |
||||
|
*tags = (*tags)[:0] |
||||
|
|
||||
|
*tags = append(*tags, "clear backoff") |
||||
|
scheduledWorkTime.WithLabelValues(*tags...).Set(float64(scheduledWork.nextBackoff.Unix())) |
||||
|
*tags = (*tags)[:0] |
||||
|
|
||||
|
*tags = append(*tags, "old candidate check") |
||||
|
scheduledWorkTime.WithLabelValues(*tags...).Set(float64(scheduledWork.nextOldCandidateCheck.Unix())) |
||||
|
} |
||||
|
|
||||
|
func (mt *metricsTracer) DesiredReservations(cnt int) { |
||||
|
desiredReservations.Set(float64(cnt)) |
||||
|
} |
||||
|
|
||||
|
func getReservationRequestStatus(err error) string { |
||||
|
if err == nil { |
||||
|
return "success" |
||||
|
} |
||||
|
|
||||
|
status := "err other" |
||||
|
var re client.ReservationError |
||||
|
if errors.As(err, &re) { |
||||
|
switch re.Status { |
||||
|
case pbv2.Status_CONNECTION_FAILED: |
||||
|
return "connection failed" |
||||
|
case pbv2.Status_MALFORMED_MESSAGE: |
||||
|
return "malformed message" |
||||
|
case pbv2.Status_RESERVATION_REFUSED: |
||||
|
return "reservation refused" |
||||
|
case pbv2.Status_PERMISSION_DENIED: |
||||
|
return "permission denied" |
||||
|
case pbv2.Status_RESOURCE_LIMIT_EXCEEDED: |
||||
|
return "resource limit exceeded" |
||||
|
} |
||||
|
} |
||||
|
return status |
||||
|
} |
||||
|
|
||||
|
// wrappedMetricsTracer wraps MetricsTracer and ignores all calls when mt is nil
|
||||
|
type wrappedMetricsTracer struct { |
||||
|
mt MetricsTracer |
||||
|
} |
||||
|
|
||||
|
var _ MetricsTracer = &wrappedMetricsTracer{} |
||||
|
|
||||
|
func (mt *wrappedMetricsTracer) RelayFinderStatus(isActive bool) { |
||||
|
if mt.mt != nil { |
||||
|
mt.mt.RelayFinderStatus(isActive) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (mt *wrappedMetricsTracer) ReservationEnded(cnt int) { |
||||
|
if mt.mt != nil { |
||||
|
mt.mt.ReservationEnded(cnt) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (mt *wrappedMetricsTracer) ReservationOpened(cnt int) { |
||||
|
if mt.mt != nil { |
||||
|
mt.mt.ReservationOpened(cnt) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (mt *wrappedMetricsTracer) ReservationRequestFinished(isRefresh bool, err error) { |
||||
|
if mt.mt != nil { |
||||
|
mt.mt.ReservationRequestFinished(isRefresh, err) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (mt *wrappedMetricsTracer) RelayAddressUpdated() { |
||||
|
if mt.mt != nil { |
||||
|
mt.mt.RelayAddressUpdated() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (mt *wrappedMetricsTracer) RelayAddressCount(cnt int) { |
||||
|
if mt.mt != nil { |
||||
|
mt.mt.RelayAddressCount(cnt) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (mt *wrappedMetricsTracer) CandidateChecked(supportsCircuitV2 bool) { |
||||
|
if mt.mt != nil { |
||||
|
mt.mt.CandidateChecked(supportsCircuitV2) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (mt *wrappedMetricsTracer) CandidateAdded(cnt int) { |
||||
|
if mt.mt != nil { |
||||
|
mt.mt.CandidateAdded(cnt) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (mt *wrappedMetricsTracer) CandidateRemoved(cnt int) { |
||||
|
if mt.mt != nil { |
||||
|
mt.mt.CandidateRemoved(cnt) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (mt *wrappedMetricsTracer) ScheduledWorkUpdated(scheduledWork *scheduledWorkTimes) { |
||||
|
if mt.mt != nil { |
||||
|
mt.mt.ScheduledWorkUpdated(scheduledWork) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (mt *wrappedMetricsTracer) DesiredReservations(cnt int) { |
||||
|
if mt.mt != nil { |
||||
|
mt.mt.DesiredReservations(cnt) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (mt *wrappedMetricsTracer) CandidateLoopState(state candidateLoopState) { |
||||
|
if mt.mt != nil { |
||||
|
mt.mt.CandidateLoopState(state) |
||||
|
} |
||||
|
} |
@ -0,0 +1,59 @@ |
|||||
|
//go:build nocover
|
||||
|
|
||||
|
package autorelay |
||||
|
|
||||
|
import ( |
||||
|
"math/rand" |
||||
|
"testing" |
||||
|
"time" |
||||
|
|
||||
|
"github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client" |
||||
|
pbv2 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/pb" |
||||
|
) |
||||
|
|
||||
|
func getRandScheduledWork() scheduledWorkTimes { |
||||
|
randTime := func() time.Time { |
||||
|
return time.Now().Add(time.Duration(rand.Intn(10)) * time.Second) |
||||
|
} |
||||
|
return scheduledWorkTimes{ |
||||
|
leastFrequentInterval: 0, |
||||
|
nextRefresh: randTime(), |
||||
|
nextBackoff: randTime(), |
||||
|
nextOldCandidateCheck: randTime(), |
||||
|
nextAllowedCallToPeerSource: randTime(), |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func TestMetricsNoAllocNoCover(t *testing.T) { |
||||
|
scheduledWork := []scheduledWorkTimes{} |
||||
|
for i := 0; i < 10; i++ { |
||||
|
scheduledWork = append(scheduledWork, getRandScheduledWork()) |
||||
|
} |
||||
|
errs := []error{ |
||||
|
client.ReservationError{Status: pbv2.Status_MALFORMED_MESSAGE}, |
||||
|
client.ReservationError{Status: pbv2.Status_MALFORMED_MESSAGE}, |
||||
|
nil, |
||||
|
} |
||||
|
tr := NewMetricsTracer() |
||||
|
tests := map[string]func(){ |
||||
|
"RelayFinderStatus": func() { tr.RelayFinderStatus(rand.Intn(2) == 1) }, |
||||
|
"ReservationEnded": func() { tr.ReservationEnded(rand.Intn(10)) }, |
||||
|
"ReservationRequestFinished": func() { tr.ReservationRequestFinished(rand.Intn(2) == 1, errs[rand.Intn(len(errs))]) }, |
||||
|
"RelayAddressCount": func() { tr.RelayAddressCount(rand.Intn(10)) }, |
||||
|
"RelayAddressUpdated": func() { tr.RelayAddressUpdated() }, |
||||
|
"ReservationOpened": func() { tr.ReservationOpened(rand.Intn(10)) }, |
||||
|
"CandidateChecked": func() { tr.CandidateChecked(rand.Intn(2) == 1) }, |
||||
|
"CandidateAdded": func() { tr.CandidateAdded(rand.Intn(10)) }, |
||||
|
"CandidateRemoved": func() { tr.CandidateRemoved(rand.Intn(10)) }, |
||||
|
"ScheduledWorkUpdated": func() { tr.ScheduledWorkUpdated(&scheduledWork[rand.Intn(len(scheduledWork))]) }, |
||||
|
"DesiredReservations": func() { tr.DesiredReservations(rand.Intn(10)) }, |
||||
|
"CandidateLoopState": func() { tr.CandidateLoopState(candidateLoopState(rand.Intn(10))) }, |
||||
|
} |
||||
|
for method, f := range tests { |
||||
|
allocs := testing.AllocsPerRun(1000, f) |
||||
|
|
||||
|
if allocs > 0 { |
||||
|
t.Fatalf("Alloc Test: %s, got: %0.2f, expected: 0 allocs", method, allocs) |
||||
|
} |
||||
|
} |
||||
|
} |
Loading…
Reference in new issue