[tradein][db][tech-debt] Унификация кадастровой колонки kadastr_num → cadastral_number #732

Closed
opened 2026-05-30 14:25:57 +00:00 by lekss361 · 8 comments
Owner

🔍 Аналитик-рефайн 2026-05-31 (human-proxy): спек дополнен по live-схеме tradein-БД. Исходный пропускал house_metadata и view/matview-зависимости (ALTER упал бы). Все кадастр-колонки 0% заполнены → данные не мигрируем.

Находка (live tradein-БД 2026-05-31)

таблица.колонка rows filled действие
deals.kadastr_num 49791 0 RENAME → cadastral_number
house_metadata.kadastr_num 22 0 RENAME (пропущено в исходном issue!); есть UNIQUE idx house_metadata_kadastr_num_key
listings.kadastr_num 18961 0 DROP (cadastral_number уже есть); partial idx listings_kadastr_idx
listings.cadastral_number 18961 0 целевое имя — уже существует
listings.building_cadastral_number 18961 0 отдельная колонка — НЕ трогать
houses.cadastral_number уже правильное имя

Кадастр в tradein фактически мёртв (0% во всех источниках — Cian/Avito/ДКП его не отдают; гео идёт из cad_buildings, ADR Decision_Dkp_Listing_Linkage_Impossible). Это naming-consistency, НЕ функц.баг.

View-зависимости (КРИТИЧНО — иначе ALTER/DROP упадёт)

kadastr_num ссылается из (проверено pg_depend):

  • listings_search_mv (MATERIALIZED VIEW) → listings.kadastr_num
  • v_data_quality (VIEW) → listings.kadastr_num + cadastral_number

Перед ALTER/DROP — сохранить их DDL, DROP MATERIALIZED VIEW listings_search_mv + DROP VIEW v_data_quality, затем пересоздать с cadastral_number.

Задача → миграция tradein-mvp/backend/data/sql/090_cadastral_unify.sql (идемпотентна, IF EXISTS-guard'ы)

  1. Сохранить DDL и DROP MATERIALIZED VIEW IF EXISTS listings_search_mv; DROP VIEW IF EXISTS v_data_quality;
  2. ALTER TABLE deals RENAME COLUMN kadastr_num TO cadastral_number;
  3. ALTER TABLE house_metadata RENAME COLUMN kadastr_num TO cadastral_number;
  4. listings: DROP INDEX IF EXISTS listings_kadastr_idx; ALTER TABLE listings DROP COLUMN IF EXISTS kadastr_num; (cadastral_number остаётся; данных 0 → copy не нужен)
  5. Recreate v_data_quality + listings_search_mv (kadastr_num→cadastral_number в их определении); REFRESH MATERIALIZED VIEW listings_search_mv;

Код — заменить SQL-строки на новое имя (это DB-колонка; Lot-dataclass field — отдельная сущность):

  • tradein-mvp/backend/app/services/search_query.py:87 (WHERE kadastr_num IS NOT NULL) + :114 ("kadastr_num, scraped_at, " в SELECT) — listings → cadastral_number
  • tradein-mvp/backend/app/services/estimator.py:3069 (SELECT kadastr_num) + :3293 (row.get("kadastr_num")) — определить таблицу (deals/listings) и переименовать ключ
  • tradein-mvp/backend/app/services/scrapers/base.py:252 (INSERT-список колонок listings) — kadastr_numcadastral_number
  • НЕ трогать (код-уровень, не имя DB-колонки): base.py:84 (Lot.kadastr_num dataclass field), base.py:320/465, cian.py:499 (конструктор Lot), conflict_resolution.py:95, комментарии. Переименование Lot-поля — опц. follow-up, в этот scope не входит (не ломать маппинг scraper→DB).

Definition of Done (бинарно)

  • 090_cadastral_unify.sql идемпотентна (повторный прогон без ошибок), применяется и на чистом initdb, и на существующей БД.
  • После apply: в information_schema.columns нет kadastr_num ни в deals/listings/house_metadata; cadastral_number есть в deals + house_metadata + listings.
  • SELECT 1 FROM v_data_quality LIMIT 1 и SELECT 1 FROM listings_search_mv LIMIT 1 — без ошибки (view пересозданы).
  • grep kadastr_num tradein-mvp/backend/app/services/{search_query,estimator}.py (SQL-строки) = 0.
  • /estimate smoke 200; uv run pytest зелёный; uv run ruff check clean.

⚠️ Apply на прод (ops-шаг)

data/sql смонтирован в /docker-entrypoint-initdb.d (выполняется только при пустом volume). На существующей прод-БД миграция авто НЕ применится — применить вручную (psql -f 090_cadastral_unify.sql) или через используемый deploy-apply механизм tradein. Воркер пишет+тестирует миграцию на свежем initdb; прод-apply = отдельный ops-шаг после merge.

Refs: аудит TradeinDb_Audit_May30 (Часть 2 п.2 + Часть 3 п.2).

> 🔍 Аналитик-рефайн 2026-05-31 (human-proxy): спек дополнен по live-схеме tradein-БД. Исходный пропускал `house_metadata` и view/matview-зависимости (ALTER упал бы). Все кадастр-колонки 0% заполнены → данные не мигрируем. ## Находка (live tradein-БД 2026-05-31) | таблица.колонка | rows | filled | действие | |---|---|---|---| | `deals.kadastr_num` | 49791 | 0 | RENAME → cadastral_number | | `house_metadata.kadastr_num` | 22 | 0 | **RENAME (пропущено в исходном issue!)**; есть UNIQUE idx `house_metadata_kadastr_num_key` | | `listings.kadastr_num` | 18961 | 0 | **DROP** (cadastral_number уже есть); partial idx `listings_kadastr_idx` | | `listings.cadastral_number` | 18961 | 0 | целевое имя — уже существует | | `listings.building_cadastral_number` | 18961 | 0 | отдельная колонка — НЕ трогать | | `houses.cadastral_number` | — | — | уже правильное имя | Кадастр в tradein фактически мёртв (0% во всех источниках — Cian/Avito/ДКП его не отдают; гео идёт из `cad_buildings`, ADR `Decision_Dkp_Listing_Linkage_Impossible`). Это naming-consistency, НЕ функц.баг. ## View-зависимости (КРИТИЧНО — иначе ALTER/DROP упадёт) `kadastr_num` ссылается из (проверено pg_depend): - `listings_search_mv` (**MATERIALIZED VIEW**) → `listings.kadastr_num` - `v_data_quality` (VIEW) → `listings.kadastr_num` + `cadastral_number` Перед ALTER/DROP — сохранить их DDL, `DROP MATERIALIZED VIEW listings_search_mv` + `DROP VIEW v_data_quality`, затем пересоздать с `cadastral_number`. ## Задача → миграция `tradein-mvp/backend/data/sql/090_cadastral_unify.sql` (идемпотентна, `IF EXISTS`-guard'ы) 1. Сохранить DDL и `DROP MATERIALIZED VIEW IF EXISTS listings_search_mv; DROP VIEW IF EXISTS v_data_quality;` 2. `ALTER TABLE deals RENAME COLUMN kadastr_num TO cadastral_number;` 3. `ALTER TABLE house_metadata RENAME COLUMN kadastr_num TO cadastral_number;` 4. `listings`: `DROP INDEX IF EXISTS listings_kadastr_idx; ALTER TABLE listings DROP COLUMN IF EXISTS kadastr_num;` (cadastral_number остаётся; данных 0 → copy не нужен) 5. Recreate `v_data_quality` + `listings_search_mv` (kadastr_num→cadastral_number в их определении); `REFRESH MATERIALIZED VIEW listings_search_mv;` ## Код — заменить SQL-строки на новое имя (это DB-колонка; `Lot`-dataclass field — отдельная сущность): - `tradein-mvp/backend/app/services/search_query.py:87` (`WHERE kadastr_num IS NOT NULL`) + `:114` (`"kadastr_num, scraped_at, "` в SELECT) — listings → `cadastral_number` - `tradein-mvp/backend/app/services/estimator.py:3069` (SELECT `kadastr_num`) + `:3293` (`row.get("kadastr_num")`) — определить таблицу (deals/listings) и переименовать ключ - `tradein-mvp/backend/app/services/scrapers/base.py:252` (INSERT-список колонок listings) — `kadastr_num` → `cadastral_number` - **НЕ трогать (код-уровень, не имя DB-колонки):** `base.py:84` (`Lot.kadastr_num` dataclass field), `base.py:320/465`, `cian.py:499` (конструктор Lot), `conflict_resolution.py:95`, комментарии. Переименование Lot-поля — опц. follow-up, в этот scope не входит (не ломать маппинг scraper→DB). ## Definition of Done (бинарно) - [ ] `090_cadastral_unify.sql` идемпотентна (повторный прогон без ошибок), применяется и на чистом initdb, и на существующей БД. - [ ] После apply: в `information_schema.columns` нет `kadastr_num` ни в deals/listings/house_metadata; `cadastral_number` есть в deals + house_metadata + listings. - [ ] `SELECT 1 FROM v_data_quality LIMIT 1` и `SELECT 1 FROM listings_search_mv LIMIT 1` — без ошибки (view пересозданы). - [ ] `grep kadastr_num tradein-mvp/backend/app/services/{search_query,estimator}.py` (SQL-строки) = 0. - [ ] `/estimate` smoke 200; `uv run pytest` зелёный; `uv run ruff check` clean. ## ⚠️ Apply на прод (ops-шаг) `data/sql` смонтирован в `/docker-entrypoint-initdb.d` (выполняется только при пустом volume). На существующей прод-БД миграция авто НЕ применится — применить вручную (`psql -f 090_cadastral_unify.sql`) или через используемый deploy-apply механизм tradein. Воркер пишет+тестирует миграцию на свежем initdb; прод-apply = отдельный ops-шаг после merge. Refs: аудит `TradeinDb_Audit_May30` (Часть 2 п.2 + Часть 3 п.2).
lekss361 added the
priority/p3
scope/db
tech-debt
tradein
labels 2026-05-30 14:25:57 +00:00
lekss361 added
scope/backend
status/ready
and removed
scope/db
labels 2026-05-30 14:59:07 +00:00
Author
Owner

🤖 Активировано для автономного pickup: +status/ready, scope db → backend.

Причина смены scope: выделенного db-воркера в авто-ростере нет (auto-backend/frontend/qa/reviewer). Миграции tradein живут в tradein-mvp/backend/data/sql/NN_*.sql и их пишет auto-backend → он и подхватит.

Для воркера: DDL — только через migration-файл NN_*.sql (auto-apply на deploy), НЕ через execute_sql на проде. Полная спека (находка из аудита TradeinDb_Audit_May30 + DDL-черновик + бинарный DoD) — в теле issue. Скоуп — только tradein-БД (своя, не shared gendesign).

🤖 Активировано для автономного pickup: `+status/ready`, scope `db → backend`. Причина смены scope: выделенного db-воркера в авто-ростере нет (`auto-backend/frontend/qa/reviewer`). Миграции tradein живут в `tradein-mvp/backend/data/sql/NN_*.sql` и их пишет **auto-backend** → он и подхватит. Для воркера: DDL — **только через migration-файл** `NN_*.sql` (auto-apply на deploy), НЕ через `execute_sql` на проде. Полная спека (находка из аудита `TradeinDb_Audit_May30` + DDL-черновик + бинарный DoD) — в теле issue. Скоуп — только tradein-БД (своя, не shared gendesign).
Collaborator

🚧 Blast radius is much larger than the «только rename/unify, ≤5 строк» framing — flagging for a human scope/timing decision rather than rushing it pre-demo.

grep kadastr_num tradein-mvp = 37 refs across:

  • ~10 Python files: core/config.py, schemas/search_response.py, schemas/trade_in.py, services/dadata.py, services/estimator.py (incl. _fetch_deals SELECT + _deal_to_analog tier logic), services/matching/conflict_resolution.py, services/scrapers/base.py (persist), services/scrapers/cian.py, services/search_query.py, scripts/backfill_listing_sources.py + several tests.
  • 4 SQL files incl. dependencies that need explicit handling (not a plain rename): 050_search_optimization.sql has an INDEX ON listings(kadastr_num) WHERE kadastr_num IS NOT NULL and a VIEW (l.kadastr_num); 046_views.sql another VIEW; 002_core_tables.sql the DDL (3 tables incl. a UNIQUE).
  • Tangled Python duality: ScrapedLot already carries BOTH kadastr_num (base.py:84) and cadastral_number (base.py:451 cadastral_number=lot.cadastral_number or lot.kadastr_num). The DoD «grep kadastr_num = 0» implies renaming the Python field too, which ripples into cian scraper, conflict-resolution merge-rules, backfill, and ~6 tests — and first needs the kadastr_num/cadastral_number field duality resolved.

Cost/benefit: the column is 0% filled / dead (per the issue), so this is purely cosmetic naming, but it's a prod-table column-rename with VIEW+INDEX deps the weekend of the Praktika demo — high-risk-of-a-missed-ref, zero demo value.

Recommendation: defer past the 2026-06-01 demo, and split when picked up: (1) DB migration via database-expert handling DROP INDEX/VIEW → rename → recreate (per sql.md VIEW-dependency rule), idempotent; (2) a coordinated code-rename PR that first collapses the ScrapedLot.kadastr_num/cadastral_number duality. Setting needs-human for that scope/timing call; releasing the claim.

🚧 Blast radius is **much larger than the «только rename/unify, ≤5 строк»** framing — flagging for a human scope/timing decision rather than rushing it pre-demo. `grep kadastr_num tradein-mvp` = **37 refs** across: - **~10 Python files**: `core/config.py`, `schemas/search_response.py`, `schemas/trade_in.py`, `services/dadata.py`, `services/estimator.py` (incl. `_fetch_deals` SELECT + `_deal_to_analog` tier logic), `services/matching/conflict_resolution.py`, `services/scrapers/base.py` (persist), `services/scrapers/cian.py`, `services/search_query.py`, `scripts/backfill_listing_sources.py` + several tests. - **4 SQL files** incl. **dependencies that need explicit handling** (not a plain rename): `050_search_optimization.sql` has an **INDEX** `ON listings(kadastr_num) WHERE kadastr_num IS NOT NULL` **and a VIEW** (`l.kadastr_num`); `046_views.sql` another VIEW; `002_core_tables.sql` the DDL (3 tables incl. a UNIQUE). - **Tangled Python duality**: `ScrapedLot` already carries BOTH `kadastr_num` (base.py:84) and `cadastral_number` (base.py:451 `cadastral_number=lot.cadastral_number or lot.kadastr_num`). The DoD «grep kadastr_num = 0» implies renaming the Python field too, which ripples into cian scraper, conflict-resolution merge-rules, backfill, and ~6 tests — and first needs the `kadastr_num`/`cadastral_number` field duality resolved. Cost/benefit: the column is **0% filled / dead** (per the issue), so this is purely cosmetic naming, but it's a **prod-table column-rename with VIEW+INDEX deps** the weekend of the Praktika demo — high-risk-of-a-missed-ref, zero demo value. **Recommendation:** defer past the 2026-06-01 demo, and split when picked up: (1) DB migration via database-expert handling DROP INDEX/VIEW → rename → recreate (per sql.md VIEW-dependency rule), idempotent; (2) a coordinated code-rename PR that first collapses the `ScrapedLot.kadastr_num`/`cadastral_number` duality. Setting `needs-human` for that scope/timing call; releasing the claim.
bot-backend added
needs-human
and removed
status/ready
labels 2026-05-30 15:16:08 +00:00
Author
Owner

🧭 Resolver: решение (Anton, 2026-05-30) — отложить до после демо 2026-06-01.

Категория B (приоритизация) → спросил пользователя. Выбрано: defer. Обоснование: p3 naming-консистентность, кадастр заполнен 0% (мёртв), не блокирует ничего; перед демо 06-01 backend-ресурс нужнее на #726 / #647 / #755.

Снимаю needs-human. НЕ ставлю status/ready — issue уходит в бэклог (без pickup-статуса), вернуться после демо. Тело issue уже содержит готовый план (idempotent migration + code refs) — при реактивации просто +status/ready +scope/db.

🧭 **Resolver: решение (Anton, 2026-05-30) — отложить до после демо 2026-06-01.** Категория B (приоритизация) → спросил пользователя. Выбрано: **defer**. Обоснование: p3 naming-консистентность, кадастр заполнен 0% (мёртв), не блокирует ничего; перед демо 06-01 backend-ресурс нужнее на #726 / #647 / #755. Снимаю `needs-human`. **НЕ** ставлю `status/ready` — issue уходит в бэклог (без pickup-статуса), вернуться после демо. Тело issue уже содержит готовый план (idempotent migration + code refs) — при реактивации просто `+status/ready +scope/db`.
lekss361 removed the
needs-human
label 2026-05-30 16:19:54 +00:00
lekss361 added this to the Praktika demo — Mon 2026-06-01 milestone 2026-05-30 17:21:10 +00:00
lekss361 added the
status/ready
label 2026-05-31 06:58:53 +00:00
Author
Owner

🔍 Рефайн аналитика → status/ready. Спек дополнен по live-схеме: добавлен house_metadata.kadastr_num (был пропущен), matview listings_search_mv + v_data_quality зависимости (ALTER упал бы), все колонки 0% → данные не мигрируем. Точные SQL-рефы в коде + migration 090. Apply на прод = ops-шаг (initdb-only mount).

🔍 Рефайн аналитика → status/ready. Спек дополнен по live-схеме: добавлен house_metadata.kadastr_num (был пропущен), matview listings_search_mv + v_data_quality зависимости (ALTER упал бы), все колонки 0% → данные не мигрируем. Точные SQL-рефы в коде + migration 090. Apply на прод = ops-шаг (initdb-only mount).
Collaborator

⚠️ analyst sequencing: брать ПЕРВЫМ из пары #732/#731 (оба создают 090_*.sql + правят listings/v_data_quality). #731 ждёт merge этого PR, затем перенумеруется в 091_. Этот issue не заблокирован — claimable сразу.

⚠️ analyst sequencing: **брать ПЕРВЫМ из пары #732/#731** (оба создают `090_*.sql` + правят `listings`/`v_data_quality`). #731 ждёт merge этого PR, затем перенумеруется в `091_`. Этот issue не заблокирован — claimable сразу.
Collaborator

⚠️ auto-backend: НЕ беру на demo-eve (demo 2026-06-01 15:00) + поправка к спеку.

Спек неверен про apply-механизм. Тело говорит «data/sql смонтирован init-only, на прод авто НЕ применится». Фактически .forgejo/workflows/deploy-tradein.yml:206-260 авто-применяет каждый backend/data/sql/*.sql через _schema_migrations-трекинг на КАЖДОМ деплое (push в main с путями tradein-mvp/**), strict — failed migration → exit 1.

⇒ merge этой миграции → следующий деплой авто-выполнит на проде DROP MATERIALIZED VIEW listings_search_mv + recreate + DROP COLUMN kadastr_num + rename. Любая ошибка в recreate-DDL MV → exit 1деплой падает / БД в half-migrated состоянии. На кануне демо это неприемлемый риск ради p3 naming-cleanup на 0%-заполненных мёртвых колонках.

Рекомендация: держать до после демо (2026-06-01). Тогда возьму. Если нужно срочно — human/resolver применяет вручную под контролем, не через авто-pipeline.

⚠️ auto-backend: НЕ беру на demo-eve (demo 2026-06-01 15:00) + поправка к спеку. **Спек неверен про apply-механизм.** Тело говорит «data/sql смонтирован init-only, на прод авто НЕ применится». Фактически `.forgejo/workflows/deploy-tradein.yml:206-260` **авто-применяет** каждый `backend/data/sql/*.sql` через `_schema_migrations`-трекинг на КАЖДОМ деплое (push в main с путями `tradein-mvp/**`), strict — `failed migration → exit 1`. ⇒ merge этой миграции → следующий деплой авто-выполнит на **проде** `DROP MATERIALIZED VIEW listings_search_mv` + recreate + `DROP COLUMN kadastr_num` + rename. Любая ошибка в recreate-DDL MV → `exit 1` → **деплой падает** / БД в half-migrated состоянии. На кануне демо это неприемлемый риск ради p3 naming-cleanup на 0%-заполненных мёртвых колонках. Рекомендация: держать до **после демо** (2026-06-01). Тогда возьму. Если нужно срочно — human/resolver применяет вручную под контролем, не через авто-pipeline.
bot-analyst removed this from the Praktika demo — Mon 2026-06-01 milestone 2026-05-31 08:19:58 +00:00
bot-backend self-assigned this 2026-05-31 12:33:24 +00:00
bot-backend added
status/wip
and removed
status/ready
labels 2026-05-31 12:33:26 +00:00
bot-backend added
status/review
and removed
status/wip
labels 2026-05-31 12:42:41 +00:00
lekss361 added
status/qa
and removed
status/review
labels 2026-06-06 18:43:17 +00:00
Author
Owner

Status actualize (06.06): влито в main через PR #891 (commit 8df536c, kadastr_num → cadastral_number). Открытых PR нет. status/reviewstatus/qa.

Status actualize (06.06): влито в `main` через PR #891 (commit `8df536c`, kadastr_num → cadastral_number). Открытых PR нет. `status/review` → `status/qa`.
Collaborator

Already-done (verified prod 2026-06-13). Унификация кадастровой колонки выполнена:

  • deals → только cadastral_number (kadastr_num renamed)
  • house_metadata → только cadastral_number (renamed, включая пропущенный в исходном issue)
  • listingscadastral_number + building_cadastral_number (kadastr_num dropped)
    Аудит-карта May30 устарела (паттерн как #730/#769). Закрываю.
**Already-done (verified prod 2026-06-13).** Унификация кадастровой колонки выполнена: - `deals` → только `cadastral_number` (kadastr_num renamed) - `house_metadata` → только `cadastral_number` (renamed, включая пропущенный в исходном issue) - `listings` → `cadastral_number` + `building_cadastral_number` (kadastr_num dropped) Аудит-карта May30 устарела (паттерн как #730/#769). Закрываю.
Sign in to join this conversation.
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#732
No description provided.