Skip to content

Count TCP server connections in the public stats#219

Merged
gophergogo merged 2 commits intomainfrom
feature/mcp-server-tcp-connection-stats
Apr 20, 2026
Merged

Count TCP server connections in the public stats#219
gophergogo merged 2 commits intomainfrom
feature/mcp-server-tcp-connection-stats

Conversation

@gophergogo
Copy link
Copy Markdown
Collaborator

Summary

  • McpServer's public connections_total/connections_active counters never moved on TCP accepts. ConnectionImpl raises Connected only on the client-side connect-completion path, so the existing increment in the server's onConnectionEvent/onConnectionLifecycleEvent Connected branch was dead code for server sockets — the counters stayed at zero while real traffic landed, then RemoteClose decrements could wrap them into UINT64_MAX.
  • The increment now lives in McpServer::onNewConnection, the server-side equivalent checkpoint (dispatcher thread, fully-assembled connection). The dead Connected branch in onConnectionLifecycleEvent has been simplified to a plain return so it can't silently drift back in.
  • Added integration coverage in test_mcp_server_connection_lifecycle.cc: three concurrent TCP accepts push connections_active up by 3 (baseline-relative), and the server-side drain path brings it back to zero while connections_total stays at baseline+3.

Test plan

  • ctest -R 'McpServerConnectionLifecycleTest|McpClientInitializeRoutingTest|McpInitializeRequestTest|McpServerResponsesTest'
  • ./tests/test_mcp_server_connection_lifecycle — 3 tests pass

gophergogo added 2 commits April 20, 2026 12:59
server_stats_.connections_active and connections_total are supposed
to track accepted connections on the server, but ConnectionImpl only
raises ConnectionEvent::Connected on the client-side connect-completion
path, never for server-accepted sockets. The per-connection lifecycle
adapter therefore never fired the Connected branch of
onConnectionLifecycleEvent, and the counters stayed at 0 on connect.
The matching decrement on RemoteClose/LocalClose still ran, so after
the first client disconnect connections_active wrapped the uint64 to
UINT64_MAX.

Bump the counters directly in onNewConnection, which is the
server-side checkpoint where the socket is accepted, the filter chain
is built, and the call runs on the dispatcher thread. The symmetric
decrement in onConnectionLifecycleEvent also runs on that thread, so
the stats stay consistent without extra synchronization.

Remove the dead Connected/ConnectedZeroRtt increment branch from
onConnectionLifecycleEvent to keep one canonical increment site --
otherwise a future change that did raise Connected for server
connections would quietly double-count.

The stdio path is unaffected: McpConnectionManager still raises
Connected through ServerProtocolCallbacks::onConnectionEvent, which
routes to the separate McpServer::onConnectionEvent handler and keeps
its own increment.
Pin the accept-side stat counters with an integration test that
opens three raw TCP clients, asserts connections_active grew by 3
(baseline-relative, since the SetUp readiness probe can also land
as an accepted connection), then drains them via server shutdown
and asserts connections_active returns to zero while
connections_total stays at baseline+3.

Shutdown-driven drain rather than client-initiated close is
deliberate: a raw TCP client that never sent any application
bytes does not reliably drive the server-side HTTP filter chain
to observe the peer FIN, and coupling this stat test to that
behavior would make it flaky. The drain path is dispatcher-
driven and deterministic.

Also adds a small waitForConnectionStatsIdle helper so SetUp can
let any in-flight probe accepts settle before a test snapshots
the baseline.
@gophergogo gophergogo force-pushed the feature/mcp-server-tcp-connection-stats branch from 1044e06 to 8d5ade3 Compare April 20, 2026 19:59
@gophergogo gophergogo merged commit 7ab643f into main Apr 20, 2026
1 check passed
gophergogo pushed a commit that referenced this pull request Apr 20, 2026
server_stats_.connections_active and connections_total are supposed
to track accepted connections on the server, but ConnectionImpl only
raises ConnectionEvent::Connected on the client-side connect-completion
path, never for server-accepted sockets. The per-connection lifecycle
adapter therefore never fired the Connected branch of
onConnectionLifecycleEvent, and the counters stayed at 0 on connect.
The matching decrement on RemoteClose/LocalClose still ran, so after
the first client disconnect connections_active wrapped the uint64 to
UINT64_MAX.

Bump the counters directly in onNewConnection, which is the
server-side checkpoint where the socket is accepted, the filter chain
is built, and the call runs on the dispatcher thread. The symmetric
decrement in onConnectionLifecycleEvent also runs on that thread, so
the stats stay consistent without extra synchronization.

Remove the dead Connected/ConnectedZeroRtt increment branch from
onConnectionLifecycleEvent to keep one canonical increment site --
otherwise a future change that did raise Connected for server
connections would quietly double-count.

The stdio path is unaffected: McpConnectionManager still raises
Connected through ServerProtocolCallbacks::onConnectionEvent, which
routes to the separate McpServer::onConnectionEvent handler and keeps
its own increment.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants