Repository: libp2p/go-libp2p
Pull request #2119 (2023-02-21 12:26:21)

swarm: add early muxer selection to swarm metrics
fixes: https://github.com/libp2p/go-libp2p/issues/2021

p-shahi: Please update the Changelog.md file in this section with these changes https://github.com/libp2p/go-libp2p/blob/master/CHANGELOG.md#additional-metrics-

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cf70d574..227f165e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,9 @@
 Since the last release, we've added additional metrics to different components.
 Metrics were added to:
 * [AutoNat](https://github.com/libp2p/go-libp2p/pull/2086): Current Reachability Status and Confidence, Client and Server DialResponses, Server DialRejections. The dashboard is [available here](https://github.com/libp2p/go-libp2p/blob/master/dashboards/autonat/autonat.json).
+* Swarm:
+  - [Early Muxer Selection](https://github.com/libp2p/go-libp2p/pull/2119): Added early_muxer label indicating whether a connection was established using early muxer selection. 
+  - [IP Version](https://github.com/libp2p/go-libp2p/pull/2114): Added ip_version label to connection metrics
 
 ## 🐞 Bugfixes <!-- omit in toc -->
 
diff --git a/core/network/conn.go b/core/network/conn.go
index 4191a9d5..56031ab0 100644
--- a/core/network/conn.go
+++ b/core/network/conn.go
@@ -43,6 +43,8 @@ type ConnectionState struct {
 	Security protocol.ID
 	// the transport used on this connection. For example: tcp
 	Transport string
+	// indicates whether StreamMultiplexer was selected using inlined muxer negotiation
+	UsedEarlyMuxerNegotiation bool
 }
 
 // ConnSecurity is the interface that one can mix into a connection interface to
diff --git a/dashboards/swarm/swarm.json b/dashboards/swarm/swarm.json
index 09eee4ce..4ed0860a 100644
--- a/dashboards/swarm/swarm.json
+++ b/dashboards/swarm/swarm.json
@@ -1059,6 +1059,113 @@
       "title": "Connection Duration",
       "type": "timeseries"
     },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            }
+          },
+          "mappings": []
+        },
+        "overrides": [
+          {
+            "matcher": {
+              "id": "byName",
+              "options": "regular"
+            },
+            "properties": [
+              {
+                "id": "color",
+                "value": {
+                  "fixedColor": "yellow",
+                  "mode": "fixed"
+                }
+              }
+            ]
+          },
+          {
+            "matcher": {
+              "id": "byName",
+              "options": "early muxer"
+            },
+            "properties": [
+              {
+                "id": "color",
+                "value": {
+                  "fixedColor": "green",
+                  "mode": "fixed"
+                }
+              }
+            ]
+          }
+        ]
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 12,
+        "y": 25
+      },
+      "id": 36,
+      "options": {
+        "legend": {
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "pieType": "pie",
+        "reduceOptions": {
+          "calcs": [
+            "lastNotNull"
+          ],
+          "fields": "",
+          "values": false
+        },
+        "tooltip": {
+          "mode": "single",
+          "sort": "none"
+        }
+      },
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "sum(libp2p_swarm_connections_opened_total{transport=\"tcp\",early_muxer=\"true\"} - libp2p_swarm_connections_closed_total{transport=\"tcp\",early_muxer=\"true\"})",
+          "legendFormat": "early muxer",
+          "range": true,
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "sum(libp2p_swarm_connections_opened_total{transport=\"tcp\",early_muxer=\"false\"} - libp2p_swarm_connections_closed_total{transport=\"tcp\",early_muxer=\"false\"})",
+          "hide": false,
+          "legendFormat": "regular",
+          "range": true,
+          "refId": "B"
+        }
+      ],
+      "title": "Active Connections: Early Muxer Negotiation",
+      "type": "piechart"
+    },
     {
       "collapsed": false,
       "gridPos": {
diff --git a/p2p/net/swarm/swarm_metrics.go b/p2p/net/swarm/swarm_metrics.go
index c1c83c3f..95e4b78b 100644
--- a/p2p/net/swarm/swarm_metrics.go
+++ b/p2p/net/swarm/swarm_metrics.go
@@ -25,7 +25,7 @@ var (
 			Name:      "connections_opened_total",
 			Help:      "Connections Opened",
 		},
-		[]string{"dir", "transport", "security", "muxer", "ip_version"},
+		[]string{"dir", "transport", "security", "muxer", "early_muxer", "ip_version"},
 	)
 	keyTypes = prometheus.NewCounterVec(
 		prometheus.CounterOpts{
@@ -41,7 +41,7 @@ var (
 			Name:      "connections_closed_total",
 			Help:      "Connections Closed",
 		},
-		[]string{"dir", "transport", "security", "muxer", "ip_version"},
+		[]string{"dir", "transport", "security", "muxer", "early_muxer", "ip_version"},
 	)
 	dialError = prometheus.NewCounterVec(
 		prometheus.CounterOpts{
@@ -58,7 +58,7 @@ var (
 			Help:      "Duration of a Connection",
 			Buckets:   prometheus.ExponentialBuckets(1.0/16, 2, 25), // up to 24 days
 		},
-		[]string{"dir", "transport", "security", "muxer", "ip_version"},
+		[]string{"dir", "transport", "security", "muxer", "early_muxer", "ip_version"},
 	)
 	connHandshakeLatency = prometheus.NewHistogramVec(
 		prometheus.HistogramOpts{
@@ -67,7 +67,7 @@ var (
 			Help:      "Duration of the libp2p Handshake",
 			Buckets:   prometheus.ExponentialBuckets(0.001, 1.3, 35),
 		},
-		[]string{"transport", "security", "muxer", "ip_version"},
+		[]string{"transport", "security", "muxer", "early_muxer", "ip_version"},
 	)
 	collectors = []prometheus.Collector{
 		connsOpened,
@@ -124,6 +124,12 @@ func appendConnectionState(tags []string, cs network.ConnectionState) []string {
 	// For example, QUIC doesn't set security nor muxer.
 	tags = append(tags, string(cs.Security))
 	tags = append(tags, string(cs.StreamMultiplexer))
+
+	earlyMuxer := "false"
+	if cs.UsedEarlyMuxerNegotiation {
+		earlyMuxer = "true"
+	}
+	tags = append(tags, earlyMuxer)
 	return tags
 }
 
diff --git a/p2p/net/upgrader/conn.go b/p2p/net/upgrader/conn.go
index 5db21755..1c23a01a 100644
--- a/p2p/net/upgrader/conn.go
+++ b/p2p/net/upgrader/conn.go
@@ -16,8 +16,9 @@ type transportConn struct {
 	scope     network.ConnManagementScope
 	stat      network.ConnStats
 
-	muxer    protocol.ID
-	security protocol.ID
+	muxer                     protocol.ID
+	security                  protocol.ID
+	usedEarlyMuxerNegotiation bool
 }
 
 var _ transport.CapableConn = &transportConn{}
@@ -56,8 +57,9 @@ func (t *transportConn) Close() error {
 
 func (t *transportConn) ConnState() network.ConnectionState {
 	return network.ConnectionState{
-		StreamMultiplexer: t.muxer,
-		Security:          t.security,
-		Transport:         "tcp",
+		StreamMultiplexer:         t.muxer,
+		Security:                  t.security,
+		Transport:                 "tcp",
+		UsedEarlyMuxerNegotiation: t.usedEarlyMuxerNegotiation,
 	}
 }
diff --git a/p2p/net/upgrader/upgrader.go b/p2p/net/upgrader/upgrader.go
index 658e5df7..d18c16ea 100644
--- a/p2p/net/upgrader/upgrader.go
+++ b/p2p/net/upgrader/upgrader.go
@@ -186,14 +186,15 @@ func (u *upgrader) upgrade(ctx context.Context, t transport.Transport, maconn ma
 	}
 
 	tc := &transportConn{
-		MuxedConn:      smconn,
-		ConnMultiaddrs: maconn,
-		ConnSecurity:   sconn,
-		transport:      t,
-		stat:           stat,
-		scope:          connScope,
-		muxer:          muxer,
-		security:       security,
+		MuxedConn:                 smconn,
+		ConnMultiaddrs:            maconn,
+		ConnSecurity:              sconn,
+		transport:                 t,
+		stat:                      stat,
+		scope:                     connScope,
+		muxer:                     muxer,
+		security:                  security,
+		usedEarlyMuxerNegotiation: sconn.ConnState().UsedEarlyMuxerNegotiation,
 	}
 	return tc, nil
 }
diff --git a/p2p/security/noise/session.go b/p2p/security/noise/session.go
index 803ed7cb..2f6c23c0 100644
--- a/p2p/security/noise/session.go
+++ b/p2p/security/noise/session.go
@@ -138,6 +138,7 @@ func (s *secureSession) Close() error {
 func SessionWithConnState(s *secureSession, muxer protocol.ID) *secureSession {
 	if s != nil {
 		s.connectionState.StreamMultiplexer = muxer
+		s.connectionState.UsedEarlyMuxerNegotiation = muxer != ""
 	}
 	return s
 }
diff --git a/p2p/security/tls/transport.go b/p2p/security/tls/transport.go
index 3ca837e5..b8c1dcfb 100644
--- a/p2p/security/tls/transport.go
+++ b/p2p/security/tls/transport.go
@@ -166,12 +166,15 @@ func (t *Transport) setupConn(tlsConn *tls.Conn, remotePubKey ci.PubKey) (sec.Se
 	}
 
 	return &conn{
-		Conn:            tlsConn,
-		localPeer:       t.localPeer,
-		privKey:         t.privKey,
-		remotePeer:      remotePeerID,
-		remotePubKey:    remotePubKey,
-		connectionState: network.ConnectionState{StreamMultiplexer: protocol.ID(nextProto)},
+		Conn:         tlsConn,
+		localPeer:    t.localPeer,
+		privKey:      t.privKey,
+		remotePeer:   remotePeerID,
+		remotePubKey: remotePubKey,
+		connectionState: network.ConnectionState{
+			StreamMultiplexer:         protocol.ID(nextProto),
+			UsedEarlyMuxerNegotiation: nextProto != "",
+		},
 	}, nil
 }
 


Test patch:
diff --git a/p2p/net/swarm/swarm_metrics_test.go b/p2p/net/swarm/swarm_metrics_test.go
index de159217..6b00da1a 100644
--- a/p2p/net/swarm/swarm_metrics_test.go
+++ b/p2p/net/swarm/swarm_metrics_test.go
@@ -50,8 +50,8 @@ func TestMetricsNoAllocNoCover(t *testing.T) {
 	mt := NewMetricsTracer()
 
 	connections := []network.ConnectionState{
-		{StreamMultiplexer: "yamux", Security: "tls", Transport: "tcp"},
-		{StreamMultiplexer: "yamux", Security: "noise", Transport: "tcp"},
+		{StreamMultiplexer: "yamux", Security: "tls", Transport: "tcp", UsedEarlyMuxerNegotiation: true},
+		{StreamMultiplexer: "yamux", Security: "noise", Transport: "tcp", UsedEarlyMuxerNegotiation: false},
 		{StreamMultiplexer: "", Security: "", Transport: "quic"},
 		{StreamMultiplexer: "mplex", Security: "noise", Transport: "tcp"},
 	}
diff --git a/p2p/security/noise/transport_test.go b/p2p/security/noise/transport_test.go
index 7cdd5629..ada0cbfe 100644
--- a/p2p/security/noise/transport_test.go
+++ b/p2p/security/noise/transport_test.go
@@ -686,7 +686,9 @@ func TestHandshakeWithTransportEarlyData(t *testing.T) {
 		defer respConn.Close()
 
 		require.Equal(t, expectedProto, initConn.connectionState.StreamMultiplexer)
+		require.Equal(t, expectedProto != "", initConn.connectionState.UsedEarlyMuxerNegotiation)
 		require.Equal(t, expectedProto, respConn.connectionState.StreamMultiplexer)
+		require.Equal(t, expectedProto != "", respConn.connectionState.UsedEarlyMuxerNegotiation)
 
 		initData := []byte("Test data for noise transport")
 		_, err := initConn.Write(initData)
diff --git a/p2p/security/tls/transport_test.go b/p2p/security/tls/transport_test.go
index ab7cb5f3..a30c742a 100644
--- a/p2p/security/tls/transport_test.go
+++ b/p2p/security/tls/transport_test.go
@@ -256,6 +256,7 @@ func TestHandshakeWithNextProtoSucceeds(t *testing.T) {
 		require.True(t, clientConn.RemotePublicKey().Equals(serverKey.GetPublic()), "server public key mismatch")
 		require.True(t, serverConn.RemotePublicKey().Equals(clientKey.GetPublic()), "client public key mismatch")
 		require.Equal(t, clientConn.ConnState().StreamMultiplexer, expectedMuxer)
+		require.Equal(t, clientConn.ConnState().UsedEarlyMuxerNegotiation, expectedMuxer != "")
 		// exchange some data
 		_, err = serverConn.Write([]byte("foobar"))
 		require.NoError(t, err)