From 403020ad56661d77f2700108391f2879da68f081 Mon Sep 17 00:00:00 2001 From: bot-backend Date: Wed, 17 Jun 2026 20:50:51 +0300 Subject: [PATCH] fix(sql): restore partial predicate on 5 listings indexes (#1398) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 097_index_hygiene.sql de-partialized 5 listings indexes on the false premise that is_active=100% true. 104_index_hygiene_geom_dedup.sql acknowledged the premise is stale (49% active on prod, 2026-06-13) but only cleaned up the redundant geom duplicate, leaving 3 composite indexes full. Restore WHERE is_active=true on the composite hot-path indexes: - listings_active_filter_idx (rooms, price_rub, area_m2, scraped_at DESC) - listings_house_price_idx (house_id_fk, price_rub) - listings_rooms_area_idx (rooms, area_m2) Excluded: listings_geom_active_idx (dropped by 104, listings_geom_idx covers it), listings_scraped_desc_idx (not in issue #1398 suggested fix scope). CONCURRENTLY not used — migration runner wraps in BEGIN/COMMIT (see 117 note). --- .../120_restore_partial_active_indexes.sql | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 tradein-mvp/backend/data/sql/120_restore_partial_active_indexes.sql diff --git a/tradein-mvp/backend/data/sql/120_restore_partial_active_indexes.sql b/tradein-mvp/backend/data/sql/120_restore_partial_active_indexes.sql new file mode 100644 index 00000000..bb86409d --- /dev/null +++ b/tradein-mvp/backend/data/sql/120_restore_partial_active_indexes.sql @@ -0,0 +1,68 @@ +-- 120_restore_partial_active_indexes.sql +-- Fixes #1398: restore WHERE is_active = true predicate on 3 composite listings indexes +-- that were de-partialized by 097_index_hygiene.sql on the false premise that +-- is_active is 100% true. 104_index_hygiene_geom_dedup.sql explicitly acknowledges the +-- premise is stale (prod 2026-06-13: 19312 active / 39115 total, ~49% active), but +-- only dropped the redundant geom duplicate -- the composite indexes were left full. +-- +-- Indexes restored with WHERE is_active = true predicate: +-- 1. listings_active_filter_idx (rooms, price_rub, area_m2, scraped_at DESC) +-- 2. listings_house_price_idx (house_id_fk, price_rub) +-- 3. listings_rooms_area_idx (rooms, area_m2) +-- +-- These indexes directly back the estimator hot-path (estimator.py _COMMON_WHERE: +-- AND rooms = :rooms AND area_m2 BETWEEN ... AND is_active = true AND price_rub > 0) +-- and asking_to_sold_ratio.py, which always filters is_active = true. +-- Restoring the predicate excludes ~51% inactive rows from the index, making it +-- roughly half the size and allowing index-only scans on active-only queries. +-- +-- Excluded from restore: +-- listings_geom_active_idx -- dropped by 104 as exact dup of listings_geom_idx +-- (keep listings_geom_idx as the single full GIST) +-- listings_scraped_desc_idx -- simple recency sort not in hot-path composite filters; +-- issue #1398 suggested fix does not include it +-- +-- Note on CONCURRENTLY: +-- NOT used here. The deploy migration runner wraps each file in an explicit +-- transaction (BEGIN/COMMIT). CREATE INDEX CONCURRENTLY cannot run inside a +-- transaction block. At ~40k rows the ACCESS EXCLUSIVE lock is sub-second. +-- See migration 117_listings_last_seen_deactivate_index.sql for the same rationale. +-- +-- Idempotency: +-- DROP INDEX IF EXISTS + CREATE INDEX IF NOT EXISTS -- safe to re-run. +-- +-- Dependencies: 002_core_tables.sql, 050_search_optimization.sql, +-- 097_index_hygiene.sql (de-partialized these indexes), +-- 104_index_hygiene_geom_dedup.sql (acknowledges stale premise) +-- Deploy order: standalone, apply any time after 097 + 104. + +BEGIN; + +-- 1. listings_active_filter_idx +-- Original (050_search_optimization.sql:52): +-- ON listings (rooms, price_rub, area_m2, scraped_at DESC) WHERE is_active = true +-- 097 stripped the predicate. Restoring it. +DROP INDEX IF EXISTS listings_active_filter_idx; +CREATE INDEX IF NOT EXISTS listings_active_filter_idx + ON listings (rooms, price_rub, area_m2, scraped_at DESC) + WHERE is_active = true; + +-- 2. listings_house_price_idx +-- Original (050_search_optimization.sql:57): +-- ON listings (house_id_fk, price_rub) WHERE is_active = true +-- 097 stripped the predicate. Restoring it. +DROP INDEX IF EXISTS listings_house_price_idx; +CREATE INDEX IF NOT EXISTS listings_house_price_idx + ON listings (house_id_fk, price_rub) + WHERE is_active = true; + +-- 3. listings_rooms_area_idx +-- Original (002_core_tables.sql:58): +-- ON listings (rooms, area_m2) WHERE is_active = true +-- 097 stripped the predicate. Restoring it. +DROP INDEX IF EXISTS listings_rooms_area_idx; +CREATE INDEX IF NOT EXISTS listings_rooms_area_idx + ON listings (rooms, area_m2) + WHERE is_active = true; + +COMMIT;