Росреестр не грузится 39 суток: rosreestr_dkp_import каждый день рапортует done с total_seen=0 #2998

Closed
opened 2026-08-20 17:50:30 +00:00 by lekss361 · 4 comments
Owner

Найдено при аудите скрапперов 2026-08-20. Это корневая причина симптома из #2846 («ФАКТИЧЕСКИЕ СДЕЛКИ» на экране — сделки семимесячной давности).

Находка

Таблица deals не обновлялась 39 суток. Замер на проде:

  • Всего 9 значений deal_date, все на начало квартала (2024-01-01 … 2026-01-01), после 2026-01-01 записей ноль
  • max(scraped_at) = 2026-07-12 — последняя загрузка 39 суток назад (~936 ч)
  • За 30 дней не прибыло ни одной строки

При этом задача не выключена и не падает: scrape_schedules id=97 rosreestr_dkp_import, enabled = t, отработала сегодня в 05:18 UTC со статусом done — и total_seen = 0, new_count = 0. И так каждый день: 15, 16, 17, 18, 19, 20 августа — все нули.

Это тихий отказ: планировщик зелёный, монитор зелёный, данных нет.

Почему это дороже, чем выглядит

Сделки ДКП — надёжный источник против шумных объявлений, и по директиве владельца именно он приоритетен при расхождении. Сейчас оценка опирается на предложение (половина которого фантомная, см. #2994) и на сделки, отстающие на семь с половиной месяцев.

Отдельный аспект: deal_date хранится с квартальной точностью — при выходе в Москву с потоком ~21 тыс. сделок в месяц это отдельный вопрос к модели.

Смежное: сам источник gendesign_rosreestr_deals приезжает через FDW из базы Site Finder, покрывающей только область 66 — см. #2996.

Acceptance

  • Установлена причина, по которой импорт возвращает total_seen = 0, не падая
  • Прогон с ненулевым результатом, свежие сделки в deals
  • Задача, вернувшая ноль строк N прогонов подряд, перестаёт считаться успешной — иначе следующий такой отказ снова будет невидим
  • Алерт на устаревание deals сверх согласованного порога

Scope: backend/app/tasks/, packages/scraper-kit/, scrape_schedules.

Найдено при аудите скрапперов 2026-08-20. Это **корневая причина** симптома из #2846 («ФАКТИЧЕСКИЕ СДЕЛКИ» на экране — сделки семимесячной давности). ## Находка Таблица `deals` не обновлялась **39 суток**. Замер на проде: - Всего 9 значений `deal_date`, все на начало квартала (2024-01-01 … 2026-01-01), после 2026-01-01 записей **ноль** - `max(scraped_at)` = 2026-07-12 — последняя загрузка 39 суток назад (~936 ч) - За 30 дней не прибыло ни одной строки При этом задача **не выключена и не падает**: `scrape_schedules` id=97 `rosreestr_dkp_import`, `enabled = t`, отработала сегодня в 05:18 UTC со статусом `done` — и `total_seen = 0`, `new_count = 0`. И так каждый день: 15, 16, 17, 18, 19, 20 августа — все нули. Это тихий отказ: планировщик зелёный, монитор зелёный, данных нет. ## Почему это дороже, чем выглядит Сделки ДКП — надёжный источник против шумных объявлений, и по директиве владельца именно он приоритетен при расхождении. Сейчас оценка опирается на предложение (половина которого фантомная, см. #2994) и на сделки, отстающие на семь с половиной месяцев. Отдельный аспект: `deal_date` хранится с квартальной точностью — при выходе в Москву с потоком ~21 тыс. сделок в месяц это отдельный вопрос к модели. Смежное: сам источник `gendesign_rosreestr_deals` приезжает через FDW из базы Site Finder, покрывающей только область 66 — см. #2996. ## Acceptance - [ ] Установлена причина, по которой импорт возвращает `total_seen = 0`, не падая - [ ] Прогон с ненулевым результатом, свежие сделки в `deals` - [ ] Задача, вернувшая ноль строк N прогонов подряд, перестаёт считаться успешной — иначе следующий такой отказ снова будет невидим - [ ] Алерт на устаревание `deals` сверх согласованного порога Scope: `backend/app/tasks/`, `packages/scraper-kit/`, `scrape_schedules`.
lekss361 added the
bug
data
priority/p1
scope/backend
scrapers
tradein
labels 2026-08-20 17:51:01 +00:00
Collaborator

Диагноз уточнён: импорт исправен, сторожа сработали, не услышал никто

Проверил всю цепочку на проде 21.08. Постановка верна в симптоме (deals стоит на Q1 2026, 39 суток без новых строк) и неверна в причине.

1. Импорт НЕ «возвращает total_seen=0» — он ежедневно честно перечитывает весь источник

run 4493  2026-08-21 04:03  done
  rows_fetched 96974 · rows_skipped 96974 · rows_inserted 0 · rows_updated 0 · batches_done 49

И так каждый день с 16.08 — одни и те же 96 974. total_seen — это не счётчик импорта, а поле админ-витрины RunSummary, которое для этой задачи остаётся None и рисуется нулём. Импорт работает: он вычитывает все строки FDW-источника и правильно их пропускает, потому что все уже есть.

2. Источник gendesign.rosreestr_deals — квартальные агрегаты Росреестра, и он стоит на Q1

Это не поток сделок, а квартальные агрегаты открытых данных (period_start_date = начало квартала, deal_count). На проде:

кварталы: 2024-01-01 … 2026-01-01   (9 значений, max = Q1 2026)
loaded_at: 26.04 — 4 975 398 строк, 30.04 — 1 854 603 строки   ← последняя загрузка

Q1 2026 был загружен 30.04 и с тех пор ничего. «39 суток» в постановке — это возраст scraped_at в deals, а не возраст данных; источник стоит 113 суток.

3. Q2 2026 опубликован, и сторож это ЗАМЕТИЛ 14.08

rosreestr_quarter_poll:
  17.06, 18.06, 19.06, 17.07   available=0
  14.08                        available=1  Q2 2026   ← вот оно

Проверил сам с VPS тем же URL, что строит poll:

HEAD dataset_СДЕЛКИ_r-r_01-92_y_2026_q_2.csv.zip → HTTP 200, Content-Length 12 142 318

(первые две попытки давали 502 — портал моргает, не блок по IP; с третьей стабильно 200). В папке квартала лежат все шесть датасетов, наш — СДЕЛКИ.

4. Оба сторожа сработали — и оба ушли в пустоту

По коду rosreestr_quarter_poll при available=1 только логирует и шлёт событие в Sentry: «SCOPE: detect availability + log actionable alert ONLY… When available=True, the operator runs manually». deals_freshness_monitor с 16.08 ежедневно ставит alert: 1 и logger.error. В GlitchTip лежат все семь событий:

14.08  Rosreestr: доступен новый квартал Q2 2026 — нужен ручной импорт (02_load_all_quarters.sh + impo…)
16.08  deals freshness: новый квартал просрочен на 1 дней
17.08  … на 2 дней
18.08  … на 3 дней
19.08  … на 4 дней
20.08  … на 5 дней

А в GlitchTip: правил 0, адресатов 0, отправок 0. Сторожа исправны, тревога корректна, человека в конце провода нет. Это тот же класс, что я уже фиксировал по DOM.РФ/НСПД: «тревога без адресата».

Что это меняет в acceptance

  • «Установить причину total_seen=0» — причины нет: счётчик не тот, импорт исправен. Вычёркивается.
  • «Задача с нулём N прогонов подряд перестаёт считаться успешной» — для импорта это было бы неверно: ноль вставок при полном перечитывании источника и есть правильный результат, пока источник не вырос. Гейт нужен не здесь, а у poll: available=1 без последующей загрузки N дней — вот это отказ.
  • «Алерт на устаревание» — уже есть и работает (deals_freshness_monitor), проблема в доставке. Это #2704 п.«GlitchTip без адресатов», не новая работа.
  • Остаётся настоящее: загрузить Q2 2026 и подумать, нужно ли poll'у перестать быть «только сообщить».

Что делаю сейчас

Загружаю Q2 2026 по штатному пути (02_load_all_quarters.shimport-rosreestr.sh). Ожидаемый прирост deals по ЕКБ-фильтру — порядка 12–16 тыс. строк (Q1 дал 12 264, Q4 2025 — 15 984). Отчитаюсь числами до/после.

## Диагноз уточнён: импорт исправен, сторожа сработали, не услышал никто Проверил всю цепочку на проде 21.08. Постановка верна в симптоме (`deals` стоит на Q1 2026, 39 суток без новых строк) и неверна в причине. ### 1. Импорт НЕ «возвращает total_seen=0» — он ежедневно честно перечитывает весь источник ``` run 4493 2026-08-21 04:03 done rows_fetched 96974 · rows_skipped 96974 · rows_inserted 0 · rows_updated 0 · batches_done 49 ``` И так каждый день с 16.08 — одни и те же 96 974. `total_seen` — это не счётчик импорта, а поле админ-витрины `RunSummary`, которое для этой задачи остаётся `None` и рисуется нулём. Импорт работает: он вычитывает **все** строки FDW-источника и правильно их пропускает, потому что все уже есть. ### 2. Источник `gendesign.rosreestr_deals` — квартальные агрегаты Росреестра, и он стоит на Q1 Это не поток сделок, а квартальные агрегаты открытых данных (`period_start_date` = начало квартала, `deal_count`). На проде: ``` кварталы: 2024-01-01 … 2026-01-01 (9 значений, max = Q1 2026) loaded_at: 26.04 — 4 975 398 строк, 30.04 — 1 854 603 строки ← последняя загрузка ``` Q1 2026 был загружен 30.04 и с тех пор ничего. «39 суток» в постановке — это возраст `scraped_at` в `deals`, а не возраст данных; источник стоит 113 суток. ### 3. Q2 2026 опубликован, и сторож это ЗАМЕТИЛ 14.08 ``` rosreestr_quarter_poll: 17.06, 18.06, 19.06, 17.07 available=0 14.08 available=1 Q2 2026 ← вот оно ``` Проверил сам с VPS тем же URL, что строит poll: ``` HEAD dataset_СДЕЛКИ_r-r_01-92_y_2026_q_2.csv.zip → HTTP 200, Content-Length 12 142 318 ``` (первые две попытки давали 502 — портал моргает, не блок по IP; с третьей стабильно 200). В папке квартала лежат все шесть датасетов, наш — СДЕЛКИ. ### 4. Оба сторожа сработали — и оба ушли в пустоту По коду `rosreestr_quarter_poll` при `available=1` **только логирует и шлёт событие в Sentry**: «SCOPE: detect availability + log actionable alert ONLY… When available=True, the operator runs manually». `deals_freshness_monitor` с 16.08 ежедневно ставит `alert: 1` и `logger.error`. В GlitchTip лежат все семь событий: ``` 14.08 Rosreestr: доступен новый квартал Q2 2026 — нужен ручной импорт (02_load_all_quarters.sh + impo…) 16.08 deals freshness: новый квартал просрочен на 1 дней 17.08 … на 2 дней 18.08 … на 3 дней 19.08 … на 4 дней 20.08 … на 5 дней ``` А в GlitchTip: **правил 0, адресатов 0, отправок 0**. Сторожа исправны, тревога корректна, человека в конце провода нет. Это тот же класс, что я уже фиксировал по DOM.РФ/НСПД: «тревога без адресата». ### Что это меняет в acceptance - «Установить причину total_seen=0» — причины нет: счётчик не тот, импорт исправен. Вычёркивается. - «Задача с нулём N прогонов подряд перестаёт считаться успешной» — для **импорта** это было бы неверно: ноль вставок при полном перечитывании источника и есть правильный результат, пока источник не вырос. Гейт нужен не здесь, а у poll: `available=1` без последующей загрузки N дней — вот это отказ. - «Алерт на устаревание» — **уже есть и работает** (`deals_freshness_monitor`), проблема в доставке. Это #2704 п.«GlitchTip без адресатов», не новая работа. - Остаётся настоящее: **загрузить Q2 2026** и подумать, нужно ли poll'у перестать быть «только сообщить». ### Что делаю сейчас Загружаю Q2 2026 по штатному пути (`02_load_all_quarters.sh` → `import-rosreestr.sh`). Ожидаемый прирост `deals` по ЕКБ-фильтру — порядка 12–16 тыс. строк (Q1 дал 12 264, Q4 2025 — 15 984). Отчитаюсь числами до/после.
Collaborator

Загружено. И найден настоящий корень — его в постановке не было

Q2 2026 на проде:

rosreestr_deals:  2026Q2 741 874 строк (ЕКБ-фильтр 13 654)
tradein.deals:    96 974 → 108 623  (+11 649), max(deal_date) 2026-01-01 → 2026-04-01
deals_freshness_monitor, следующий тик:  alert 0 · latest_quarter 2

Корень. Первая попытка загрузки упала:

ERROR:  no partition of relation "rosreestr_deals" found for row
DETAIL:  (period_start_date) = (2026-04-01)

rosreestr_deals партиционирована по кварталам, партиции созданы списком в 01_schema «2024 Q3 — 2026 Q1», и никакой механизм новые не создаёт. То есть даже оператор, запустив загрузчик по подсказке poll 14.08, получил бы этот же отказ. Это и объясняет, почему poll — «только сообщить».

Побочно пойман второй дефект: загрузчик под set -e молча умирал на первом квартале без CSV, не дойдя до лежащего файла (на VPS с одним Q2 — пустой вывод, rc=0).

PR с миграцией 193 (партиции до конца 2026), починкой загрузчика и сторожем горизонта партиций — открыт, ссылка выше в треде.

По acceptance: данные свежие ✔ · причина установлена ✔ (не та, что предполагалась) · «ноль N прогонов подряд = не успех» — для импорта неприменимо (ноль вставок при полном перечитывании источника и есть правильный результат), для poll — да: available=1 без последующей загрузки и есть отказ, но это уже решение о том, делать ли poll загружающим · алерт на устаревание уже есть и сработал, нет адресатов — #2704.

## Загружено. И найден настоящий корень — его в постановке не было **Q2 2026 на проде:** ``` rosreestr_deals: 2026Q2 741 874 строк (ЕКБ-фильтр 13 654) tradein.deals: 96 974 → 108 623 (+11 649), max(deal_date) 2026-01-01 → 2026-04-01 deals_freshness_monitor, следующий тик: alert 0 · latest_quarter 2 ``` **Корень.** Первая попытка загрузки упала: ``` ERROR: no partition of relation "rosreestr_deals" found for row DETAIL: (period_start_date) = (2026-04-01) ``` `rosreestr_deals` партиционирована по кварталам, партиции созданы списком в `01_schema` «2024 Q3 — 2026 Q1», и никакой механизм новые не создаёт. То есть **даже оператор, запустив загрузчик по подсказке poll 14.08, получил бы этот же отказ**. Это и объясняет, почему poll — «только сообщить». Побочно пойман второй дефект: загрузчик под `set -e` молча умирал на первом квартале без CSV, не дойдя до лежащего файла (на VPS с одним Q2 — пустой вывод, `rc=0`). PR с миграцией 193 (партиции до конца 2026), починкой загрузчика и сторожем горизонта партиций — открыт, ссылка выше в треде. **По acceptance:** данные свежие ✔ · причина установлена ✔ (не та, что предполагалась) · «ноль N прогонов подряд = не успех» — для импорта неприменимо (ноль вставок при полном перечитывании источника и есть правильный результат), для poll — да: `available=1` без последующей загрузки и есть отказ, но это уже решение о том, делать ли poll загружающим · алерт на устаревание **уже есть и сработал**, нет адресатов — #2704.
Collaborator

Долг загрузки закрыт: геокод Q2

Загрузка Q2 оставила 11 649 сделок без координат — import-rosreestr.sh их не ставит («Координаты проставит geocode-deals — отдельный шаг»), а в scrape_schedules такого шага нет. Пока geom пуст, сделка невидима для _fetch_deals (ST_DWithin) и для бэктеста.

Прогнал штатный scripts/geocode_deals_from_houses (уличные центроиды из houses, ноль внешних вызовов, идемпотентно по lat IS NULL):

dry-run:  backlog 11 654 → прогноз 7 815 (67.1 %)
прогон:   processed 11 654 · geocoded 7 610 · no_street_match 4 044 · failed 0

deal_date   сделок   с geom
2026-01-01  10 626   10 626
2026-04-01  11 649    7 610   ← было 0

Остаток 4 044 — сделки малых городов без домов в houses (Сухой Лог 89, Верхняя Салда 87, Реж 71, Богданович 69…). Для них есть второй скрипт geocode_deals_nominatim.py (внешний геокодер, бюджет 429 — #2953), его не гонял: это решение о трате лимита Nominatim.

Зафиксирую отдельно: геокод сделок не входит ни в одно расписание — каждая квартальная загрузка будет оставлять этот же долг, пока шаг не станет частью import-rosreestr.sh или отдельной задачей.

## Долг загрузки закрыт: геокод Q2 Загрузка Q2 оставила 11 649 сделок без координат — `import-rosreestr.sh` их не ставит («Координаты проставит geocode-deals — отдельный шаг»), а в `scrape_schedules` такого шага нет. Пока geom пуст, сделка невидима для `_fetch_deals` (ST_DWithin) и для бэктеста. Прогнал штатный `scripts/geocode_deals_from_houses` (уличные центроиды из `houses`, ноль внешних вызовов, идемпотентно по `lat IS NULL`): ``` dry-run: backlog 11 654 → прогноз 7 815 (67.1 %) прогон: processed 11 654 · geocoded 7 610 · no_street_match 4 044 · failed 0 deal_date сделок с geom 2026-01-01 10 626 10 626 2026-04-01 11 649 7 610 ← было 0 ``` Остаток 4 044 — сделки малых городов без домов в `houses` (Сухой Лог 89, Верхняя Салда 87, Реж 71, Богданович 69…). Для них есть второй скрипт `geocode_deals_nominatim.py` (внешний геокодер, бюджет 429 — #2953), его не гонял: это решение о трате лимита Nominatim. Зафиксирую отдельно: **геокод сделок не входит ни в одно расписание** — каждая квартальная загрузка будет оставлять этот же долг, пока шаг не станет частью `import-rosreestr.sh` или отдельной задачей.
Collaborator

Закрываю: данные свежие, корень устранён и закреплён в репо

  • Q2 2026 на проде — 741 874 строки в rosreestr_deals, +11 649 сделок в tradein.deals, 7 610 из них геокодировано; max(deal_date) 2026-01-01 → 2026-04-01; deals_freshness_monitor погас (alert 0, latest_quarter 2).
  • Корень — партиции rosreestr_deals кончались на Q1 2026, новые ничто не создавало; даже ручной загрузчик падал бы «no partition found». PR #3014 смержен: миграция 193 (Q2–Q4 2026) — на проде применена и записана в _schema_migrations через штатный деплой; загрузчик дополнен и вылечен от set -e-ловушки (молча умирал на первом квартале без CSV); сторож горизонта партиций — герметичный DB-тест в песочнице, в CI идёт.
  • Чего в постановке не было и что осталось за рамками: импорт был исправен (ежедневно перечитывал 96 974 и честно пропускал); сторожа сработали оба — poll 14.08, монитор с 16.08 — и семь событий ушли в GlitchTip с нулём адресатов. Доставка алертов — #2704. Геокод сделок не входит ни в одно расписание — каждая квартальная загрузка оставит тот же долг, пока шаг не станет частью import-rosreestr.sh; остаток 4 044 сделок малых городов — только через Nominatim (#2953, бюджет 429).
  • Горизонт: партиций хватит до конца 2026; в начале декабря сторож покраснеет на Q1 2027 — это его работа, а не сбой.
## Закрываю: данные свежие, корень устранён и закреплён в репо - **Q2 2026 на проде** — 741 874 строки в `rosreestr_deals`, +11 649 сделок в `tradein.deals`, 7 610 из них геокодировано; `max(deal_date)` 2026-01-01 → 2026-04-01; `deals_freshness_monitor` погас (`alert 0, latest_quarter 2`). - **Корень** — партиции `rosreestr_deals` кончались на Q1 2026, новые ничто не создавало; даже ручной загрузчик падал бы «no partition found». PR #3014 смержен: миграция 193 (Q2–Q4 2026) — на проде применена и записана в `_schema_migrations` через штатный деплой; загрузчик дополнен и вылечен от `set -e`-ловушки (молча умирал на первом квартале без CSV); сторож горизонта партиций — герметичный DB-тест в песочнице, в CI идёт. - **Чего в постановке не было и что осталось за рамками:** импорт был исправен (ежедневно перечитывал 96 974 и честно пропускал); сторожа сработали оба — poll 14.08, монитор с 16.08 — и семь событий ушли в GlitchTip с нулём адресатов. Доставка алертов — #2704. Геокод сделок не входит ни в одно расписание — каждая квартальная загрузка оставит тот же долг, пока шаг не станет частью `import-rosreestr.sh`; остаток 4 044 сделок малых городов — только через Nominatim (#2953, бюджет 429). - **Горизонт:** партиций хватит до конца 2026; в начале декабря сторож покраснеет на Q1 2027 — это его работа, а не сбой.
Sign in to join this conversation.
No milestone
No project
No assignees
2 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#2998
No description provided.