feat(perf): implement HTTP connection pooling with httpx.AsyncClient singleton and Keep-Alive

- get_http_client: singleton AsyncClient with limits(max_keepalive=10, max_connections=20, keepalive_expiry=60s)
- close_http_client: clean socket pool teardown in FastAPI lifespan shutdown
- refactor dictionary fetchers to reuse pooled client and avoid 5x TLS handshakes per word lookup
- add test_http_connection_pooling (22/22 pytest passed)
This commit is contained in:
2026-10-06 11:34:31 +03:00
parent c4a6edee37
commit 438dfdb055
4 changed files with 211 additions and 132 deletions
+31
View File
@@ -1011,3 +1011,34 @@ def test_books_pdf_multipart_upload():
# Cleanup
client.delete(f"/api/books/{book_id}")
def test_http_connection_pooling():
import pytest
from app.dictionary import get_http_client, close_http_client, lookup_term
import asyncio
# 1. Singleton returns same instance
c1 = get_http_client()
c2 = get_http_client()
assert c1 is c2
assert not c1.is_closed
# 2. Check connection limits configuration
pool = c1._transport._pool
assert pool._max_keepalive_connections == 10
assert pool._max_connections == 20
# 3. Lookup using pooled client
res = asyncio.run(lookup_term("resilience"))
assert res["query"] == "resilience"
assert "translation" in res
# 4. Clean closure on shutdown
asyncio.run(close_http_client())
assert c1.is_closed
# Next call creates a new active client
c3 = get_http_client()
assert c3 is not c1
assert not c3.is_closed
asyncio.run(close_http_client())