From dce3251713a2c5b582f33135a51c80aa807d9505 Mon Sep 17 00:00:00 2001 From: bot-backend Date: Sun, 31 May 2026 16:20:28 +0300 Subject: [PATCH] =?UTF-8?q?perf(tradein):=20index=20hygiene=20=E2=80=94=20?= =?UTF-8?q?drop=20dup/redundant=20+=20de-partial=20is=5Factive=20(#730)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Deterministic subset of TradeinDb_Audit_May30. Migration 097 (BEGIN/COMMIT, IF EXISTS, idempotent): - drop 4 exact-duplicate indexes (houses_kadastr_idx2, houses_addr_fp_idx, listings_scraped_idx, listing_sources_price_divergence_idx) + redundant houses_source_ext_idx (covered by UNIQUE constraint backing index) - de-partial 5 listings WHERE is_active=true indexes (is_active 100% true → full index strictly covers partial, no plan regression) 87-unused sweep DEFERRED — needs a live pg_stat_user_indexes snapshot. Refs #730 --- .../backend/data/sql/097_index_hygiene.sql | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 tradein-mvp/backend/data/sql/097_index_hygiene.sql diff --git a/tradein-mvp/backend/data/sql/097_index_hygiene.sql b/tradein-mvp/backend/data/sql/097_index_hygiene.sql new file mode 100644 index 00000000..5924bb2f --- /dev/null +++ b/tradein-mvp/backend/data/sql/097_index_hygiene.sql @@ -0,0 +1,93 @@ +-- 097_index_hygiene.sql +-- #730 index hygiene: drop exact-duplicate + redundant indexes; +-- de-partial is_active indexes. 87-unused sweep deferred (needs live pg_stat snapshot). +-- +-- Context (Issue #730): +-- Duplicate drops: 5 named indexes that are exact duplicates of a surviving twin +-- or covered by a UNIQUE constraint (all confirmed from migration history below). +-- De-partial: 5 indexes on listings using WHERE is_active = true; since is_active +-- is 100% true in production the partial predicate saves nothing — a full index +-- strictly covers the same rows with no plan regression. +-- +-- Idempotency: +-- All DROP INDEX use IF EXISTS; all CREATE INDEX use IF NOT EXISTS. +-- Safe to re-run. +-- +-- Source evidence (each pair confirmed from migration files): +-- houses_kadastr_idx2 (050:93) dup of houses_cadastral_number_idx (029:180) +-- houses_addr_fp_idx (028:20) dup of houses_fingerprint_idx2 (050:90) +-- listings_scraped_idx (002:59) dup of listings_scraped_desc_idx (050:62) +-- listing_sources_price_divergence_idx (042:8) dup of _idx2 (050:84) +-- houses_source_ext_idx (009:59) redundant — covered by UNIQUE(source,ext_house_id) (009:54) +-- +-- Dependencies: +-- 002_core_tables.sql, 009_houses.sql, 028_matching_tables.sql, +-- 029_extend_matching_valuation_dynamics.sql, 042_listing_sources_price_divergence_idx.sql, +-- 050_search_optimization.sql + +BEGIN; + +-- ============================================================================= +-- Step 1: Drop exact-duplicate and redundant indexes +-- ============================================================================= + +-- dup of houses_cadastral_number_idx (029_extend_matching_valuation_dynamics.sql:180) +-- same: ON houses (cadastral_number) WHERE cadastral_number IS NOT NULL +DROP INDEX IF EXISTS houses_kadastr_idx2; + +-- dup of houses_fingerprint_idx2 (050_search_optimization.sql:90) +-- same: ON houses (address_fingerprint) WHERE address_fingerprint IS NOT NULL +DROP INDEX IF EXISTS houses_addr_fp_idx; + +-- dup of listings_scraped_desc_idx (050_search_optimization.sql:62) +-- same: ON listings (scraped_at DESC) WHERE is_active = true +-- Note: listings_scraped_desc_idx is also de-partialed in Step 2 below. +DROP INDEX IF EXISTS listings_scraped_idx; + +-- dup of listing_sources_price_divergence_idx2 (050_search_optimization.sql:84) +-- same: ON listing_sources (listing_id, price_rub) WHERE price_rub IS NOT NULL +DROP INDEX IF EXISTS listing_sources_price_divergence_idx; + +-- redundant: covered by UNIQUE (source, ext_house_id) constraint (009_houses.sql:54) +-- which automatically creates a unique index on the same column set. +-- The plain btree here (009_houses.sql:59) is a strict duplicate of that unique index. +DROP INDEX IF EXISTS houses_source_ext_idx; + +-- ============================================================================= +-- Step 2: De-partial is_active=true indexes +-- Since is_active is 100% true, the WHERE predicate saves nothing. +-- A full index strictly covers the partial — no plan regression. +-- ============================================================================= + +-- listings_geom_active_idx (050_search_optimization.sql:47) +-- Original: ON listings USING GIST (geom) WHERE is_active = true +DROP INDEX IF EXISTS listings_geom_active_idx; +CREATE INDEX IF NOT EXISTS listings_geom_active_idx + ON listings USING GIST (geom); + +-- listings_active_filter_idx (050_search_optimization.sql:52) +-- Original: ON listings (rooms, price_rub, area_m2, scraped_at DESC) WHERE is_active = true +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); + +-- listings_house_price_idx (050_search_optimization.sql:57) +-- Original: ON listings (house_id_fk, price_rub) WHERE is_active = true +DROP INDEX IF EXISTS listings_house_price_idx; +CREATE INDEX IF NOT EXISTS listings_house_price_idx + ON listings (house_id_fk, price_rub); + +-- listings_scraped_desc_idx (050_search_optimization.sql:62) +-- Original: ON listings (scraped_at DESC) WHERE is_active = true +-- Note: listings_scraped_idx (exact duplicate) already dropped in Step 1 above. +DROP INDEX IF EXISTS listings_scraped_desc_idx; +CREATE INDEX IF NOT EXISTS listings_scraped_desc_idx + ON listings (scraped_at DESC); + +-- listings_rooms_area_idx (002_core_tables.sql:58) +-- Original: ON listings (rooms, area_m2) WHERE is_active = true +DROP INDEX IF EXISTS listings_rooms_area_idx; +CREATE INDEX IF NOT EXISTS listings_rooms_area_idx + ON listings (rooms, area_m2); + +COMMIT; -- 2.45.3