Skip to content

[PERFORMANCE]: Configure HTTP Client Connection Pool Limits #1676

@crivetimihai

Description

@crivetimihai

Configure HTTP client connection pool limits to prevent connection exhaustion and improve reliability. Many places create new httpx.AsyncClient instances per request, preventing connection reuse.

Current State Analysis

Already Optimized (Persistent Clients)

These services correctly create a persistent HTTP client:

Location Code Status
mcpgateway/utils/retry_manager.py:273 self.client = httpx.AsyncClient(**self.client_args) Good
mcpgateway/federation/forward.py:101 self._http_client = httpx.AsyncClient(...) Good
mcpgateway/services/server_service.py:135 self._http_client = httpx.AsyncClient(...) Good
mcpgateway/federation/discovery.py:284 self._http_client = httpx.AsyncClient(...) Good
mcpgateway/services/llm_proxy_service.py:73 self._client = httpx.AsyncClient(...) Good

Problem Areas (New Client Per Request)

These files create new clients per request, preventing connection reuse:

Location Line Context
mcpgateway/translate.py 1174, 1331, 1582, 1698 HTTP-to-MCP translation
mcpgateway/routers/llm_admin_router.py 691 LLM admin operations
mcpgateway/services/llm_provider_service.py 647 LLM provider checks
mcpgateway/services/sso_service.py 460, 479 SSO token exchange
mcpgateway/services/tool_service.py 2649 Tool HTTP invocation
mcpgateway/utils/keycloak_discovery.py 47 OIDC discovery
mcpgateway/services/catalog_service.py 424 Catalog fetching
mcpgateway/services/a2a_service.py 856 Agent-to-agent calls
mcpgateway/services/gateway_service.py 2664 Gateway peer communication

Example Problem Pattern

# Found in translate.py:1174, sso_service.py:460, etc.
async with httpx.AsyncClient(headers=headers, timeout=...) as client:
    response = await client.get(url)
    # Client is closed immediately after - no connection reuse!

Proposed Solution

Create a shared HTTP client factory:

# mcpgateway/utils/http_client.py
from functools import lru_cache
import httpx

@lru_cache
def get_http_client() -> httpx.AsyncClient:
    return httpx.AsyncClient(
        limits=httpx.Limits(
            max_keepalive_connections=100,
            max_connections=200,
            keepalive_expiry=30.0,
        ),
        timeout=httpx.Timeout(30.0, connect=10.0),
    )

Configuration

HTTP_CLIENT_MAX_KEEPALIVE=100
HTTP_CLIENT_MAX_CONNECTIONS=200
HTTP_CLIENT_KEEPALIVE_EXPIRY=30.0
HTTP_CLIENT_TIMEOUT=30.0
HTTP_CLIENT_CONNECT_TIMEOUT=10.0

Acceptance Criteria

  • Shared HTTP client factory created
  • Problem files updated to use shared client
  • Connection pool limits configurable via environment
  • Lifespan cleanup in main.py
  • Connection pool metrics exposed
  • All HTTP-related tests pass
  • Passes make verify

Metadata

Metadata

Assignees

Labels

enhancementNew feature or requestperformancePerformance related itemspythonPython / backend development (FastAPI)

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions