From e919dc38f1a4bbc838ce3b3e3028d0fde0dde7a2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 9 Jun 2026 13:44:53 +0000 Subject: [PATCH 1/2] refactor(launcher): extract getOrLaunchWithConfig to eliminate double config lookup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GetOrLaunchForSession previously called getServerConfig to fetch the server config, then delegated to GetOrLaunch for HTTP backends, which called getServerConfig a second time — acquiring the read lock twice for every HTTP request routed through GetOrLaunchForSession. Extract the inner logic of GetOrLaunch into a private getOrLaunchWithConfig helper that accepts a pre-fetched *config.ServerConfig. Both GetOrLaunch and GetOrLaunchForSession now call this helper after their single config lookup, ensuring the RWMutex read lock is acquired only once per request. No public API changes; existing callers (server/tool_registry.go, server/unified.go) are unaffected. Closes #7254 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- internal/launcher/launcher.go | 39 +++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/internal/launcher/launcher.go b/internal/launcher/launcher.go index 8cd326c49..284953cc8 100644 --- a/internal/launcher/launcher.go +++ b/internal/launcher/launcher.go @@ -117,19 +117,11 @@ func (l *Launcher) getServerConfig(serverID string) (*config.ServerConfig, error return cfg, nil } -// GetOrLaunch returns an existing connection or launches a new one -func GetOrLaunch(l *Launcher, serverID string) (*mcp.Connection, error) { - logger.LogDebugToServer(serverID, "backend", "GetOrLaunch called for server: %s", serverID) - - // Look up config before entering GetOrCreate. GetOrCreate takes a read lock - // first, upgrading to a write lock only on a cache miss; doing this - // read-only check up front avoids holding any lock while validating the - // server ID. - serverCfg, err := l.getServerConfig(serverID) - if err != nil { - return nil, err - } - +// getOrLaunchWithConfig is the internal implementation shared by GetOrLaunch and +// GetOrLaunchForSession. It accepts a pre-fetched serverCfg so callers that +// already hold the config (e.g. GetOrLaunchForSession for HTTP backends) do not +// need to acquire the config read-lock a second time. +func getOrLaunchWithConfig(l *Launcher, serverID string, serverCfg *config.ServerConfig) (*mcp.Connection, error) { return syncutil.GetOrCreate(&l.mu, l.connections, serverID, func() (*mcp.Connection, error) { logLauncher.Printf("Connection not found in cache, launching new: serverID=%s", serverID) logLauncher.Printf("Retrieved server config: serverID=%s, type=%s", serverID, serverCfg.Type) @@ -181,6 +173,22 @@ func GetOrLaunch(l *Launcher, serverID string) (*mcp.Connection, error) { }) } +// GetOrLaunch returns an existing connection or launches a new one +func GetOrLaunch(l *Launcher, serverID string) (*mcp.Connection, error) { + logger.LogDebugToServer(serverID, "backend", "GetOrLaunch called for server: %s", serverID) + + // Look up config before entering GetOrCreate. GetOrCreate takes a read lock + // first, upgrading to a write lock only on a cache miss; doing this + // read-only check up front avoids holding any lock while validating the + // server ID. + serverCfg, err := l.getServerConfig(serverID) + if err != nil { + return nil, err + } + + return getOrLaunchWithConfig(l, serverID, serverCfg) +} + // GetOrLaunchForSession returns a session-aware connection or launches a new one // This is used for stateful stdio backends that require persistent connections func GetOrLaunchForSession(l *Launcher, serverID, sessionID string) (*mcp.Connection, error) { @@ -192,10 +200,11 @@ func GetOrLaunchForSession(l *Launcher, serverID, sessionID string) (*mcp.Connec return nil, err } - // For HTTP backends, use the regular GetOrLaunch (stateless) + // For HTTP backends, use the stateless connection pool, reusing the + // already-fetched config to avoid acquiring the config read-lock twice. if serverCfg.Type == "http" { logLauncher.Printf("HTTP backend detected, using stateless connection: serverID=%s", serverID) - return GetOrLaunch(l, serverID) + return getOrLaunchWithConfig(l, serverID, serverCfg) } logLauncher.Printf("Checking session pool: serverID=%s, sessionID=%s", serverID, sessionID) From 06d08fb0f8850673c8658aa6679f542186f119e5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 9 Jun 2026 14:46:45 +0000 Subject: [PATCH 2/2] fix(launcher): preserve GetOrLaunch debug log on HTTP session path --- internal/launcher/launcher.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/launcher/launcher.go b/internal/launcher/launcher.go index 284953cc8..1b2d2e8a2 100644 --- a/internal/launcher/launcher.go +++ b/internal/launcher/launcher.go @@ -204,6 +204,7 @@ func GetOrLaunchForSession(l *Launcher, serverID, sessionID string) (*mcp.Connec // already-fetched config to avoid acquiring the config read-lock twice. if serverCfg.Type == "http" { logLauncher.Printf("HTTP backend detected, using stateless connection: serverID=%s", serverID) + logger.LogDebugToServer(serverID, "backend", "GetOrLaunch called for server: %s", serverID) return getOrLaunchWithConfig(l, serverID, serverCfg) }