All checks were successful
Deploy Trade-In / changes (push) Successful in 12s
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 2m57s
Deploy Trade-In / build-backend (push) Successful in 1m34s
Deploy Trade-In / deploy (push) Successful in 1m27s
62 lines
6.3 KiB
PL/PgSQL
62 lines
6.3 KiB
PL/PgSQL
-- 217_position_in_serp_unexpressible.sql
|
||
-- listings_snapshots.position_in_serp — диагноз «механизм невыразим», шаг 1 из 2.
|
||
--
|
||
-- Dependencies: 016_listings_snapshots.sql (создала колонку).
|
||
-- Apply after: 216_dead_code_sweep.sql
|
||
-- Идемпотентно: только COMMENT ON COLUMN (CREATE OR REPLACE-семантика).
|
||
--
|
||
-- ── ЧТО НАЙДЕНО ──────────────────────────────────────────────────────────────
|
||
-- upsert_listing_snapshot принимал position_in_serp, но НИ ОДИН боевой вызывающий
|
||
-- его не передавал: три call-site'а (scraper_kit/base.py:801 save_listings,
|
||
-- providers/cian/detail.py:382, app/tasks/avito_detail_backfill.py:454) плюс
|
||
-- ingest-скрипты — везде аргумент опущен. На проде колонка пуста 0 из 395 240 строк
|
||
-- (данные с 2026-05-30 по 2026-08-06).
|
||
--
|
||
-- ── ПОЧЕМУ ЭТО НЕ «ПОДКЛЮЧИТЬ» ───────────────────────────────────────────────
|
||
-- Соблазнительный вывод — «парсер выдачи знает индекс карточки, передайте его».
|
||
-- Он неверен: позиция есть свойство пары (объявление, конкретный прогон выдачи с
|
||
-- конкретными фильтрами), а выбранная структура этого отношения не выражает.
|
||
-- PRIMARY KEY (listing_id, snapshot_date) — максимум ОДНА строка на объявление в
|
||
-- сутки; run_id здесь обычный атрибут, да ещё и под COALESCE в ON CONFLICT.
|
||
-- Следствия на живых данных:
|
||
-- * 2026-08-05: 77 прогонов и 5392 total_seen дали 4595 строк снэпшотов; за
|
||
-- 2026-08-03 в таблице 13 разных run_id на одну дату. Четыре SERP-источника
|
||
-- (yandex/cian/avito/domclick city_sweep) в одни сутки пишут по одному ключу.
|
||
-- * Внутри одного city_sweep обход идёт по десяткам гео-якорей радиусом 1500 м с
|
||
-- перекрытием, и save_listings получает `anchor_lots` — 3 страницы ОДНОГО якоря,
|
||
-- а не общий ранжированный список. Одно объявление приезжает с разным индексом
|
||
-- от разных якорей того же прогона.
|
||
-- * Старый ON CONFLICT писал COALESCE(EXCLUDED.position_in_serp, <старое>) — в
|
||
-- строке оседал бы индекс последнего писателя дня. Это не «позиция в выдаче», а
|
||
-- произвольный представитель суток; хуже NULL, потому что читался бы как факт.
|
||
-- Чтобы позицию можно было хранить честно, нужна отдельная таблица с ключом
|
||
-- (run_id, listing_id) и сохранёнными фильтрами прогона. Такой задачи сейчас нет —
|
||
-- ни один потребитель позицию не читает (0 view/matview на проде ссылаются на
|
||
-- колонку), поэтому колонка удаляется, а не переносится.
|
||
--
|
||
-- ── ПОЧЕМУ DROP НЕ ЗДЕСЬ ─────────────────────────────────────────────────────
|
||
-- Шаг 1 (этот файл + правка кода в том же PR): upsert_listing_snapshot перестаёт
|
||
-- упоминать колонку; колонка остаётся, комментарий объясняет почему.
|
||
-- Шаг 2 (отдельный PR, после того как образ с шагом 1 живёт на проде):
|
||
-- ALTER TABLE listings_snapshots DROP COLUMN IF EXISTS position_in_serp;
|
||
-- Порядок не косметический. deploy-tradein.yml применяет data/sql/*.sql ДО
|
||
-- перезапуска контейнеров («(3) Применяем SQL миграции — ДО app»), так что DROP в
|
||
-- одном деплое с правкой кода оставил бы окно в несколько минут, где ещё живой
|
||
-- СТАРЫЙ образ выполняет INSERT со списком колонок, включающим удалённую. Запись
|
||
-- снэпшотов fault-tolerant (SAVEPOINT + warning), поэтому упало бы тихо — ровно тот
|
||
-- класс потерь, который замечают через недели по дырке в истории цен.
|
||
|
||
BEGIN;
|
||
|
||
COMMENT ON COLUMN listings_snapshots.position_in_serp IS
|
||
'МЁРТВАЯ, удаляется следующей миграцией (#2674). Пуста 0/395240 на 2026-08-06: '
|
||
'писатель принимал аргумент, ни один вызывающий его не передавал. Не подключена, '
|
||
'потому что механизм невыразим в этой таблице: позиция — свойство пары '
|
||
'(объявление, конкретный прогон выдачи с конкретными фильтрами), а PK здесь '
|
||
'(listing_id, snapshot_date) — одна строка на объявление в сутки, при том что в '
|
||
'одни сутки по этому ключу пишут до 13 прогонов и 4 разных SERP-источника, а '
|
||
'внутри одного прогона объявление приходит с разным индексом от перекрывающихся '
|
||
'гео-якорей. Честное хранение требует таблицы с ключом (run_id, listing_id) и '
|
||
'сохранёнными фильтрами прогона; читателей у позиции нет ни одного.';
|
||
|
||
COMMIT;
|