perf(analyze): partial index on objective_corpus_room_month (~3.8x on hot velocity baseline) #1942
No reviewers
Labels
No labels
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
Fable 5 ревью
feedback/max
generative
GG-форсайт
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
вторичка
ИРД
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#1942
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "perf/171-objective-crm-report-month-idx"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Что
Partial-индекс на
objective_corpus_room_month (report_month) WHERE deals_total_count > 0. Ускоряет EKB-wide velocity-baseline, который считается на КАЖДОМ/analyze.Почему (найдено через
pg_stat_user_tablesseq-scan аудит + database-expert EXPLAIN)_get_ekb_median()(velocity.py:706) →compute_velocity()→POST /parcels/{cad}/analyze(parcels.py:3157) — раз на каждый analyze (самый горячий эндпоинт). Предикатыreport_month >= now-6mo AND deals_total_count > 0не имели пригодного индекса (5 существующих report_month-индексов не partial поdeals_total_count; голый range = 27% строк → планнер корректно выбирал Seq Scan по всей таблице ~95 MB).Доказательство (prod EXPLAIN ANALYZE, BEGIN/ROLLBACK)
Планнер использует индекс естественно (без
enable_seqscanхаков). Независимо перепроверил dry-run'ом:Index Only Scan using idx_objective_crm_report_month_sold, 2747 buffers, 2.9ms на фильтре.Безопасность
objective_etl.py:288), не request-path → write-overhead пренебрежим.IF NOT EXISTS(идемпотентно). ПлейнCREATE INDEX(не CONCURRENTLY — нельзя в BEGIN/COMMIT миграции); build sub-second, SHARE-lock блокирует лишь weekly-ETL writer, не analyze-читателей.Migration
data/sql/171_*. Database-expert проверил и 3 других seq-scan-таблицы (objective_lots/cad_buildings уже оптимизированы #70; domrf_kn_flats нужен query-rewrite, не индекс — отдельный follow-up)._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.