From 8a9127f10c3ea4fb969974d6c0298882bb47535b Mon Sep 17 00:00:00 2001 From: Pavel Zbitskiy Date: Tue, 16 Dec 2025 15:36:38 -0500 Subject: [PATCH 1/2] network: fix double counting in numOutgoingPending tryConnectAddrs always populates two entries per pending connection: addr and gossipAddr that leads to 2x of pending conns reported by numOutgoingPending wo that meshThreadInner always reports inflated value --- network/wsNetwork.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/network/wsNetwork.go b/network/wsNetwork.go index 6fb26e03f2..46349249e5 100644 --- a/network/wsNetwork.go +++ b/network/wsNetwork.go @@ -1986,7 +1986,8 @@ func (wn *WebsocketNetwork) tryConnectReleaseAddr(addr, gossipAddr string) { func (wn *WebsocketNetwork) numOutgoingPending() int { wn.tryConnectLock.Lock() defer wn.tryConnectLock.Unlock() - return len(wn.tryConnectAddrs) + // tryConnectAddrs always populates two entries per pending connection: addr and gossipAddr + return len(wn.tryConnectAddrs) / 2 } // GetHTTPClient returns a http.Client with a suitable for the network Transport From e81beded828f6440a4c29224b8d410b7eb74e6b9 Mon Sep 17 00:00:00 2001 From: Pavel Zbitskiy Date: Tue, 16 Dec 2025 17:13:13 -0500 Subject: [PATCH 2/2] add a test --- network/wsNetwork_test.go | 65 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/network/wsNetwork_test.go b/network/wsNetwork_test.go index 28c73b133d..c3aad3edbb 100644 --- a/network/wsNetwork_test.go +++ b/network/wsNetwork_test.go @@ -4896,3 +4896,68 @@ func TestMaybeSendMessagesOfInterestLegacyPeer(t *testing.T) { } }) } + +// TestNumOutgoingPending tests that numOutgoingPending returns the correct count +// of pending connections, accounting for the fact that tryConnectAddrs always +// stores two entries per pending connection (addr and gossipAddr). +func TestNumOutgoingPending(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + netA := makeTestWebsocketNode(t) + netA.Start() + defer netA.Stop() + + assertEvenEntries := func() { + netA.tryConnectLock.Lock() + mapLen := len(netA.tryConnectAddrs) + netA.tryConnectLock.Unlock() + require.Equal(t, 0, mapLen%2, "tryConnectAddrs should always have even number of entries, got %d", mapLen) + } + + require.Equal(t, 0, netA.numOutgoingPending()) + assertEvenEntries() + + gossipAddr1, ok := netA.tryConnectReserveAddr("127.0.0.1:4161") + require.True(t, ok) + require.NotEmpty(t, gossipAddr1) + require.Equal(t, 1, netA.numOutgoingPending()) + assertEvenEntries() + + netA.tryConnectLock.Lock() + require.Equal(t, 2, len(netA.tryConnectAddrs)) + netA.tryConnectLock.Unlock() + + gossipAddr2, ok := netA.tryConnectReserveAddr("127.0.0.1:4162") + require.True(t, ok) + require.NotEmpty(t, gossipAddr2) + require.Equal(t, 2, netA.numOutgoingPending()) + assertEvenEntries() + + netA.tryConnectLock.Lock() + require.Equal(t, 4, len(netA.tryConnectAddrs)) + netA.tryConnectLock.Unlock() + + // Trying to reserve the same address should fail + _, ok = netA.tryConnectReserveAddr("127.0.0.1:4161") + require.False(t, ok) + require.Equal(t, 2, netA.numOutgoingPending(), "count should not change after failed reserve") + assertEvenEntries() + + // Release addresses + netA.tryConnectReleaseAddr("127.0.0.1:4161", gossipAddr1) + require.Equal(t, 1, netA.numOutgoingPending()) + assertEvenEntries() + + netA.tryConnectLock.Lock() + require.Equal(t, 2, len(netA.tryConnectAddrs)) + netA.tryConnectLock.Unlock() + + netA.tryConnectReleaseAddr("127.0.0.1:4162", gossipAddr2) + require.Equal(t, 0, netA.numOutgoingPending()) + assertEvenEntries() + + netA.tryConnectLock.Lock() + require.Equal(t, 0, len(netA.tryConnectAddrs), "map should be empty after all releases") + netA.tryConnectLock.Unlock() +}