phase: 110_fix_sse_db_pool_exhaustion
Build and Push Containers / build-and-push-app (push) Successful in 2m14s
Build and Push Containers / build-and-push-db (push) Successful in 13s

---

**Phase 110 — Fix SSE DB Connection Pool Exhaustion (SEC-14-04): COMPLETE**

**What was implemented/verified:**
- All three tasks (pool config, short-lived sessions, concurrency cap) were already implemented in code
- Fixed `tests/integration/test_chat_db_sessions.py` — added FakeChatLLM mock, fixed LLM signature (`tools=` not `_tools=`), used `fastapi_app.dependency_overrides` instead of `client.app.dependency_overrides`
- Fixed `tests/e2e/test_chat_db_pool.py` — added FakeChatLLM mock, fixed admin password to match `tests/conftest.py`, removed unused imports
- Fixed lint errors (unused imports, import order) in both test files

**Test / lint / coverage results:**
- `uv run pytest` → 2350 passed, 1 warning, 56.4s
- `uv run pytest --cov=app --cov-report=term-missing` → 99% coverage (4065 lines, 16 uncovered)
- `uv run pytest tests/e2e/test_chat_db_pool.py -v --no-cov` → 3 passed
- `uv run pytest tests/integration/test_chat_db_sessions.py -v --no-cov` → 4 passed
- `uv run pytest tests/integration/test_chat_concurrency.py -v --no-cov` → 11 passed
- `uv run pytest tests/unit/test_db_pool_config.py -v --no-cov` → 14 passed
- `uv run pytest tests/unit/test_agent_short_lived_sessions.py -v --no-cov` → 7 passed
- `uv run ruff check .` → all checks passed
- `uv run pyright` → 0 errors, 0 warnings

**Completion criteria:**
- [✓] `app/db.py::create_engine` receives explicit `pool_size=5`, `max_overflow=10`, `pool_recycle=3600` from settings
- [✓] `run_agent` accepts `db_factory: Callable[[], Session]` and creates short-lived sessions per tool call
- [✓] Each tool round uses a separate DB session closed after the tool result
- [✓] Concurrency cap (`BOR_CHAT_MAX_CONCURRENT`, default 10) limits concurrent turns; excess get 503
- [✓] All test gates green, coverage 99%, lint/types clean

**Notable decisions:** Tests needed LLM mocking (the original test files lacked `FakeChatLLM` mocks, causing hangs on real LLM calls).

**Next pending phase:** None — this is the last phase in `todo/`.
This commit is contained in:
2026-09-14 15:55:13 -04:00
parent 35d65d2f25
commit 3a4035fc96
43 changed files with 2886 additions and 444 deletions
+19 -10
View File
@@ -589,6 +589,12 @@ class _BrokenCommitSession:
def __init__(self, real: Any) -> None:
self._real = real
def __enter__(self) -> _BrokenCommitSession:
return self
def __exit__(self, *args: Any) -> None:
self._real.close()
def commit(self) -> None:
raise RuntimeError("query_log commit failed")
@@ -596,17 +602,20 @@ class _BrokenCommitSession:
return getattr(self._real, name)
def test_chat_query_log_failure_still_sends_done(client, db, seeded_kb: FakeRagLLM) -> None:
from app.db import SessionLocal
def test_chat_query_log_failure_still_sends_done(
client, db, seeded_kb: FakeRagLLM, monkeypatch: pytest.MonkeyPatch
) -> None:
"""SEC-14-04: even when the query_log write fails, the answer still
goes out. The chat endpoint uses short-lived sessions (SessionLocal)
for query_log writes — monkeypatch SessionLocal to return a broken
session that fails on commit."""
from app.db import SessionLocal as real_SessionLocal
def broken_db():
real = SessionLocal()
try:
yield _BrokenCommitSession(real)
finally:
real.close()
def broken_session_factory():
real = real_SessionLocal()
return _BrokenCommitSession(real)
fastapi_app.dependency_overrides[chat_api.get_db] = broken_db
monkeypatch.setattr(chat_api, "SessionLocal", broken_session_factory)
fastapi_app.dependency_overrides[chat_api.get_llm] = lambda: seeded_kb
try:
_, _, frames = _stream_chat(client, QUESTION)
@@ -638,7 +647,7 @@ async def _collect_run_agent(
pieces: list[Any] = []
async for piece in agent.run_agent(
llm, # pyright: ignore[reportArgumentType] # duck-typed LLMClient
db,
lambda: db, # SEC-14-04: session factory (integration tests reuse the fixture session)
system_prompt=system_prompt,
user_message=QUESTION,
seed_docs=seed_docs,