feat(tradein/houses): журнал слияний домов — слияние стало обратимым #2740

Merged
bot-backend merged 1 commit from feat/2690-merge-audit-log into main 2026-08-06 15:41:21 +00:00

1 commit

Author SHA1 Message Date
5ac310bb80 feat(tradein/houses): журнал слияний домов — слияние стало обратимым
All checks were successful
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / changes (pull_request) Successful in 9s
CI / changes (pull_request) Successful in 9s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 3m11s
Проход house_dedup_merge удалял проигравшие дома вместе с дочерними строками,
не оставляя восстановимого следа: единственной записью «кто в кого» была строка
logger.info, а логи контейнера ротируются быстрее суток. Через сутки после
прогона нельзя было даже назвать пары, не говоря о возврате.

Это не гипотетическая проблема: расписание на проде enabled=true, dry_run=false,
такт 7 дней; шесть прогонов с 27.06 уже удалили 119 строк houses (2/39/31/9/6/32
по счётчикам scrape_runs), ближайший — 08.08.

Миграция 230: таблица house_merge_log (одна строка = один проигравший) с полным
jsonb-снимком удалённой строки, снимком победителя ДО переноса метаданных,
списком id перенесённых дочерних строк, полными снимками дочерних строк,
уничтоженных коллизией UNIQUE, и основанием слияния — проход, значение ключа,
был ли включён гео-страж и расстояние между домами. Плюс функция
house_merge_undo(batch_id) — обратная операция с построчным отчётом, включая
то, что вернуть не удалось.

Журнал пишется в ТОЙ ЖЕ транзакции, что и слияние, поэтому разрыв «слияние
прошло, запись не легла» невозможен по построению, а dry_run откатывает оба.

Правка НЕЙТРАЛЬНА к спорным решениям прохода: ключ схлопывания, правило выбора
победителя и гео-страж не тронуты. Расстояние теперь ПИШЕТСЯ и на fias-проходе,
где страж выключен, — измерение не есть проверка, но раньше не было и его.

Сторож полноты: если у houses появится FK-ребёнок, которого не знает _STEPS,
слияние падает вместо того, чтобы молча уничтожить его строки по CASCADE
(9 из 11 FK — ON DELETE CASCADE; ровно на пропущенном ребёнке сломалась
миграция 133, но там отказ был громким).

Объём: ~5.3 КБ на запись, наблюдаемый темп 20 слияний/нед → ~5.5 МБ/год,
потолок при схлопывании всех 9 571 домов ~50 МБ. Retention не нужен.

Заодно починены 4 живых теста этого модуля: они самоотключаются без Postgres,
поэтому в CI никогда не запускались и молча разошлись со схемой (houses.url
стал NOT NULL; у пары в первом тесте проигравший без geom, что канон-страж
#2187 справедливо отказывается сливать). Проверено на живом Postgres:
43 passed, из них 6 — на реальной БД.

Refs #2690
2026-08-06 20:21:18 +05:00