Skip to content

refactor: replace per-call httpx clients with module-level singletons#67

Closed
bolinocroustibat wants to merge 1 commit into
mainfrom
refactor/httpx-singleton-clients
Closed

refactor: replace per-call httpx clients with module-level singletons#67
bolinocroustibat wants to merge 1 commit into
mainfrom
refactor/httpx-singleton-clients

Conversation

@bolinocroustibat
Copy link
Copy Markdown
Collaborator

@bolinocroustibat bolinocroustibat commented Mar 5, 2026

Related to #86.

httpx.AsyncClient is costly to spin up and tear down on every request: HTTPS repeats TLS handshakes and SSL context work. Clients are closed in finally today, so this is not a leak, but repeating this cycle may still cost CPU and memory under high load.

When removing Matomo httpx clients, production then saw fewer OOM-related restarts (see #86).
#88 used one shared client for Matomo to try to solve this issue, reducing the number of short-lived clients.
If that helps reducing memory overhead, then sharing clients for the API helpers could reduce similar overhead the same way. This PR applies the same idea for the remaining API clients.

Profiling

Memray on ~40 outbound calls in one short LLM session showed ~295 MB cumulative SSL-related allocations with the old per-call pattern.
Most of that is transient, so a local profiling didn't prove production wins, but we hope connection pooling lowers that work under real traffic.

Significant Changes

  • one module-level AsyncClient per helper with pooled connections
  • optional session unchanged for tests
  • pytest-asyncio session-scoped event loop so teardown does not hit "event loop is closed".

We do not know yet that this alone fixes #86, please treat it as a reasonable follow-up to #88 and validate with prod traffic metrics.

@bolinocroustibat bolinocroustibat self-assigned this Mar 5, 2026
@bolinocroustibat bolinocroustibat force-pushed the refactor/httpx-singleton-clients branch from 7ae3b21 to 07d82f5 Compare March 25, 2026 17:52
@bolinocroustibat bolinocroustibat added 3 - Low priority Nice-to-have perf Latency of tool calls, memory usage, connection throughput labels Mar 31, 2026
@bolinocroustibat bolinocroustibat force-pushed the refactor/httpx-singleton-clients branch from 07d82f5 to 29bbf47 Compare April 1, 2026 16:43
Each helper module previously created a new httpx.AsyncClient on every
function call and closed it immediately after, causing a full TLS handshake
on every outbound request. A memray profiling session showed this pattern
accounted for 295 MB of cumulative SSL context allocations across a single
short LLM session (~40 outbound calls).

Each helper now holds one shared AsyncClient created at import time. The
optional `session` parameter is kept on all public functions as a test
override hook. pytest-asyncio is configured with a session-scoped event
loop so module-level clients survive across tests without triggering
"event loop is closed" errors on teardown.

Made-with: Cursor

# Conflicts:
#	helpers/datagouv_api_client.py

# Conflicts:
#	helpers/matomo.py
@bolinocroustibat bolinocroustibat force-pushed the refactor/httpx-singleton-clients branch from 9f1bb5a to b51a293 Compare April 1, 2026 17:08
@bolinocroustibat bolinocroustibat added 2 - Moderate priority Important but not urgent 3 - Low priority Nice-to-have and removed 3 - Low priority Nice-to-have 2 - Moderate priority Important but not urgent labels Apr 1, 2026
@bolinocroustibat
Copy link
Copy Markdown
Collaborator Author

Since #86 seems to be solved with fewer profiling/performance calls to Sentry, this PR might not be useful anymore. Let's confirm all is good on production for a few days and than close it without merging.

@bolinocroustibat bolinocroustibat removed the request for review from maudetes April 2, 2026 13:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3 - Low priority Nice-to-have perf Latency of tool calls, memory usage, connection throughput

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MCP Out of Memory errors on production instances

1 participant