[tradein] cian-первичка не освежается: 9205 заморожены с 31.05, 93% без house_id_fk, фантомы не гаснут (Савкова 29) #1781

Open
opened 2026-06-19 11:14:06 +00:00 by bot-backend · 3 comments
Collaborator

Жалоба (live): оценка Trade-In по «ул. Евгения Савкова 29» (ЕКБ, ЖК Суходольский квартал) не подтягивает все cian-объявления того же дома. Проверка вживую (cian, 2026-06-19): на циане по дому 29 (house id 10386120) 5 объявлений, у нас в БД 7 (все last_seen=2026-05-31), пересекаются только 2. БД одновременно устарела, переоценивает дом (фантомы) и не добирает 3 свежих.

Корни (подтверждены кодом + прод-запросами)

1. #938 заморозил cian-первичку с 31.05 [HIGH]. Ночной cian_full_load идёт secondary_only=True (hardcoded scrape_pipeline.py:2063) и выбрасывает listing_segment='novostroyki' на парсе (cian.py:712-715). С момента #938 (коммит 567549c, 2026-05-31 21:35 UTC) регулярный свип новостройки не пере-сохраняет. Итог: 9205 из 9925 активных cian-novostroyki города заморожены на last_seen=2026-05-31.

2. cian_city_sweep — единственный путь загрузки cian-первички — покрывает мало [HIGH]. Жив (last_run 2026-06-19 03:31, next 06-20 04:34, окно 2-5 UTC), но anchor+radius (radius_m=1500, pages_per_anchor=3) освежает ~145 новостроек/ночь на весь город. Дом 29/29А вне досягаемости якорей — освежилась только Савкова 35 (id 331185305), 29/29А нет. Зона Лёхи (cian-парсер/анти-бан/якоря).

3. house_id_fk=NULL у 9235/9925 (93%) [HIGH]. Не промах матчинга — все 9235 сматчены (резолвятся через house_sources по (ext_source, ext_id) 100%: 9235/9235). Просто зеркало FK в realtime появилось только 17.06 (коммит e63b21e, base.py:600-616), а майские строки переписались после одноразового бэкфилла, и рекуррентный бэкфилл их пропускает (NOT EXISTS). NULL FK ломает детерминированный Tier S-canonical «тот же дом» (estimator.py:3375-3423).

4. deactivate_stale_cian чистит только вторичку [MEDIUM]. params {segments:['vtorichka'],ttl_days:30} → протухшие новостройки никогда не гаснут → фантомы (5 из 7 по дому 29 уже ушли с циана) висят is_active=true вечно и кормят оценщик.

5. NULL geom — ложный след [LOW]. У всех активных novostroyki есть координаты (null_geom=0), они на карте и в радиусном пути. geocode_missing нерелевантен.

План фикса (с зонами ответственности)

  • [me-safe → PR] Идемпотентный бэкфилл house_id_fk из house_sources по точному source-id (без адресного fuzzy, 9235/9235 резолвятся) → восстанавливает каноническую группировку «тот же дом» по всему городу. Применяется на деплое.
  • [Лёха · cian-зона] Расширить охват cian_city_sweep (якоря/радиус/pages) или выделенный newbuilding-свип, чтобы дом 29/29А и заморо­женная когорта 9205 переобновлялись. Плюс обновить cian-session-cookies (протухли с 31.05) — без них свип не фетчит.
  • [Лёха · prod-exec] Запустить бэкфилл/прогон на проде (у Клода read-only psql), точечно ре-скрейпить Широкую Речку.
  • [рекомендация, сиквенс ПОСЛЕ охвата] Добавить novostroyki в deactivate_stale_cian (ttl_days≈30) — чтобы фантомы гасли, но только после расширения охвата, иначе массово деактивирует 9205.

Связано: #1774 (PR #1776) — estimator Tier A теперь принимает same-building novostroyki; этот тикет — про то, что скрейпер не держит cian-первичку свежей (бутылочное горло). #1772 (data-block), #1773 (avito de-glue PR #1775).

Диагностика: multi-agent workflow (код-пути + прод-данные + матчинг/гео + live-cian).

**Жалоба (live):** оценка Trade-In по «ул. Евгения Савкова 29» (ЕКБ, ЖК Суходольский квартал) не подтягивает все cian-объявления того же дома. Проверка вживую (cian, 2026-06-19): на циане по дому 29 (house id 10386120) **5 объявлений**, у нас в БД **7** (все `last_seen=2026-05-31`), пересекаются **только 2**. БД одновременно устарела, переоценивает дом (фантомы) и не добирает 3 свежих. ## Корни (подтверждены кодом + прод-запросами) **1. #938 заморозил cian-первичку с 31.05 [HIGH].** Ночной `cian_full_load` идёт `secondary_only=True` (hardcoded `scrape_pipeline.py:2063`) и выбрасывает `listing_segment='novostroyki'` на парсе (`cian.py:712-715`). С момента #938 (коммит 567549c, 2026-05-31 21:35 UTC) регулярный свип новостройки **не пере-сохраняет**. Итог: **9205 из 9925** активных cian-novostroyki города заморожены на `last_seen=2026-05-31`. **2. `cian_city_sweep` — единственный путь загрузки cian-первички — покрывает мало [HIGH].** Жив (last_run 2026-06-19 03:31, next 06-20 04:34, окно 2-5 UTC), но anchor+radius (radius_m=1500, pages_per_anchor=3) освежает ~145 новостроек/ночь на весь город. Дом 29/29А вне досягаемости якорей — освежилась только Савкова 35 (id 331185305), 29/29А нет. **Зона Лёхи (cian-парсер/анти-бан/якоря).** **3. `house_id_fk=NULL` у 9235/9925 (93%) [HIGH].** Не промах матчинга — все 9235 сматчены (резолвятся через `house_sources` по `(ext_source, ext_id)` 100%: 9235/9235). Просто **зеркало FK** в realtime появилось только 17.06 (коммит e63b21e, `base.py:600-616`), а майские строки переписались после одноразового бэкфилла, и рекуррентный бэкфилл их пропускает (NOT EXISTS). NULL FK ломает детерминированный Tier S-canonical «тот же дом» (`estimator.py:3375-3423`). **4. `deactivate_stale_cian` чистит только вторичку [MEDIUM].** params `{segments:['vtorichka'],ttl_days:30}` → протухшие новостройки никогда не гаснут → фантомы (5 из 7 по дому 29 уже ушли с циана) висят `is_active=true` вечно и кормят оценщик. **5. NULL geom — ложный след [LOW].** У всех активных novostroyki есть координаты (`null_geom=0`), они на карте и в радиусном пути. `geocode_missing` нерелевантен. ## План фикса (с зонами ответственности) - **[me-safe → PR]** Идемпотентный бэкфилл `house_id_fk` из `house_sources` по точному source-id (без адресного fuzzy, 9235/9235 резолвятся) → восстанавливает каноническую группировку «тот же дом» по всему городу. Применяется на деплое. - **[Лёха · cian-зона]** Расширить охват `cian_city_sweep` (якоря/радиус/pages) или выделенный newbuilding-свип, чтобы дом 29/29А и заморо­женная когорта 9205 переобновлялись. Плюс обновить cian-session-cookies (протухли с 31.05) — без них свип не фетчит. - **[Лёха · prod-exec]** Запустить бэкфилл/прогон на проде (у Клода read-only psql), точечно ре-скрейпить Широкую Речку. - **[рекомендация, сиквенс ПОСЛЕ охвата]** Добавить `novostroyki` в `deactivate_stale_cian` (ttl_days≈30) — чтобы фантомы гасли, но только после расширения охвата, иначе массово деактивирует 9205. **Связано:** #1774 (PR #1776) — estimator Tier A теперь принимает same-building novostroyki; этот тикет — про то, что скрейпер не держит cian-первичку свежей (бутылочное горло). #1772 (data-block), #1773 (avito de-glue PR #1775). _Диагностика: multi-agent workflow (код-пути + прод-данные + матчинг/гео + live-cian)._
Author
Collaborator

Verify (2026-06-23, из audit #1871): разморозка cian-первички подтверждена — newbuilding_listings MAX(source_last_scraped_at) = 2026-06-22 20:54, 491 строк освежено за 7 дней (total 9653). Корень #1/#2 (freeze + sweep) — снят, свип живой.

⚠️ НЕ закрываю автоматически: остальные корни тикета вне этой проверки и частично в зоне Лёхи (расширение охвата cian_city_sweep / cookies; phantom-deactivation novostroyki). Плюс схема таблицы изменилась с момента заведения (house_id_fk теперь не в newbuilding_listings) — стоит ревизовать актуальность пунктов #3/#4 перед закрытием. Оставляю решение о close за владельцем.

**Verify (2026-06-23, из audit #1871):** разморозка cian-первички подтверждена — `newbuilding_listings` MAX(`source_last_scraped_at`) = **2026-06-22 20:54**, 491 строк освежено за 7 дней (total 9653). Корень #1/#2 (freeze + sweep) — снят, свип живой. ⚠️ НЕ закрываю автоматически: остальные корни тикета вне этой проверки и частично в зоне Лёхи (расширение охвата `cian_city_sweep` / cookies; phantom-deactivation novostroyki). Плюс схема таблицы изменилась с момента заведения (`house_id_fk` теперь не в `newbuilding_listings`) — стоит ревизовать актуальность пунктов #3/#4 перед закрытием. Оставляю решение о close за владельцем.
Author
Collaborator

Статус 2026-06-26 (свежие прод-цифры — сильно сдвинулось с 06-19)

Root #3 (house_id_fk) — РЕШЕНО (bot-safe часть). Миграция 130_backfill_listings_house_id_fk.sql задеплоена (в _schema_migrations) + realtime-зеркало base.py:636-645 (#e63b21e). NULL-fk по cian: 9235 → 485 (3% от 15951 активных). Из этих 485 — 0 имеют house_ext_id и 0 резолвятся детерминированным джойном (house_source+house_ext_id → house_sources). Т.е. бэкфилл выбрал всё что можно; остаток 485 — строки без house-ключа, их добьёт только ре-скрейп (зона Лёхи, см. ниже). Новой миграции не нужно. NULL-fk теперь мал по всем источникам (avito 4%, cian 3%, yandex 0.7%).

🟡 Root #1 (новостройки заморожены) — ЖИВ, зона Лёхи. cian novostroyki: 9598 / 10003 активных заморожены на last_seen ≤ 31.05 (vtorichka свежая: 482/5734 frozen). Причина та же — cian_full_load идёт secondary_only=True (#938, scrape_pipeline.py:2063) + cian.py:712-715 дропает novostroyki на парсе. Это намеренное отключение (#938) — просто снять флаг рискованно (анти-бан/нагрузка). Нужно: anti-ban-aware охват novostroyki (выделенный newbuilding-listing-свип или расширение cian_city_sweep) + обновить cian-session-cookies (протухли с 31.05). Зона Лёхи (cian-парсер/анти-бан/якоря).

🟡 Root #2 (cian_city_sweep охват) — Лёха. ~145 новостроек/ночь на весь город, дом 29/29А вне якорей.

🔴 Root #4 (фантомы не гаснут) — BLOCKED на охвате. deactivate_stale_cian чистит только вторичку. Добавлять novostroyki в деактивацию СЕЙЧАС НЕЛЬЗЯ — мгновенно деактивирует 9598 замороженных (они active+stale, но многие реальны, просто не ре-скрейпятся). Делать ТОЛЬКО после расширения охвата.

Итог

Bot-safe часть закрыта (house_id_fk). Остаток — целиком cian-зона Лёхи (охват novostroyki + cookies), после чего фантом-деактивация. Помечаю needs-human/p1.

## Статус 2026-06-26 (свежие прод-цифры — сильно сдвинулось с 06-19) **✅ Root #3 (house_id_fk) — РЕШЕНО (bot-safe часть).** Миграция `130_backfill_listings_house_id_fk.sql` задеплоена (в `_schema_migrations`) + realtime-зеркало `base.py:636-645` (#e63b21e). NULL-fk по cian: **9235 → 485** (3% от 15951 активных). Из этих 485 — **0 имеют `house_ext_id`** и **0 резолвятся** детерминированным джойном (`house_source+house_ext_id → house_sources`). Т.е. бэкфилл выбрал всё что можно; остаток 485 — строки без house-ключа, их добьёт только ре-скрейп (зона Лёхи, см. ниже). Новой миграции не нужно. NULL-fk теперь мал по всем источникам (avito 4%, cian 3%, yandex 0.7%). **🟡 Root #1 (новостройки заморожены) — ЖИВ, зона Лёхи.** cian novostroyki: **9598 / 10003 активных заморожены на last_seen ≤ 31.05** (vtorichka свежая: 482/5734 frozen). Причина та же — `cian_full_load` идёт `secondary_only=True` (#938, scrape_pipeline.py:2063) + cian.py:712-715 дропает novostroyki на парсе. Это **намеренное** отключение (#938) — просто снять флаг рискованно (анти-бан/нагрузка). Нужно: anti-ban-aware охват novostroyki (выделенный newbuilding-listing-свип или расширение cian_city_sweep) + обновить cian-session-cookies (протухли с 31.05). **Зона Лёхи (cian-парсер/анти-бан/якоря).** **🟡 Root #2 (cian_city_sweep охват) — Лёха.** ~145 новостроек/ночь на весь город, дом 29/29А вне якорей. **🔴 Root #4 (фантомы не гаснут) — BLOCKED на охвате.** `deactivate_stale_cian` чистит только вторичку. Добавлять novostroyki в деактивацию СЕЙЧАС НЕЛЬЗЯ — мгновенно деактивирует 9598 замороженных (они active+stale, но многие реальны, просто не ре-скрейпятся). Делать ТОЛЬКО после расширения охвата. ## Итог Bot-safe часть закрыта (house_id_fk). Остаток — целиком cian-зона Лёхи (охват novostroyki + cookies), после чего фантом-деактивация. Помечаю needs-human/p1.
bot-backend added the
bug
needs-human
priority/p1
scope/backend
scrapers
tradein
labels 2026-06-26 15:53:59 +00:00
Author
Collaborator

[me-safe] FK-бэкфилл задеплоен (migration 136) + актуализация цифр (2026-06-27)

PR #1936 смержен + задеплоен. Важно: исходная диагностика (06-19) устарела — пересчёт на проде:

Что было на самом деле сейчас (не 9235)

  • Активных NULL-FK осталось 1884 (не 9205 — когорта рассосалась: re-scrape после 17.06 ловил mirror, deactivate_stale почистил часть).
  • Из них 0 несут house_source/house_ext_id → re-run migration 130 был бы strict no-op (130 покрывала только (house_source, house_ext_id)).
  • Реальный путь резолва для этих строк — per-listing-identity: mirror (base.py:704-750) при отсутствии house-catalog id зовёт match_or_create_house(ext_source=source, ext_id=source_id) → Tier-1 house_sources(ext_source,ext_id) conf=1.0. Migration 130 этот ключ НЕ трогала.

Что сделала migration 136 (faithful replay Tier-1, не fuzzy)

UPDATE listings SET house_id_fk = hs.house_id FROM house_sources hs
WHERE house_id_fk IS NULL AND source_id IS NOT NULL
  AND hs.ext_source=source AND hs.ext_id=source_id AND hs.house_id IS NOT NULL;

Результат на проде (verified):

  • Активных NULL-FK 1884 → 1140 (залито 744 active).
  • Всего (с inactive) залито 4608 строк; linked house_id_fk = 65406.
  • Residual resolvable via (source,source_id) = 0 — всё, что резолвилось каноническим source-id, залито.
  • listings_search_mv пере-refreshнут (CONCURRENTLY) → estimator/search MV видит новые линки сразу.
  • code-reviewer + database-expert: APPROVE (идемпотентно, collision-safe, faithful mirror-replay, короткие row-locks).

Остаётся (зона Лёхи — НЕ SQL-бэкфилл)

  1. ~1140 active NULL-FK без canonical source-id матча → нужен re-scrape/re-match (mirror проставит FK при следующем скрейпе). SQL-путь для них невозможен (нет существующего house_sources-маппинга — нужен прогон матчера: address/geo/fingerprint).
  2. Корень тикета — freshness заморо­женной cian-первички (9205 на last_seen=2026-05-31): cian_full_load secondary_only=True + охват cian_city_sweep ~145 новостроек/ночь + протухшие cian-cookies. Расширить охват/якоря + обновить cookies. После расширения охвата — добавить novostroyki в deactivate_stale_cian (фантомы), но ТОЛЬКО после, иначе массово деактивирует.

Группировочный слой (house_id_fk) починен везде, где это возможно детерминированно. Остаток — чисто скрейперный (свежесть + охват). Оставляю open под scraper-часть.

## [me-safe] FK-бэкфилл задеплоен (migration 136) + актуализация цифр (2026-06-27) PR #1936 смержен + задеплоен. **Важно: исходная диагностика (06-19) устарела** — пересчёт на проде: ### Что было на самом деле сейчас (не 9235) - Активных NULL-FK осталось **1884** (не 9205 — когорта рассосалась: re-scrape после 17.06 ловил mirror, deactivate_stale почистил часть). - Из них **0** несут `house_source/house_ext_id` → re-run migration 130 был бы **strict no-op** (130 покрывала только `(house_source, house_ext_id)`). - Реальный путь резолва для этих строк — **per-listing-identity**: mirror (`base.py:704-750`) при отсутствии house-catalog id зовёт `match_or_create_house(ext_source=source, ext_id=source_id)` → Tier-1 `house_sources(ext_source,ext_id)` conf=1.0. Migration 130 этот ключ НЕ трогала. ### Что сделала migration 136 (faithful replay Tier-1, не fuzzy) ```sql UPDATE listings SET house_id_fk = hs.house_id FROM house_sources hs WHERE house_id_fk IS NULL AND source_id IS NOT NULL AND hs.ext_source=source AND hs.ext_id=source_id AND hs.house_id IS NOT NULL; ``` **Результат на проде (verified):** - Активных NULL-FK **1884 → 1140** (залито 744 active). - Всего (с inactive) залито **4608** строк; linked house_id_fk = **65406**. - **Residual resolvable via (source,source_id) = 0** — всё, что резолвилось каноническим source-id, залито. - `listings_search_mv` пере-refreshнут (CONCURRENTLY) → estimator/search MV видит новые линки сразу. - code-reviewer + database-expert: **✅ APPROVE** (идемпотентно, collision-safe, faithful mirror-replay, короткие row-locks). ### Остаётся (зона Лёхи — НЕ SQL-бэкфилл) 1. **~1140 active NULL-FK без canonical source-id матча** → нужен re-scrape/re-match (mirror проставит FK при следующем скрейпе). SQL-путь для них невозможен (нет существующего house_sources-маппинга — нужен прогон матчера: address/geo/fingerprint). 2. **Корень тикета — freshness заморо­женной cian-первички** (9205 на `last_seen=2026-05-31`): `cian_full_load secondary_only=True` + охват `cian_city_sweep` ~145 новостроек/ночь + протухшие cian-cookies. Расширить охват/якоря + обновить cookies. После расширения охвата — добавить `novostroyki` в `deactivate_stale_cian` (фантомы), но ТОЛЬКО после, иначе массово деактивирует. Группировочный слой (house_id_fk) починен везде, где это возможно детерминированно. Остаток — чисто скрейперный (свежесть + охват). Оставляю open под scraper-часть.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
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#1781
No description provided.