_get_ekb_median() (velocity.py:706) runs on EVERY POST /parcels/{cad}/analyze
(the hottest endpoint) and seq-scanned the whole objective_corpus_room_month
(~95MB, ~12159 buffers, 144ms) — its predicates (report_month >= now-6mo AND
deals_total_count > 0) had no usable index (the 5 existing report_month indexes
aren't partial on deals_total_count; a bare range matches 27% of rows, so the
planner correctly chose Seq Scan).
Add partial b-tree (report_month) WHERE deals_total_count > 0 (~280kB, 8.9%
selectivity). Prod EXPLAIN (BEGIN/ROLLBACK): 144ms→38ms (~3.8x), buffers
12281→3136 (-74%); planner uses it naturally (Index/Bitmap scan). Independently
dry-run-verified: Index Only Scan, 2747 buffers.
Write cost negligible (objective_corpus_room_month written only by weekly ETL,
not request-path). Idempotent (IF NOT EXISTS); plain CREATE INDEX (not
CONCURRENTLY, can't run in the migration's BEGIN/COMMIT) — sub-second build,
SHARE lock blocks only the weekly ETL writer, not analyze readers.
Found via pg_stat_user_tables seq-scan audit + database-expert EXPLAIN analysis.