Effort: deep
api.py is full of code patterns like
for hyp in hypotheses: row = db.execute("SELECT ... WHERE id=?", (hyp.id,)) —
the canonical N+1 anti-pattern. With 310 hypotheses headed to 10K
and 15K papers headed to 1M, the same code that returns in 80ms
today will return in 8s. Ship a runtime detector that flags routes
that issue >N queries against the same statement template within a
single request, plus a small helper library to convert those
patterns to WHERE id = ANY($1) batch calls.
api_shared/n_plus_one_detector.py:_request_db_holder in api_shared/db.py) and counts(query_template_hash, route_label) per$N. When the count exceeds N_PLUS_ONE_THRESHOLD=8 forWARN with route, hash,n_plus_one_incidents (route,GET /senate/n-plus-one lists top offenders bycount × frequency, with a "view sample request" expanderq-obs-trace-id-propagation lookup endpoint toapi_shared/batch_query.py:batch_in(db, sql_template, ids, chunk=500) -> list[dict]WHERE id = ANY($1) chunked.batch_lookup(db, table, ids, columns) -> dict[id, row] —prefetch(rows, fk_field, table, columns) -> list[rows] —tests/test_n_plus_one_detector.py: syntheticcount=50. Helper round-trips batch_in against a 10K-row@pytest.mark.no_n_plus_one fails any test that triggers aq-obs-trace-id-propagation — for replay-sample button.q-perf-hot-path-optimizer — supplies route labels.q-perf-deferred-work-queue — many N+1 sites are heavyFiles created / modified:
api_shared/n_plus_one_detector.py — ContextVar-based per-request query counter.$N), tracks count + total_ms per template hash per request.close_request_db_scope), emits one WARN per offending template withroute, hash, count, total_ms, and template snippet. Upserts to n_plus_one_incidents.ENVIRONMENT != 'prod'), configurable via N_PLUS_ONE_SAMPLE_RATE (default 1% in prod).
api_shared/batch_query.py — Three stateless helpers:batch_in(db, sql_template, ids, chunk=500) — replaces {ids} token with ANY(%s), chunked.batch_lookup(db, table, ids, columns) — returns dict[id → row_dict] for FK hydration.prefetch(rows, fk_field, table, columns) — one-shot N+1 → batch pattern; merges FK data under {fk_field}_data.
api_shared/db.py — Three hook points added: open_request_db_scope() opens detector scope;PGConnection.execute() calls record_query(); close_request_db_scope() calls flush_request().except so detector is never on the critical failure path.
migrations/20260427_n_plus_one_incidents.sql — Creates n_plus_one_incidents table withUNIQUE(route, query_template_hash) for upsert aggregation. Applied to local DB.
tests/test_n_plus_one_detector.py — 19 unit tests (all passing). Includes: normalize_sql stability,count=50, below-threshold no-warn, independent template tracking,batch_in/batch_lookup/prefetch mock tests, and one @pytest.mark.integrationbatch_in against live 10K-row table.
tests/conftest.py — Created _no_n_plus_one_guard autouse fixture; installs log handler during@pytest.mark.no_n_plus_one tests, fails on any detector WARNING.
pyproject.toml — Registered no_n_plus_one marker.api.py — Added GET /senate/n-plus-one route after /senate/hot-paths. Shows top 50 offenderscount DESC, summary stat cards (distinct offenders, total redundant queries, total wasted ms),batch_lookup.Acceptance criteria status:
N_PLUS_ONE_THRESHOLD=8 + WARN loggingn_plus_one_incidents table (migration applied)/senate/n-plus-one page (no trace-replay button — q-obs-trace-id-propagation still open)batch_query.py with batch_in, batch_lookup, prefetchno_n_plus_one pytest marker + conftest enforcement