mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-04 21:06:03 +00:00
* perf: reduce memory usage by ~200MB+ at startup
Two key optimizations:
1. Use importlib.util.find_spec() instead of __import__() in dependency
checking. find_spec() only locates modules without executing them,
avoiding loading all 36 dependencies (~222MB) into memory at startup.
2. Introduce shared aiohttp.ClientSession via httpclient module.
Previously, every HTTP request created a new ClientSession, which
creates a new TCPConnector and SSL context, loading system root
certificates each time (~270MB total allocations observed via memray).
Now all HTTP client code reuses shared sessions.
- satori.py and coze_server_api/client.py are left unchanged as they
create one session per adapter lifecycle (not per-request).
Profiling data (memray):
- Peak memory: 403MB
- SSL context creation: 270MB / 6.7M allocations (67% of total)
- Dependency import: 222MB (55% of peak)
- Expected reduction: 150-350MB at startup
* fix: remove unused aiohttp imports (ruff F401)
* style: ruff format
44 lines
1.3 KiB
Python
44 lines
1.3 KiB
Python
"""Shared aiohttp.ClientSession to avoid repeated SSL context creation.
|
|
|
|
Each call to `aiohttp.ClientSession()` creates a new `TCPConnector` which in turn
|
|
creates a new `ssl.SSLContext` and loads all system root certificates. This is
|
|
extremely expensive in both CPU and memory (~270MB total allocations observed via
|
|
memray profiling).
|
|
|
|
This module provides a shared session pool so that all HTTP client code in LangBot
|
|
reuses the same underlying SSL context and connection pool.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import aiohttp
|
|
|
|
_sessions: dict[str, aiohttp.ClientSession] = {}
|
|
|
|
|
|
def get_session(*, trust_env: bool = False) -> aiohttp.ClientSession:
|
|
"""Get or create a shared aiohttp.ClientSession.
|
|
|
|
Args:
|
|
trust_env: Whether to trust environment variables for proxy settings.
|
|
|
|
Returns:
|
|
A shared aiohttp.ClientSession instance.
|
|
"""
|
|
key = f'trust_env={trust_env}'
|
|
|
|
session = _sessions.get(key)
|
|
if session is None or session.closed:
|
|
session = aiohttp.ClientSession(trust_env=trust_env)
|
|
_sessions[key] = session
|
|
|
|
return session
|
|
|
|
|
|
async def close_all():
|
|
"""Close all shared sessions. Call on application shutdown."""
|
|
for session in _sessions.values():
|
|
if not session.closed:
|
|
await session.close()
|
|
_sessions.clear()
|