chore(tradein/db): снести DEPRECATED-колонку listings.ceiling_height (#2799)
All checks were successful
Deploy Trade-In / changes (push) Successful in 13s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m11s
Deploy Trade-In / build-backend (push) Successful in 32s
Deploy Trade-In / deploy (push) Successful in 3m26s

This commit is contained in:
bot-backend 2026-08-09 17:51:15 +00:00
parent f3bcb1a25f
commit 687bd38322
2 changed files with 110 additions and 0 deletions

View file

@ -0,0 +1,109 @@
-- 251_listings_drop_ceiling_height.sql
-- Issue #2699 (хвост) — снос DEPRECATED-колонки listings.ceiling_height.
--
-- Dependencies: 019_listings_alter_cian.sql (завела колонку, numeric(3,2)),
-- 238_listings_ceiling_height_unify.sql (перенесла значения в
-- канон ceiling_height_m и пометила эту колонку DEPRECATED).
-- Apply after: 240_trade_in_estimates_retain_until.sql
-- Deploy order: код УЖЕ впереди схемы — писатели сняты PR #2779 (07.08) и с тех
-- пор на проде. Это тот случай, когда «код первый» правилен: DROP COLUMN
-- безопасен только после того, как ни один живой writer/reader колонки не
-- остался. Обратный порядок (снести колонку, потом деплоить код) уронил бы
-- скрейпинг.
--
-- ── ПОЧЕМУ ЭТО ОТДЕЛЬНЫЙ ФАЙЛ, А НЕ ЧАСТЬ 238 ───────────────────────────────
-- 238 намеренно оставила колонку: «сначала прод должен подтвердить, что колонку
-- никто не пишет и не читает. Снос — отдельным шагом». Подтверждение получено,
-- ниже — числа.
--
-- ── КРИТЕРИЙ «ПИСАТЕЛЕЙ НЕТ» (записан ДО, а не после) ───────────────────────
-- count(ceiling_height) обязан остаться 8552 (8554 из #2699 минус 2 мусорных
-- значения cian, обнулённых шагом 1 миграции 238).
-- 2026-08-07 (сразу после 238): 8552
-- 2026-08-09 16:54 UTC: 8552
-- 2026-08-09 16:57 UTC: 8552
-- Двое суток без единой записи. Контроль того, что замер не «мёртвый» (БД жива,
-- скрейпинг идёт, просто пишет в канон): за те же 2.5 минуты между двумя
-- замерами count(ceiling_height_m) вырос 16133 → 16147, а last_seen_at строк с
-- непустым ceiling_height обновлялся в ту же минуту, что и замер. То есть UPDATE
-- по этим строкам идут прямо сейчас и НЕ трогают сносимую колонку — это сильнее,
-- чем «два дня тишины».
--
-- ── ПОТРЕБИТЕЛИ: сверка на origin/main перед сносом ─────────────────────────
-- `git grep -n 'ceiling_height\b' origin/main -- '*.py' '*.ts' '*.tsx' '*.sql'`
-- минус вхождения ceiling_height_m: ни одного обращения к КОЛОНКЕ не осталось.
-- Что попало в выдачу и почему это не потребители:
-- - имена полей Python-датаклассов enrichment'ов (CianEnrichment.ceiling_height,
-- YandexEnrichment.ceiling_height, YandexValuation...) — атрибуты объектов,
-- не колонки;
-- - `CAST(:ceiling_height AS numeric)` в yandex/detail.py:600 — ИМЯ БИНД-
-- ПАРАМЕТРА, а присваивается он колонке ceiling_height_m (соседняя строка);
-- то же в cian/detail.py (`:ch`) и base.py (`:ceiling_height_m`);
-- - комментарии/докстринги с историей #2699 и тесты, которые как раз
-- УТВЕРЖДАЮТ отсутствие колонки в SQL писателей
-- (tests/test_ceiling_height_unify_2699.py, test_scraper_admin_apis.py);
-- - 019/238 — сами миграции, их переписывать нельзя и не нужно.
-- Фронтовых (.ts/.tsx) вхождений нет вообще.
--
-- ── ЗАВИСИМОСТИ В СХЕМЕ: проверено на проде 2026-08-09, все нули ────────────
-- pg_depend по атрибуту listings.ceiling_height ................ 0 объектов
-- вьюхи/матвьюхи с 'ceiling' в определении ..................... 0
-- индексы listings с 'ceiling' в indexdef ...................... 0
-- CHECK/constraint с 'ceiling' ................................. 0
-- функции и процедуры с 'ceiling_height' в теле ................ 0
-- тела обоих триггеров listings (price_change, set_geom) ....... не упоминают
-- pg_publication_rel по listings (column list ломает DROP) ..... 0 (публикаций в БД нет)
-- foreign tables НА listings в gendesign-БД (FDW-читатель) ..... 0
-- То есть CASCADE не нужен — и не должен появиться: в этом продукте
-- `DROP ... CASCADE` уже терял гранты FDW-пользователю (инцидент C3).
--
-- ── ПОТЕРИ ДАННЫХ НЕТ (проверено, а не предположено) ────────────────────────
-- строк, где ceiling_height IS NOT NULL AND ceiling_height_m IS NULL ..... 0
-- строк, где заполнены обе ............................................ 8552
-- из них расходятся значения .............................................. 0
-- Всё содержимое сносимой колонки присутствует в каноне до последнего знака.
-- ceiling_height_m на момент написания: 16 147 непустых (после 238 было 15 591 —
-- канон растёт, то есть живой).
--
-- ── СТОИМОСТЬ БЛОКИРОВКИ И ПОЧЕМУ SET LOCAL lock_timeout ───────────────────
-- ALTER TABLE ... DROP COLUMN берёт ACCESS EXCLUSIVE на listings. УДЕРЖАНИЕ
-- дёшево и не зависит от размера таблицы: PostgreSQL не переписывает heap, а
-- помечает атрибут attisdropped в каталоге (в listings уже 4 таких «пенька» от
-- прошлых сносов при 92 живых колонках) — единицы миллисекунд.
--
-- Дорого ОЖИДАНИЕ выдачи лока, и цена здесь выше, чем у 250: listings — 19 GB,
-- 97 540 строк, по ней постоянно идёт скрейпинг (в т.ч. длинные проходы вроде
-- avito_full_load). Ждущий ACCESS EXCLUSIVE встаёт в очередь ПЕРЕД новыми
-- запросами, поэтому за ним начнут ждать обычные SELECT/UPDATE приложения —
-- ровно то, что 2026-08-07 положило деплой на 29 минут (#2791, #2792).
-- Поэтому `SET LOCAL lock_timeout = '5s'` (снизу ограничено deadlock_timeout =
-- 1 s на проде, сверху — потолок простоя очереди приложения; на работу ПОД
-- локом не влияет). Срабатывание = честный красный деплой через 5 секунд,
-- миграция не помечается применённой, повторить в окно потише.
--
-- IDEMPOTENCY / SAFETY:
-- - DROP COLUMN IF EXISTS — безопасный re-run.
-- - Без CASCADE: зависимых объектов нет (см. выше), а CASCADE молча снёс бы
-- то, что появится позже.
-- - Одна DDL-операция внутри BEGIN/COMMIT.
-- - Откат: колонку вернуть можно (ALTER TABLE ... ADD COLUMN), но данные в неё
-- не восстановятся — они и не нужны, дубликат канона (0 расхождений).
--
-- Критерий приёмки (записан ДО применения):
-- 1. Запись в _schema_migrations по имени этого файла (а не «деплой зелёный»).
-- 2. information_schema.columns по listings: ceiling_height отсутствует,
-- ceiling_height_m на месте и count(ceiling_height_m) >= 16 147.
-- 3. Скрейпинг продолжает писать: count(ceiling_height_m) растёт после сноса.
BEGIN;
-- Ограничивает ОЖИДАНИЕ лока, не работу под ним. Обоснование значения — в шапке
-- и в .claude/rules/sql.md § lock_timeout.
SET LOCAL lock_timeout = '5s';
ALTER TABLE listings DROP COLUMN IF EXISTS ceiling_height;
COMMENT ON COLUMN listings.ceiling_height_m IS
'Высота потолков, метры. ЕДИНСТВЕННАЯ колонка этого признака (#2699): дубль listings.ceiling_height (019) снесён миграцией 251 после того, как 238 перенесла в неё значения. Пишут все источники через scraper_kit.ceiling_height.plausible_ceiling_m (гейт правдоподобия 2.0-6.0 м). Читает estimator (comp-scoring #2012).';
COMMIT;

View file

@ -243,3 +243,4 @@
234_scrape_runs_ban_kind_unknown.sql
240_trade_in_estimates_retain_until.sql
250_drop_duplicate_expires_at_index.sql
251_listings_drop_ceiling_height.sql