fix: close DB session in run_step finally block

Wraps the SessionLocal body in try/finally so db.close() is always
called, preventing connection leaks on exception. Updates the
test monkeypatch to use a no-op close() proxy so the transactional
fixture stays live after run_step returns.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-07 06:42:37 -07:00
co-authored by Claude Sonnet 4.6
parent e3ca8b7a96
commit 914fdbdb51
2 changed files with 25 additions and 13 deletions
+15 -12
View File
@@ -63,18 +63,21 @@ def run_step(step_name: str, week_start_date: Optional[date] = None) -> None:
week = week_start_date or _current_week_start() week = week_start_date or _current_week_start()
db = SessionLocal() db = SessionLocal()
families = db.query(FamilyProfile).all() try:
if not families: families = db.query(FamilyProfile).all()
logger.warning("run_step(%s): no family profiles, skipping", step_name) if not families:
return logger.warning("run_step(%s): no family profiles, skipping", step_name)
for family in families: return
run = _get_or_create_run(db, family.id, week) for family in families:
try: run = _get_or_create_run(db, family.id, week)
step_fns[step_name](run, db) try:
except Exception: step_fns[step_name](run, db)
logger.exception( except Exception:
"run_step(%s) failed for family %s", step_name, family.id logger.exception(
) "run_step(%s) failed for family %s", step_name, family.id
)
finally:
db.close()
def run_week(week_start_date: Optional[date] = None) -> None: def run_week(week_start_date: Optional[date] = None) -> None:
+10 -1
View File
@@ -434,9 +434,18 @@ def test_run_step_upserts_weekly_run(db, family, monkeypatch):
"app.services.orchestrator.steps.ScraperService.run_scrape", "app.services.orchestrator.steps.ScraperService.run_scrape",
lambda self, **kw: {"status": "success", "items_scraped": 0}, lambda self, **kw: {"status": "success", "items_scraped": 0},
) )
class _NoCloseSession:
"""Proxy that delegates all attribute access to the test session
but turns close() into a no-op so the transactional fixture stays live."""
def __getattr__(self, name):
return getattr(db, name)
def close(self):
pass
monkeypatch.setattr( monkeypatch.setattr(
"app.services.orchestrator.runner.SessionLocal", "app.services.orchestrator.runner.SessionLocal",
lambda: db, lambda: _NoCloseSession(),
) )
from app.services.orchestrator.runner import run_step from app.services.orchestrator.runner import run_step
run_step("scrape", WEEK) run_step("scrape", WEEK)