diff --git a/dashboards/relaysvc/relaysvc.json b/dashboards/relaysvc/relaysvc.json index ff82b25acc..ac07eb3db2 100644 --- a/dashboards/relaysvc/relaysvc.json +++ b/dashboards/relaysvc/relaysvc.json @@ -71,6 +71,19 @@ "links": [], "liveNow": false, "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 20, + "panels": [], + "title": "Relay Service", + "type": "row" + }, { "datasource": { "type": "prometheus", @@ -119,7 +132,7 @@ "h": 7, "w": 24, "x": 0, - "y": 0 + "y": 1 }, "id": 2, "options": { @@ -145,14 +158,99 @@ }, "editorMode": "code", "expr": "libp2p_relaysvc_status", - "legendFormat": "service active", + "legendFormat": "active", "range": true, "refId": "A" } ], - "title": "Panel Title", + "title": "Status", "type": "state-timeline" }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 8 + }, + "id": 22, + "panels": [], + "title": "Reservations", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "index": 0, + "text": "0" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 9 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.3.6", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "libp2p_relaysvc_reservations_total{type=\"opened\"} - ignoring(type) libp2p_relaysvc_reservations_total{type=\"closed\"}", + "legendFormat": "active reservations", + "range": true, + "refId": "A" + } + ], + "title": "Active Reservations", + "type": "stat" + }, { "datasource": { "type": "prometheus", @@ -200,60 +298,146 @@ { "color": "green", "value": null + }, + { + "color": "red", + "value": 80 } ] - }, - "unit": "Bps" + } }, "overrides": [ { "matcher": { "id": "byName", - "options": "bandwidth" + "options": "error" }, "properties": [ { "id": "color", "value": { - "fixedColor": "blue", + "fixedColor": "purple", "mode": "fixed" } } ] }, { - "__systemRef": "hideSeriesFrom", "matcher": { - "id": "byNames", - "options": { - "mode": "exclude", - "names": [ - "bandwidth" - ], - "prefix": "All except:", - "readOnly": true - } + "id": "byName", + "options": "ok" }, "properties": [ { - "id": "custom.hideFrom", + "id": "color", "value": { - "legend": false, - "tooltip": false, - "viz": true + "fixedColor": "green", + "mode": "fixed" } } ] } ] }, + "gridPos": { + "h": 8, + "w": 18, + "x": 6, + "y": 9 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "increase(libp2p_relaysvc_reservation_request_response_status_total[$__rate_interval])", + "legendFormat": "{{status}}", + "range": true, + "refId": "A" + } + ], + "title": "Reservation Request Response Status", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, "gridPos": { "h": 8, "w": 12, "x": 0, - "y": 7 + "y": 17 }, - "id": 16, + "id": 26, "options": { "legend": { "calcs": [], @@ -273,13 +457,25 @@ "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", - "expr": "rate(libp2p_relaysvc_bytes_transferred_total[$__rate_interval])", - "legendFormat": "bandwidth", + "expr": "increase(libp2p_relaysvc_reservations_total{type=\"opened\"}[$__rate_interval])", + "legendFormat": "new", "range": true, "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "increase(libp2p_relaysvc_reservations_total{type=\"renewed\"}[$__rate_interval])", + "hide": false, + "legendFormat": "renewed", + "range": true, + "refId": "B" } ], - "title": "Bandwidth Used", + "title": "Reservation Requests: New vs Renewal", "type": "timeseries" }, { @@ -287,29 +483,53 @@ "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, + "description": "", "fieldConfig": { "defaults": { "color": { - "mode": "thresholds" + "mode": "palette-classic" }, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "index": 0, - "text": "0" - } - }, - "type": "special" + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } - ], + }, + "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null + }, + { + "color": "red", + "value": 80 } ] } @@ -318,26 +538,23 @@ }, "gridPos": { "h": 8, - "w": 6, + "w": 12, "x": 12, - "y": 7 + "y": 17 }, - "id": 4, + "id": 12, "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true }, - "textMode": "auto" + "tooltip": { + "mode": "single", + "sort": "none" + } }, - "pluginVersion": "9.3.6", "targets": [ { "datasource": { @@ -345,57 +562,64 @@ "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", - "expr": "libp2p_relaysvc_reservation_total{type=\"opened\"} - ignoring(type) libp2p_relaysvc_reservation_total{type=\"closed\"}", - "legendFormat": "active reservations", + "expr": "increase(libp2p_relaysvc_reservation_rejections_total[$__rate_interval])", + "legendFormat": "{{reason}}", "range": true, "refId": "A" } ], - "title": "Active Reservations", - "type": "stat" + "title": "Reservation Request Rejected", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 25 + }, + "id": 24, + "panels": [], + "title": "Connections", + "type": "row" }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, + "description": "", "fieldConfig": { "defaults": { "color": { - "fixedColor": "blue", - "mode": "fixed" + "mode": "thresholds" }, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "index": 0, - "text": "0" - } - }, - "type": "special" - } - ], + "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null + }, + { + "color": "red", + "value": 80 } ] - } + }, + "unit": "decbytes" }, "overrides": [] }, "gridPos": { "h": 8, "w": 6, - "x": 18, - "y": 7 + "x": 0, + "y": 26 }, - "id": 6, + "id": 28, "options": { "colorMode": "value", "graphMode": "area", @@ -418,13 +642,13 @@ "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", - "expr": "libp2p_relaysvc_connection_total{type=\"opened\"} - ignoring(type) libp2p_relaysvc_connection_total{type=\"closed\"}", - "legendFormat": "active connections", + "expr": "increase(libp2p_relaysvc_data_transferred_bytes_total[$__range])", + "legendFormat": "__auto", "range": true, "refId": "A" } ], - "title": "Active Connections", + "title": "Total Data Transferred", "type": "stat" }, { @@ -435,105 +659,55 @@ "fieldConfig": { "defaults": { "color": { - "mode": "palette-classic" + "fixedColor": "blue", + "mode": "fixed" }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" + "mappings": [ + { + "options": { + "match": "null", + "result": { + "index": 0, + "text": "0" + } + }, + "type": "special" } - }, - "mappings": [], + ], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null - }, - { - "color": "red", - "value": 80 } ] } }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "error" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "purple", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "ok" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "green", - "mode": "fixed" - } - } - ] - } - ] + "overrides": [] }, "gridPos": { "h": 8, - "w": 12, - "x": 0, - "y": 15 + "w": 6, + "x": 6, + "y": 26 }, - "id": 8, + "id": 6, "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false }, - "tooltip": { - "mode": "single", - "sort": "none" - } + "textMode": "auto" }, + "pluginVersion": "9.3.6", "targets": [ { "datasource": { @@ -541,14 +715,14 @@ "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", - "expr": "increase(libp2p_relaysvc_reservation_request_status_total[$__rate_interval])", - "legendFormat": "{{status}}", + "expr": "libp2p_relaysvc_connections_total{type=\"opened\"} - ignoring(type) libp2p_relaysvc_connections_total{type=\"closed\"}", + "legendFormat": "active connections", "range": true, "refId": "A" } ], - "title": "Reservation Request Status", - "type": "timeseries" + "title": "Active Connections", + "type": "stat" }, { "datasource": { @@ -666,7 +840,7 @@ "h": 8, "w": 12, "x": 12, - "y": 15 + "y": 26 }, "id": 10, "options": { @@ -688,13 +862,13 @@ "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", - "expr": "increase(libp2p_relaysvc_connection_request_status_total[$__rate_interval])", + "expr": "increase(libp2p_relaysvc_connection_request_response_status_total[$__rate_interval])", "legendFormat": "{{status}}", "range": true, "refId": "A" } ], - "title": "Connection Request Status", + "title": "Connection Request Response Status", "type": "timeseries" }, { @@ -702,7 +876,6 @@ "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, - "description": "", "fieldConfig": { "defaults": { "color": { @@ -745,23 +918,60 @@ { "color": "green", "value": null - }, + } + ] + }, + "unit": "Bps" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "bandwidth" + }, + "properties": [ { - "color": "red", - "value": 80 + "id": "color", + "value": { + "fixedColor": "blue", + "mode": "fixed" + } + } + ] + }, + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "bandwidth" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true + } } ] } - }, - "overrides": [] + ] }, "gridPos": { "h": 8, "w": 12, "x": 0, - "y": 23 + "y": 34 }, - "id": 12, + "id": 16, "options": { "legend": { "calcs": [], @@ -781,13 +991,13 @@ "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", - "expr": "increase(libp2p_relaysvc_reservation_rejected_total[$__rate_interval])", - "legendFormat": "{{reason}}", + "expr": "2 * rate(libp2p_relaysvc_data_transferred_bytes_total[$__rate_interval])", + "legendFormat": "bandwidth", "range": true, "refId": "A" } ], - "title": "Reservation Request Rejected", + "title": "Bandwidth Used", "type": "timeseries" }, { @@ -851,7 +1061,7 @@ "h": 8, "w": 12, "x": 12, - "y": 23 + "y": 34 }, "id": 14, "options": { @@ -873,7 +1083,7 @@ "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", - "expr": "increase(libp2p_relaysvc_connection_rejected_total[$__rate_interval])", + "expr": "increase(libp2p_relaysvc_connection_rejections_total[$__rate_interval])", "legendFormat": "{{reason}}", "range": true, "refId": "A" @@ -944,7 +1154,7 @@ "h": 8, "w": 12, "x": 12, - "y": 31 + "y": 42 }, "id": 18, "options": { @@ -988,6 +1198,7 @@ "type": "timeseries" } ], + "refresh": "1m", "schemaVersion": 37, "style": "dark", "tags": [], @@ -995,13 +1206,13 @@ "list": [] }, "time": { - "from": "now-15m", + "from": "now-1h", "to": "now" }, "timepicker": {}, "timezone": "", "title": "Relay Service", "uid": "C6RUfAx4z", - "version": 25, + "version": 2, "weekStart": "" } \ No newline at end of file diff --git a/p2p/protocol/circuitv2/relay/metrics.go b/p2p/protocol/circuitv2/relay/metrics.go index 9b9ada7aca..7786459133 100644 --- a/p2p/protocol/circuitv2/relay/metrics.go +++ b/p2p/protocol/circuitv2/relay/metrics.go @@ -4,6 +4,7 @@ import ( "time" "github.com/libp2p/go-libp2p/p2p/metricshelper" + pbv2 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/pb" "github.com/prometheus/client_golang/prometheus" ) @@ -14,55 +15,55 @@ var ( prometheus.GaugeOpts{ Namespace: metricNamespace, Name: "status", - Help: "Relay Current Status", + Help: "Relay Status", }, ) - reservationTotal = prometheus.NewCounterVec( + reservationsTotal = prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: metricNamespace, - Name: "reservation_total", + Name: "reservations_total", Help: "Relay Reservation Request", }, []string{"type"}, ) - reservationRequestStatusTotal = prometheus.NewCounterVec( + reservationRequestResponseStatusTotal = prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: metricNamespace, - Name: "reservation_request_status_total", - Help: "Relay Reservation Request Status", + Name: "reservation_request_response_status_total", + Help: "Relay Reservation Request Response Status", }, []string{"status"}, ) - reservationRejectedTotal = prometheus.NewCounterVec( + reservationRejectionsTotal = prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: metricNamespace, - Name: "reservation_rejected_total", + Name: "reservation_rejections_total", Help: "Relay Reservation Rejected Reason", }, []string{"reason"}, ) - connectionTotal = prometheus.NewCounterVec( + connectionsTotal = prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: metricNamespace, - Name: "connection_total", + Name: "connections_total", Help: "Relay Connection Total", }, []string{"type"}, ) - connectionRequestStatusTotal = prometheus.NewCounterVec( + connectionRequestResponseStatusTotal = prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: metricNamespace, - Name: "connection_request_status_total", + Name: "connection_request_response_status_total", Help: "Relay Connection Request Status", }, []string{"status"}, ) - connectionRejectionTotal = prometheus.NewCounterVec( + connectionRejectionsTotal = prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: metricNamespace, - Name: "connection_rejected_total", + Name: "connection_rejections_total", Help: "Relay Connection Rejected Reason", }, []string{"reason"}, @@ -75,24 +76,24 @@ var ( }, ) - bytesTransferredTotal = prometheus.NewCounter( + dataTransferredBytesTotal = prometheus.NewCounter( prometheus.CounterOpts{ Namespace: metricNamespace, - Name: "bytes_transferred_total", + Name: "data_transferred_bytes_total", Help: "Bytes Transferred Total", }, ) collectors = []prometheus.Collector{ status, - reservationTotal, - reservationRequestStatusTotal, - reservationRejectedTotal, - connectionTotal, - connectionRequestStatusTotal, - connectionRejectionTotal, + reservationsTotal, + reservationRequestResponseStatusTotal, + reservationRejectionsTotal, + connectionsTotal, + connectionRequestResponseStatusTotal, + connectionRejectionsTotal, connectionDurationSeconds, - bytesTransferredTotal, + dataTransferredBytesTotal, } ) @@ -102,48 +103,26 @@ const ( requestStatusError = "error" ) -const ( - typeReceived = "received" - typeOpened = "opened" - typeClosed = "closed" -) - -const ( - rejectionReasonAttemptOverRelay = "attempt over relay" - rejectionReasonDisallowed = "disallowed" - rejectionReasonIPConstraintViolation = "ip constraint violation" - rejectionReasonResourceLimitExceeded = "resource limit exceeded" - rejectionReasonBadRequest = "bad request" - rejectionReasonNoReservation = "no reservation" - rejectionReasonClosed = "closed" -) - // MetricsTracer is the interface for tracking metrics for relay service type MetricsTracer interface { // RelayStatus tracks whether the service is currently active RelayStatus(enabled bool) - // ConnectionRequestReceived tracks a new relay connect request - ConnectionRequestReceived() // ConnectionOpened tracks metrics on opening a relay connection ConnectionOpened() // ConnectionClosed tracks metrics on closing a relay connection ConnectionClosed(d time.Duration) // ConnectionRequestHandled tracks metrics on handling a relay connection request - // rejectionReason is ignored for status other than `requestStatusRejected` - ConnectionRequestHandled(status string, rejectionReason string) + ConnectionRequestHandled(status pbv2.Status) - // ReservationRequestReceived tracks a new relay reservation request - ReservationRequestReceived() - // ReservationOpened tracks metrics on Opening a relay reservation - ReservationOpened() + // ReservationAllowed tracks metrics on opening or renewing a relay reservation + ReservationAllowed(isRenewal bool) // ReservationRequestClosed tracks metrics on closing a relay reservation ReservationClosed(cnt int) // ReservationRequestHandled tracks metrics on handling a relay reservation request - // rejectionReason is ignored for status other than `requestStatusRejected` - ReservationRequestHandled(status string, rejectionReason string) + ReservationRequestHandled(status pbv2.Status) - // BytesTransferred tracks the total bytes transferred(incoming + outgoing) by the relay service + // BytesTransferred tracks the total bytes transferred by the relay service BytesTransferred(cnt int) } @@ -182,81 +161,108 @@ func (mt *metricsTracer) RelayStatus(enabled bool) { } } -func (mt *metricsTracer) ConnectionRequestReceived() { - tags := metricshelper.GetStringSlice() - defer metricshelper.PutStringSlice(tags) - *tags = append(*tags, typeReceived) - - connectionTotal.WithLabelValues(*tags...).Add(1) -} - func (mt *metricsTracer) ConnectionOpened() { tags := metricshelper.GetStringSlice() defer metricshelper.PutStringSlice(tags) - *tags = append(*tags, typeOpened) + *tags = append(*tags, "opened") - connectionTotal.WithLabelValues(*tags...).Add(1) + connectionsTotal.WithLabelValues(*tags...).Add(1) } func (mt *metricsTracer) ConnectionClosed(d time.Duration) { tags := metricshelper.GetStringSlice() defer metricshelper.PutStringSlice(tags) - *tags = append(*tags, typeClosed) + *tags = append(*tags, "closed") - connectionTotal.WithLabelValues(*tags...).Add(1) + connectionsTotal.WithLabelValues(*tags...).Add(1) connectionDurationSeconds.Observe(d.Seconds()) } -func (mt *metricsTracer) ConnectionRequestHandled(status string, rejectionReason string) { +func (mt *metricsTracer) ConnectionRequestHandled(status pbv2.Status) { tags := metricshelper.GetStringSlice() defer metricshelper.PutStringSlice(tags) - *tags = append(*tags, status) - connectionRequestStatusTotal.WithLabelValues(*tags...).Add(1) - if status == requestStatusRejected { + respStatus := getResponseStatus(status) + + *tags = append(*tags, respStatus) + connectionRequestResponseStatusTotal.WithLabelValues(*tags...).Add(1) + if respStatus == requestStatusRejected { *tags = (*tags)[:0] - *tags = append(*tags, rejectionReason) - connectionRejectionTotal.WithLabelValues(*tags...).Add(1) + *tags = append(*tags, getRejectionReason(status)) + connectionRejectionsTotal.WithLabelValues(*tags...).Add(1) } } -func (mt *metricsTracer) ReservationRequestReceived() { - tags := metricshelper.GetStringSlice() - defer metricshelper.PutStringSlice(tags) - *tags = append(*tags, typeReceived) - - reservationTotal.WithLabelValues(*tags...).Add(1) -} - -func (mt *metricsTracer) ReservationOpened() { +func (mt *metricsTracer) ReservationAllowed(isRenewal bool) { tags := metricshelper.GetStringSlice() defer metricshelper.PutStringSlice(tags) - *tags = append(*tags, typeOpened) + if isRenewal { + *tags = append(*tags, "renewed") + } else { + *tags = append(*tags, "opened") + } - reservationTotal.WithLabelValues(*tags...).Add(1) + reservationsTotal.WithLabelValues(*tags...).Add(1) } func (mt *metricsTracer) ReservationClosed(cnt int) { tags := metricshelper.GetStringSlice() defer metricshelper.PutStringSlice(tags) - *tags = append(*tags, typeClosed) + *tags = append(*tags, "closed") - reservationTotal.WithLabelValues(*tags...).Add(float64(cnt)) + reservationsTotal.WithLabelValues(*tags...).Add(float64(cnt)) } -func (mt *metricsTracer) ReservationRequestHandled(status string, rejectionReason string) { +func (mt *metricsTracer) ReservationRequestHandled(status pbv2.Status) { tags := metricshelper.GetStringSlice() defer metricshelper.PutStringSlice(tags) - *tags = append(*tags, status) - reservationRequestStatusTotal.WithLabelValues(*tags...).Add(1) - if status == requestStatusRejected { + respStatus := getResponseStatus(status) + + *tags = append(*tags, respStatus) + reservationRequestResponseStatusTotal.WithLabelValues(*tags...).Add(1) + if respStatus == requestStatusRejected { *tags = (*tags)[:0] - *tags = append(*tags, rejectionReason) - reservationRejectedTotal.WithLabelValues(*tags...).Add(1) + *tags = append(*tags, getRejectionReason(status)) + reservationRejectionsTotal.WithLabelValues(*tags...).Add(1) } } func (mt *metricsTracer) BytesTransferred(cnt int) { - bytesTransferredTotal.Add(float64(cnt)) + dataTransferredBytesTotal.Add(float64(cnt)) +} + +func getResponseStatus(status pbv2.Status) string { + responseStatus := "unknown" + switch status { + case pbv2.Status_RESERVATION_REFUSED, + pbv2.Status_RESOURCE_LIMIT_EXCEEDED, + pbv2.Status_PERMISSION_DENIED, + pbv2.Status_NO_RESERVATION, + pbv2.Status_MALFORMED_MESSAGE: + + responseStatus = requestStatusRejected + case pbv2.Status_UNEXPECTED_MESSAGE, pbv2.Status_CONNECTION_FAILED: + responseStatus = requestStatusError + case pbv2.Status_OK: + responseStatus = requestStatusOK + } + return responseStatus +} + +func getRejectionReason(status pbv2.Status) string { + reason := "unknown" + switch status { + case pbv2.Status_RESERVATION_REFUSED: + reason = "ip constraint violation" + case pbv2.Status_RESOURCE_LIMIT_EXCEEDED: + reason = "resource limit exceeded" + case pbv2.Status_PERMISSION_DENIED: + reason = "permission denied" + case pbv2.Status_NO_RESERVATION: + reason = "no reservation" + case pbv2.Status_MALFORMED_MESSAGE: + reason = "malformed message" + } + return reason } diff --git a/p2p/protocol/circuitv2/relay/metrics_test.go b/p2p/protocol/circuitv2/relay/metrics_test.go index 2c9de6c415..9af23fec75 100644 --- a/p2p/protocol/circuitv2/relay/metrics_test.go +++ b/p2p/protocol/circuitv2/relay/metrics_test.go @@ -6,27 +6,27 @@ import ( "math/rand" "testing" "time" + + pbv2 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/pb" ) func TestNoCoverNoAlloc(t *testing.T) { - statuses := []string{requestStatusOK, requestStatusRejected, requestStatusError} - rejectionReason := []string{"", rejectionReasonAttemptOverRelay, rejectionReasonBadRequest, rejectionReasonDisallowed} + statuses := []pbv2.Status{ + pbv2.Status_OK, + pbv2.Status_NO_RESERVATION, + pbv2.Status_RESOURCE_LIMIT_EXCEEDED, + pbv2.Status_PERMISSION_DENIED, + } mt := NewMetricsTracer() tests := map[string]func(){ "RelayStatus": func() { mt.RelayStatus(rand.Intn(2) == 1) }, - "ConnectionRequestReceived": func() { mt.ConnectionRequestReceived() }, "ConnectionOpened": func() { mt.ConnectionOpened() }, "ConnectionClosed": func() { mt.ConnectionClosed(time.Duration(rand.Intn(10)) * time.Second) }, - "ConnectionRequestHandled": func() { - mt.ConnectionRequestHandled(statuses[rand.Intn(len(statuses))], rejectionReason[rand.Intn(len(rejectionReason))]) - }, - "ReservationRequestReceived": func() { mt.ReservationRequestReceived() }, - "ReservationOpened": func() { mt.ReservationOpened() }, - "ReservationClosed": func() { mt.ReservationClosed(rand.Intn(10)) }, - "ReservationRequestHandled": func() { - mt.ReservationRequestHandled(statuses[rand.Intn(len(statuses))], rejectionReason[rand.Intn(len(rejectionReason))]) - }, - "BytesTransferred": func() { mt.BytesTransferred(rand.Intn(1000)) }, + "ConnectionRequestHandled": func() { mt.ConnectionRequestHandled(statuses[rand.Intn(len(statuses))]) }, + "ReservationAllowed": func() { mt.ReservationAllowed(rand.Intn(2) == 1) }, + "ReservationClosed": func() { mt.ReservationClosed(rand.Intn(10)) }, + "ReservationRequestHandled": func() { mt.ReservationRequestHandled(statuses[rand.Intn(len(statuses))]) }, + "BytesTransferred": func() { mt.BytesTransferred(rand.Intn(1000)) }, } for method, f := range tests { allocs := testing.AllocsPerRun(1000, f) diff --git a/p2p/protocol/circuitv2/relay/relay.go b/p2p/protocol/circuitv2/relay/relay.go index 58fee44b09..01f144007d 100644 --- a/p2p/protocol/circuitv2/relay/relay.go +++ b/p2p/protocol/circuitv2/relay/relay.go @@ -114,9 +114,10 @@ func New(h host.Host, opts ...Option) (*Relay, error) { func (r *Relay) Close() error { r.mx.Lock() - defer r.mx.Unlock() if !r.closed { r.closed = true + r.mx.Unlock() + r.host.RemoveStreamHandler(proto.ProtoIDv2Hop) r.host.Network().StopNotify(r.notifiee) r.scope.Done() @@ -125,7 +126,9 @@ func (r *Relay) Close() error { r.metricsTracer.RelayStatus(false) } r.wg.Wait() + return nil } + r.mx.Unlock() return nil } @@ -159,41 +162,37 @@ func (r *Relay) handleStream(s network.Stream) { } // reset stream deadline as message has been read s.SetReadDeadline(time.Time{}) - switch msg.GetType() { case pbv2.HopMessage_RESERVE: - r.handleReserve(s) - + status := r.handleReserve(s) + if r.metricsTracer != nil { + r.metricsTracer.ReservationRequestHandled(status) + } case pbv2.HopMessage_CONNECT: - r.handleConnect(s, &msg) - + status := r.handleConnect(s, &msg) + if r.metricsTracer != nil { + r.metricsTracer.ConnectionRequestHandled(status) + } default: r.handleError(s, pbv2.Status_MALFORMED_MESSAGE) } } -func (r *Relay) handleReserve(s network.Stream) { +func (r *Relay) handleReserve(s network.Stream) pbv2.Status { defer s.Close() - - if r.metricsTracer != nil { - r.metricsTracer.ReservationRequestReceived() - } - p := s.Conn().RemotePeer() a := s.Conn().RemoteMultiaddr() if isRelayAddr(a) { log.Debugf("refusing relay reservation for %s; reservation attempt over relay connection") - r.handleErrorAndTrackMetrics(s, pbv2.HopMessage_RESERVE, pbv2.Status_PERMISSION_DENIED, - rejectionReasonAttemptOverRelay) - return + r.handleError(s, pbv2.Status_PERMISSION_DENIED) + return pbv2.Status_PERMISSION_DENIED } if r.acl != nil && !r.acl.AllowReserve(p, a) { log.Debugf("refusing relay reservation for %s; permission denied", p) - r.handleErrorAndTrackMetrics(s, pbv2.HopMessage_RESERVE, pbv2.Status_PERMISSION_DENIED, - rejectionReasonDisallowed) - return + r.handleError(s, pbv2.Status_PERMISSION_DENIED) + return pbv2.Status_PERMISSION_DENIED } r.mx.Lock() @@ -203,6 +202,7 @@ func (r *Relay) handleReserve(s network.Stream) { r.mx.Unlock() log.Debugf("refusing relay reservation for %s; relay closed", p) r.handleError(s, pbv2.Status_PERMISSION_DENIED) + return pbv2.Status_PERMISSION_DENIED } now := time.Now() @@ -211,9 +211,8 @@ func (r *Relay) handleReserve(s network.Stream) { if err := r.constraints.AddReservation(p, a); err != nil { r.mx.Unlock() log.Debugf("refusing relay reservation for %s; IP constraint violation: %s", p, err) - r.handleErrorAndTrackMetrics(s, pbv2.HopMessage_RESERVE, pbv2.Status_RESERVATION_REFUSED, - rejectionReasonIPConstraintViolation) - return + r.handleError(s, pbv2.Status_RESERVATION_REFUSED) + return pbv2.Status_RESERVATION_REFUSED } } @@ -221,8 +220,8 @@ func (r *Relay) handleReserve(s network.Stream) { r.rsvp[p] = expire r.host.ConnManager().TagPeer(p, "relay-reservation", ReservationTagWeight) r.mx.Unlock() - if !exists && r.metricsTracer != nil { - r.metricsTracer.ReservationOpened() + if r.metricsTracer != nil { + r.metricsTracer.ReservationAllowed(exists) } log.Debugf("reserving relay slot for %s", p) @@ -233,60 +232,50 @@ func (r *Relay) handleReserve(s network.Stream) { if err := r.writeResponse(s, pbv2.Status_OK, r.makeReservationMsg(p, expire), r.makeLimitMsg(p)); err != nil { log.Debugf("error writing reservation response; retracting reservation for %s", p) s.Reset() - if r.metricsTracer != nil { - r.metricsTracer.ReservationRequestHandled(requestStatusError, "") - } - } - - if r.metricsTracer != nil { - r.metricsTracer.ReservationRequestHandled(requestStatusOK, "") + return pbv2.Status_CONNECTION_FAILED } + return pbv2.Status_OK } -func (r *Relay) handleConnect(s network.Stream, msg *pbv2.HopMessage) { - if r.metricsTracer != nil { - r.metricsTracer.ConnectionRequestReceived() - } - +func (r *Relay) handleConnect(s network.Stream, msg *pbv2.HopMessage) pbv2.Status { src := s.Conn().RemotePeer() a := s.Conn().RemoteMultiaddr() span, err := r.scope.BeginSpan() if err != nil { log.Debugf("failed to begin relay transaction: %s", err) - r.handleErrorAndTrackMetrics(s, pbv2.HopMessage_CONNECT, pbv2.Status_RESOURCE_LIMIT_EXCEEDED, - rejectionReasonResourceLimitExceeded) - return + r.handleError(s, pbv2.Status_RESOURCE_LIMIT_EXCEEDED) + return pbv2.Status_RESOURCE_LIMIT_EXCEEDED } - fail := func(status pbv2.Status, rejectionReason string) { + fail := func(status pbv2.Status) { span.Done() - r.handleErrorAndTrackMetrics(s, pbv2.HopMessage_CONNECT, status, rejectionReason) + r.handleError(s, status) } // reserve buffers for the relay if err := span.ReserveMemory(2*r.rc.BufferSize, network.ReservationPriorityHigh); err != nil { log.Debugf("error reserving memory for relay: %s", err) - fail(pbv2.Status_RESOURCE_LIMIT_EXCEEDED, rejectionReasonResourceLimitExceeded) - return + fail(pbv2.Status_RESOURCE_LIMIT_EXCEEDED) + return pbv2.Status_RESOURCE_LIMIT_EXCEEDED } if isRelayAddr(a) { log.Debugf("refusing connection from %s; connection attempt over relay connection") - fail(pbv2.Status_PERMISSION_DENIED, rejectionReasonAttemptOverRelay) - return + fail(pbv2.Status_PERMISSION_DENIED) + return pbv2.Status_PERMISSION_DENIED } dest, err := util.PeerToPeerInfoV2(msg.GetPeer()) if err != nil { - fail(pbv2.Status_MALFORMED_MESSAGE, rejectionReasonBadRequest) - return + fail(pbv2.Status_MALFORMED_MESSAGE) + return pbv2.Status_MALFORMED_MESSAGE } if r.acl != nil && !r.acl.AllowConnect(src, s.Conn().RemoteMultiaddr(), dest.ID) { log.Debugf("refusing connection from %s to %s; permission denied", src, dest.ID) - fail(pbv2.Status_PERMISSION_DENIED, rejectionReasonDisallowed) - return + fail(pbv2.Status_PERMISSION_DENIED) + return pbv2.Status_PERMISSION_DENIED } r.mx.Lock() @@ -294,24 +283,24 @@ func (r *Relay) handleConnect(s network.Stream, msg *pbv2.HopMessage) { if !rsvp { r.mx.Unlock() log.Debugf("refusing connection from %s to %s; no reservation", src, dest.ID) - fail(pbv2.Status_NO_RESERVATION, rejectionReasonNoReservation) - return + fail(pbv2.Status_NO_RESERVATION) + return pbv2.Status_NO_RESERVATION } srcConns := r.conns[src] if srcConns >= r.rc.MaxCircuits { r.mx.Unlock() log.Debugf("refusing connection from %s to %s; too many connections from %s", src, dest.ID, src) - fail(pbv2.Status_RESOURCE_LIMIT_EXCEEDED, rejectionReasonResourceLimitExceeded) - return + fail(pbv2.Status_RESOURCE_LIMIT_EXCEEDED) + return pbv2.Status_RESOURCE_LIMIT_EXCEEDED } destConns := r.conns[dest.ID] if destConns >= r.rc.MaxCircuits { r.mx.Unlock() log.Debugf("refusing connection from %s to %s; too many connecitons to %s", src, dest.ID, dest.ID) - fail(pbv2.Status_RESOURCE_LIMIT_EXCEEDED, rejectionReasonResourceLimitExceeded) - return + fail(pbv2.Status_RESOURCE_LIMIT_EXCEEDED) + return pbv2.Status_RESOURCE_LIMIT_EXCEEDED } r.addConn(src) @@ -343,27 +332,27 @@ func (r *Relay) handleConnect(s network.Stream, msg *pbv2.HopMessage) { if err != nil { log.Debugf("error opening relay stream to %s: %s", dest.ID, err) cleanup() - r.handleErrorAndTrackMetrics(s, pbv2.HopMessage_CONNECT, pbv2.Status_CONNECTION_FAILED, "") - return + r.handleError(s, pbv2.Status_CONNECTION_FAILED) + return pbv2.Status_CONNECTION_FAILED } - fail = func(status pbv2.Status, rejectionReason string) { + fail = func(status pbv2.Status) { bs.Reset() cleanup() - r.handleErrorAndTrackMetrics(s, pbv2.HopMessage_CONNECT, status, rejectionReason) + r.handleError(s, status) } if err := bs.Scope().SetService(ServiceName); err != nil { log.Debugf("error attaching stream to relay service: %s", err) - fail(pbv2.Status_RESOURCE_LIMIT_EXCEEDED, rejectionReasonResourceLimitExceeded) - return + fail(pbv2.Status_RESOURCE_LIMIT_EXCEEDED) + return pbv2.Status_RESOURCE_LIMIT_EXCEEDED } // handshake if err := bs.Scope().ReserveMemory(maxMessageSize, network.ReservationPriorityAlways); err != nil { log.Debugf("error reserving memory for stream: %s", err) - fail(pbv2.Status_RESOURCE_LIMIT_EXCEEDED, rejectionReasonResourceLimitExceeded) - return + fail(pbv2.Status_RESOURCE_LIMIT_EXCEEDED) + return pbv2.Status_RESOURCE_LIMIT_EXCEEDED } defer bs.Scope().ReleaseMemory(maxMessageSize) @@ -381,8 +370,8 @@ func (r *Relay) handleConnect(s network.Stream, msg *pbv2.HopMessage) { err = wr.WriteMsg(&stopmsg) if err != nil { log.Debugf("error writing stop handshake") - fail(pbv2.Status_CONNECTION_FAILED, "") - return + fail(pbv2.Status_CONNECTION_FAILED) + return pbv2.Status_CONNECTION_FAILED } stopmsg.Reset() @@ -390,20 +379,20 @@ func (r *Relay) handleConnect(s network.Stream, msg *pbv2.HopMessage) { err = rd.ReadMsg(&stopmsg) if err != nil { log.Debugf("error reading stop response: %s", err.Error()) - fail(pbv2.Status_CONNECTION_FAILED, "") - return + fail(pbv2.Status_CONNECTION_FAILED) + return pbv2.Status_CONNECTION_FAILED } if t := stopmsg.GetType(); t != pbv2.StopMessage_STATUS { log.Debugf("unexpected stop response; not a status message (%d)", t) - fail(pbv2.Status_CONNECTION_FAILED, "") - return + fail(pbv2.Status_CONNECTION_FAILED) + return pbv2.Status_CONNECTION_FAILED } if status := stopmsg.GetStatus(); status != pbv2.Status_OK { log.Debugf("relay stop failure: %d", status) - fail(pbv2.Status_CONNECTION_FAILED, "") - return + fail(pbv2.Status_CONNECTION_FAILED) + return pbv2.Status_CONNECTION_FAILED } var response pbv2.HopMessage @@ -418,10 +407,7 @@ func (r *Relay) handleConnect(s network.Stream, msg *pbv2.HopMessage) { bs.Reset() s.Reset() cleanup() - if r.metricsTracer != nil { - r.metricsTracer.ConnectionRequestHandled(requestStatusError, "") - } - return + return pbv2.Status_CONNECTION_FAILED } // reset deadline @@ -451,9 +437,7 @@ func (r *Relay) handleConnect(s network.Stream, msg *pbv2.HopMessage) { go r.relayUnlimited(bs, s, dest.ID, src, done) } - if r.metricsTracer != nil { - r.metricsTracer.ConnectionRequestHandled(requestStatusOK, "") - } + return pbv2.Status_OK } func (r *Relay) addConn(p peer.ID) { @@ -550,7 +534,7 @@ func (r *Relay) copyWithBuffer(dst io.Writer, src io.Reader, buf []byte) (writte break } if r.metricsTracer != nil { - r.metricsTracer.BytesTransferred(nr + nw) + r.metricsTracer.BytesTransferred(nw) } } if er != nil { @@ -574,34 +558,6 @@ func (r *Relay) handleError(s network.Stream, status pbv2.Status) { } } -func (r *Relay) handleErrorAndTrackMetrics(s network.Stream, reqType pbv2.HopMessage_Type, status pbv2.Status, - rejectionReason string) { - r.handleError(s, status) - if r.metricsTracer != nil { - var reqStatus string - switch status { - case pbv2.Status_RESERVATION_REFUSED, - pbv2.Status_RESOURCE_LIMIT_EXCEEDED, - pbv2.Status_PERMISSION_DENIED, - pbv2.Status_NO_RESERVATION, - pbv2.Status_MALFORMED_MESSAGE: - - reqStatus = requestStatusRejected - case pbv2.Status_UNEXPECTED_MESSAGE, pbv2.Status_CONNECTION_FAILED: - reqStatus = requestStatusError - default: - reqStatus = "unknown" - } - - switch reqType { - case pbv2.HopMessage_CONNECT: - r.metricsTracer.ConnectionRequestHandled(reqStatus, rejectionReason) - case pbv2.HopMessage_RESERVE: - r.metricsTracer.ReservationRequestHandled(reqStatus, rejectionReason) - } - } -} - func (r *Relay) writeResponse(s network.Stream, status pbv2.Status, rsvp *pbv2.Reservation, limit *pbv2.Limit) error { wr := util.NewDelimitedWriter(s) @@ -691,11 +647,10 @@ func (r *Relay) gc() { r.mx.Lock() defer r.mx.Unlock() - closed := r.closed.Load() now := time.Now() cnt := 0 for p, expire := range r.rsvp { - if closed || expire.Before(now) { + if r.closed || expire.Before(now) { delete(r.rsvp, p) r.host.ConnManager().UntagPeer(p, "relay-reservation") cnt++