All checks were successful
CI / changes (push) Successful in 7s
CI / changes (pull_request) Successful in 6s
CI / frontend-tests (push) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (push) Successful in 2m3s
CI / openapi-codegen-check (pull_request) Successful in 2m7s
CI / backend-tests (push) Successful in 8m55s
CI / backend-tests (pull_request) Successful in 8m58s
Deploy / changes (push) Successful in 6s
Deploy / build-frontend (push) Has been skipped
Deploy / build-backend (push) Successful in 29s
Deploy / build-worker (push) Successful in 29s
Deploy / deploy (push) Successful in 1m10s
#70: EXPLAIN top-5 hot-path на prod — analyze district-price block по objective_lots (1.12M) делал Bitmap Heap Scan ~41k блоков (cold 5.3s/warm 474ms), пробивая p95 ≤3с. Partial covering-index (district, price_per_m2_rub) WHERE price_per_m2_rub IS NOT NULL → Index Only Scan (verified scratch: 87ms, Heap Fetches 0). Требует VACUUM ANALYZE objective_lots post-deploy (stale VM иначе глушит выигрыш). database-expert ✅ verified. #79: 152-ФЗ PII grep-аудит — plain-PII в логах НЕ найдено; Sentry scrub / PRINZIP-хеширование / LLM §19-redaction подтверждены. Остаточный риск: pilot_requests хранит сырые ПДн без retention (рекомендация в docs). httpx DEBUG off (ops-инвариант). Closes #70 Closes #79
45 lines
3.3 KiB
PL/PgSQL
45 lines
3.3 KiB
PL/PgSQL
-- 153_objective_lots_district_price_idx.sql
|
||
-- Issue #70 (B4-3 Performance аудит) — covering-index для analyze district-price block.
|
||
--
|
||
-- ПРОБЛЕМА (EXPLAIN ANALYZE на prod, district='Академический', 2026-06-13):
|
||
-- app/api/v1/parcels.py:2123 (analyze hot-path, "B5-5 District price ranges")
|
||
-- SELECT MIN/MAX/PERCENTILE_CONT(price_per_m2_rub), COUNT(*)
|
||
-- FROM objective_lots
|
||
-- WHERE district = :dn AND price_per_m2_rub IS NOT NULL
|
||
-- AND price_per_m2_rub BETWEEN 30000 AND 600000
|
||
-- На плотном районе матчит ~182k строк (objective_lots ~1.12M строк всего).
|
||
-- Существующий objective_lots_district_class_idx (district, class) даёт только
|
||
-- district-match по индексу, НО price_per_m2_rub лежит в heap → Bitmap Heap Scan
|
||
-- читает ~41k heap-блоков (~320 МБ) чтобы достать цену каждой строки.
|
||
-- Замеры: cold-cache 5286 ms, warm-cache 499 ms — оба пробивают analyze
|
||
-- p95-таргет ≤3 с (issue #70 targets) на холодном кэше / плотных районах.
|
||
--
|
||
-- РЕШЕНИЕ: частичный covering-index (district, price_per_m2_rub). Обе колонки —
|
||
-- и фильтр, и агрегируемое значение — в индексе → планировщик может сделать
|
||
-- Index Only Scan (читает ~283 листовых страницы индекса вместо 41k heap-блоков),
|
||
-- heap не трогается (видимость закрывает visibility map после VACUUM).
|
||
-- WHERE price_per_m2_rub IS NOT NULL — частичный: индексируем только строки с
|
||
-- ценой (~919k из 1.12M), отсекая ~204k NULL-цен; зеркалит предикат запроса
|
||
-- (IS NOT NULL) и сужает индекс. Конкретный район в запросе делает Index Cond
|
||
-- по первой колонке, BETWEEN — диапазон по второй.
|
||
--
|
||
-- Dependencies: objective_lots (существует; обновляется objective_etl). PG 16.4.
|
||
-- Idempotent: CREATE INDEX IF NOT EXISTS. Не-CONCURRENTLY намеренно — deploy.yml
|
||
-- гоняет файл в транзакции (BEGIN/COMMIT), а CREATE INDEX CONCURRENTLY в tx
|
||
-- запрещён. Index ~45 MB, build несколько секунд; обычный CREATE INDEX берёт
|
||
-- SHARE-lock (блокирует только writes/objective_etl, НЕ analyze-читателей) —
|
||
-- приемлемо в deploy-окне (как индексы 87/97). Не запускать одновременно с ETL.
|
||
-- Applied: automatically via deploy.yml in NN order (трекер _schema_migrations).
|
||
|
||
BEGIN;
|
||
|
||
CREATE INDEX IF NOT EXISTS idx_objective_lots_district_price
|
||
ON objective_lots (district, price_per_m2_rub)
|
||
WHERE price_per_m2_rub IS NOT NULL;
|
||
|
||
COMMENT ON INDEX idx_objective_lots_district_price IS
|
||
'Covering-index для analyze district-price block (#70): district-фильтр + '
|
||
'агрегаты по price_per_m2_rub через Index Only Scan, без Bitmap Heap Scan '
|
||
'~41k блоков. См. parcels.py:2123, docs/Performance_Audit.md.';
|
||
|
||
COMMIT;
|