tradein: успешный прогон загрузки не двигает отметку времени — здоровый источник будет выглядеть протухшим #2691

Closed
opened 2026-08-06 00:20:56 +00:00 by bot-backend · 2 comments
Collaborator

Найдено при ревью PR #2689, где загрузку данных ДОМ.РФ ставят на расписание. Поведение предсуществующее, но актуальным становится именно сейчас.

Что происходит

Загрузчик обновляет отметку времени только для изменившихся строк — вставка идёт с проверкой «значение отличается от текущего». Логика разумная: не трогать строки зря.

Но следствие такое: если ДОМ.РФ за неделю ничего не опубликовал, успешный прогон не сдвинет отметку вообще. Данные проверены, источник жив, всё в порядке — а по метке кажется, что загрузка не работает с прошлого раза.

Почему это выстрелит

В репозитории уже есть ровно такой паттерн мониторинга — как минимум два монитора свежести судят о здоровье источника по возрасту данных. Заведи мы третий по этому же принципу, он объявил бы здоровый источник протухшим, и мы бы пошли чинить работающее.

Это зеркало того, что случилось сегодня со СберИндексом (#2674): там монитор мерил наш собственный такт загрузки и выдавал это за застой данных. Здесь та же ошибка с другой стороны — метка отражает не «когда мы проверяли», а «когда источник менялся».

Что предлагается

Различать два времени: когда мы последний раз успешно сходили и когда данные последний раз менялись. Первое — признак здоровья сбора, второе — признак активности источника. Судить о работоспособности надо по первому.

Дешёвый вариант — писать время успешного прогона в счётчики прогона (там оно уже есть) и строить монитор на них, а не на метке в таблице данных. Тогда отметка в данных остаётся честной — «когда менялось», — и никого не вводит в заблуждение.

Оговорка

Это профилактика, а не инцидент: монитора на эту загрузку пока нет. Но задача заводится именно чтобы его не сделали наивно — сегодня мы уже потратили заход на разбор ровно такой ошибки.

Связано: #2689, #2674, #2670.

Найдено при ревью PR #2689, где загрузку данных ДОМ.РФ ставят на расписание. Поведение предсуществующее, но актуальным становится именно сейчас. ## Что происходит Загрузчик обновляет отметку времени **только для изменившихся строк** — вставка идёт с проверкой «значение отличается от текущего». Логика разумная: не трогать строки зря. Но следствие такое: если ДОМ.РФ за неделю ничего не опубликовал, **успешный прогон не сдвинет отметку вообще**. Данные проверены, источник жив, всё в порядке — а по метке кажется, что загрузка не работает с прошлого раза. ## Почему это выстрелит В репозитории уже есть **ровно такой паттерн мониторинга** — как минимум два монитора свежести судят о здоровье источника по возрасту данных. Заведи мы третий по этому же принципу, он объявил бы здоровый источник протухшим, и мы бы пошли чинить работающее. Это зеркало того, что случилось сегодня со СберИндексом (#2674): там монитор мерил **наш собственный такт загрузки** и выдавал это за застой данных. Здесь та же ошибка с другой стороны — метка отражает не «когда мы проверяли», а «когда источник менялся». ## Что предлагается Различать два времени: **когда мы последний раз успешно сходили** и **когда данные последний раз менялись**. Первое — признак здоровья сбора, второе — признак активности источника. Судить о работоспособности надо по первому. Дешёвый вариант — писать время успешного прогона в счётчики прогона (там оно уже есть) и строить монитор на них, а не на метке в таблице данных. Тогда отметка в данных остаётся честной — «когда менялось», — и никого не вводит в заблуждение. ## Оговорка Это профилактика, а не инцидент: монитора на эту загрузку пока нет. Но задача заводится именно чтобы его не сделали наивно — сегодня мы уже потратили заход на разбор ровно такой ошибки. Связано: #2689, #2674, #2670.
Author
Collaborator

Проверка постановки: чинить не надо, и вот почему

Разбор с тремя независимыми проверками на опровержение. Все числа — с прода 2026-08-07, посчитаны тем же выражением, что читает код.

Названный симптом на проде не воспроизвёлся

Единственный прогон domrf_kapremont_load по расписанию (3338, done) сдвинул отметку: max(loaded_at) = 2026-08-07 01:01:02, за 15 секунд до finished_at. Гейт пропустил 29 967 строк из 29 978 — но одиннадцати изменившихся хватило.

Механизм в коде реален, отказ на проде — нет. Оговорка честная: отметка выжила случайно, при трёх изменённых строках в неделю пустая неделя вполне возможна.

Третий монитор уже существует и не обманут

В задаче есть довод «заведи мы третий монитор по этому же принципу, он объявил бы здоровый источник протухшим». Такой монитор уже есть: v_data_quality.{avito,cian,yandex}_last_scrape_ago и админский listings_last_scraped читают ровно эту отметку. Показывают 02:49:03 / 06:19:43 / 13:18:33 — правду.

Причина, по которой он не обманут: писатель listings не гейтован, scraped_at = last_seen_at у 38 466 из 38 466 активных строк, отставание от прогона 0.02–0.04 часа. Ловушка захлопывается только на гейтованном писателе, а на гейтованные монитор никто и не вешал.

Главное: предложенное лекарство погасило бы единственный работающий сигнал

Задача предлагает судить о работоспособности по времени успешного прогона, а не по отметке данных. Из трёх расхождений больше суток два не артефакт, а настоящая поломка:

источник отставание что внутри прогонов
house_imv_backfill 33.00 суток saved: 0, errors: 35 из checked: 50, 8 прогонов подряд, все done
domclick_detail_backfill 17.81 суток enriched: 0, blocked: 3; плюс 4 прогона с attempted: 0 и duration_sec: 0 — тоже done

Монитор по scrape_runs.finished_at был бы зелёным все 33 и все 18 суток. Устаревшая отметка данных там — единственный честный сигнал, и предложенная правка его выключает.

Это ровно тот случай, когда «источник выглядит устаревшим» — не ложная тревога, а верная.

Что действительно сломано, но безвредно

Гейтованное залипание реально произошло у rosreestr_dkp_import25.59 суток. Причём там хуже гейта: scraped_at отсутствует в ON CONFLICT ... SET, то есть отметка insert-only и не двигается по построению.

Но читателей у deals.scraped_at нет, а deals_freshness_monitor смотрит max(deal_date). Вред нулевой и сегодня, и после починки.

Снято до вердикта — моя же ошибка

geoportal_coords_backfill сначала выглядел худшим случаем (48.40 суток). Это ошибка сопоставления: задача пишет координаты в listings и работает (matched/updated 22 в последнем прогоне), а грузит здания ekb_geoportal_ingest.py, у которого строки расписания нет вовсе. Убрал, не доводя до вывода.

Предложение

Закрывать как «постановка не подтвердилась» либо переформулировать: проблема не в том, что отметка не двигается, а в том, что scraped_at у deals insert-only — узкий дефект без читателей.

Правку «судить по времени прогона» делать не следует: она гасит два настоящих сигнала ради одного гипотетического ложного, который на проде не наблюдается.

Оставляю открытой — решение о переформулировке за владельцем.

## Проверка постановки: чинить не надо, и вот почему Разбор с тремя независимыми проверками на опровержение. Все числа — с прода 2026-08-07, посчитаны тем же выражением, что читает код. ### Названный симптом на проде не воспроизвёлся Единственный прогон `domrf_kapremont_load` по расписанию (3338, `done`) **сдвинул** отметку: `max(loaded_at)` = 2026-08-07 01:01:02, за 15 секунд до `finished_at`. Гейт пропустил 29 967 строк из 29 978 — но одиннадцати изменившихся хватило. Механизм в коде реален, отказ на проде — нет. Оговорка честная: отметка выжила **случайно**, при трёх изменённых строках в неделю пустая неделя вполне возможна. ### Третий монитор уже существует и не обманут В задаче есть довод «заведи мы третий монитор по этому же принципу, он объявил бы здоровый источник протухшим». Такой монитор **уже есть**: `v_data_quality.{avito,cian,yandex}_last_scrape_ago` и админский `listings_last_scraped` читают ровно эту отметку. Показывают 02:49:03 / 06:19:43 / 13:18:33 — правду. Причина, по которой он не обманут: писатель `listings` **не гейтован**, `scraped_at = last_seen_at` у **38 466 из 38 466** активных строк, отставание от прогона 0.02–0.04 часа. Ловушка захлопывается только на гейтованном писателе, а на гейтованные монитор никто и не вешал. ### Главное: предложенное лекарство погасило бы единственный работающий сигнал Задача предлагает судить о работоспособности по времени **успешного прогона**, а не по отметке данных. Из трёх расхождений больше суток **два не артефакт, а настоящая поломка**: | источник | отставание | что внутри прогонов | |---|---|---| | `house_imv_backfill` | **33.00 суток** | `saved: 0`, `errors: 35` из `checked: 50`, 8 прогонов подряд, **все `done`** | | `domclick_detail_backfill` | **17.81 суток** | `enriched: 0`, `blocked: 3`; плюс 4 прогона с `attempted: 0` и `duration_sec: 0` — тоже `done` | Монитор по `scrape_runs.finished_at` был бы **зелёным все 33 и все 18 суток**. Устаревшая отметка данных там — единственный честный сигнал, и предложенная правка его выключает. Это ровно тот случай, когда «источник выглядит устаревшим» — не ложная тревога, а верная. ### Что действительно сломано, но безвредно Гейтованное залипание реально произошло у `rosreestr_dkp_import` — **25.59 суток**. Причём там хуже гейта: `scraped_at` отсутствует в `ON CONFLICT ... SET`, то есть отметка **insert-only** и не двигается по построению. Но читателей у `deals.scraped_at` нет, а `deals_freshness_monitor` смотрит `max(deal_date)`. Вред нулевой и сегодня, и после починки. ### Снято до вердикта — моя же ошибка `geoportal_coords_backfill` сначала выглядел худшим случаем (48.40 суток). Это ошибка сопоставления: задача пишет координаты в `listings` и работает (`matched`/`updated` 22 в последнем прогоне), а грузит здания `ekb_geoportal_ingest.py`, у которого **строки расписания нет вовсе**. Убрал, не доводя до вывода. ### Предложение **Закрывать как «постановка не подтвердилась»** либо переформулировать: проблема не в том, что отметка не двигается, а в том, что `scraped_at` у `deals` insert-only — узкий дефект без читателей. Правку «судить по времени прогона» делать **не следует**: она гасит два настоящих сигнала ради одного гипотетического ложного, который на проде не наблюдается. Оставляю открытой — решение о переформулировке за владельцем.
Author
Collaborator

Проверил довод от 07.08 на сегодняшних данных — закрываю как «постановка не подтвердилась»

Довод пришлось пересобрать: половина его опор с 07.08 рассыпалась, и это стоит записать, потому что вывод устоял по другим причинам.

Что рассыпалось

07.08 я утверждал, что предложенное лекарство погасило бы два настоящих сигнала. Оба примера сегодня выглядят иначе:

источник 07.08 12.08
house_imv_backfill saved: 0, errors: 35 из 50, 8 прогонов подряд, все done 11.08 15:05: saved: 25, errors: 2 из 50 — работает
domclick_detail_backfill enriched: 0, blocked: 3, статус done 6 прогонов подряд banned, последний done — 05.08

Монитор по scrape_runs.finished_at сегодня НЕ был бы зелёным ни на одном из двух: первый починился, второй честно краснеет статусом. Аргумент «лекарство гасит два настоящих сигнала» в исходном виде больше не воспроизводится — держать его как основание нельзя.

Почему вывод всё равно тот же

  1. Названный симптом на проде не воспроизводится. Единственный гейтованный загрузчик — domrf_kapremont_load, и он отметку сдвинул: max(loaded_at) = 2026-08-07 01:01:02 при finished_at 01:01:17. Прогон с тех пор один, и это не залипание: расписание недельное (interval_days: 7), next_run_at = 2026-08-14 01:52. Сводка #2670 его в список отставших не берёт — по такту он в норме.
  2. Мониторов на гейтованных писателях нет. Тот «третий монитор», которым пугала задача (v_data_quality.*_last_scrape_ago, админский listings_last_scraped), висит на негейтованном писателе listings и показывает правду. Ловушка захлопывается только на гейтованном писателе, а туда монитор никто не вешал ни тогда, ни сейчас.
  3. Единственный настоящий дефект узкий и без читателей. deals.scraped_at insert-only по построению (scraped_at отсутствует в ON CONFLICT ... SET): rosreestr_dkp_import отработал done сегодня в 05:16, а max(deals.scraped_at) стоит на 2026-07-12 15:41 — месяц. Читателей у колонки нет, deals_freshness_monitor смотрит max(deal_date).

Вердикт

Постановка «успешный прогон загрузки не двигает отметку времени» на проде не подтверждена. Правка «судить о здоровье по времени прогона» остаётся нежелательной — но по более прочной причине, чем моя прошлая: время прогона и отметка данных отвечают на разные вопросы, и подмена второго первым теряет единственный сигнал, который переживает «зелёный прогон без работы». Профилактическая ценность разбора сохраняется в этой ветке комментариев — ради неё задачу держать открытой не нужно.

Если возвращать — то с новой формулировкой и своим критерием: «deals.scraped_at insert-only» — узкий дефект с нулём читателей; заводить его имеет смысл в тот момент, когда читатель появится.

Закрываю.

## Проверил довод от 07.08 на сегодняшних данных — закрываю как «постановка не подтвердилась» Довод пришлось пересобрать: **половина его опор с 07.08 рассыпалась**, и это стоит записать, потому что вывод устоял по другим причинам. ### Что рассыпалось 07.08 я утверждал, что предложенное лекарство погасило бы два настоящих сигнала. Оба примера сегодня выглядят иначе: | источник | 07.08 | 12.08 | |---|---|---| | `house_imv_backfill` | `saved: 0, errors: 35` из 50, 8 прогонов подряд, все `done` | 11.08 15:05: `saved: 25, errors: 2` из 50 — **работает** | | `domclick_detail_backfill` | `enriched: 0, blocked: 3`, статус `done` | 6 прогонов подряд **`banned`**, последний `done` — 05.08 | Монитор по `scrape_runs.finished_at` сегодня НЕ был бы зелёным ни на одном из двух: первый починился, второй честно краснеет статусом. Аргумент «лекарство гасит два настоящих сигнала» в исходном виде больше не воспроизводится — держать его как основание нельзя. ### Почему вывод всё равно тот же 1. **Названный симптом на проде не воспроизводится.** Единственный гейтованный загрузчик — `domrf_kapremont_load`, и он отметку **сдвинул**: `max(loaded_at)` = 2026-08-07 01:01:02 при `finished_at` 01:01:17. Прогон с тех пор один, и это не залипание: расписание недельное (`interval_days: 7`), `next_run_at` = 2026-08-14 01:52. Сводка #2670 его в список отставших не берёт — по такту он в норме. 2. **Мониторов на гейтованных писателях нет.** Тот «третий монитор», которым пугала задача (`v_data_quality.*_last_scrape_ago`, админский `listings_last_scraped`), висит на **не**гейтованном писателе `listings` и показывает правду. Ловушка захлопывается только на гейтованном писателе, а туда монитор никто не вешал ни тогда, ни сейчас. 3. **Единственный настоящий дефект узкий и без читателей.** `deals.scraped_at` insert-only по построению (`scraped_at` отсутствует в `ON CONFLICT ... SET`): `rosreestr_dkp_import` отработал `done` сегодня в 05:16, а `max(deals.scraped_at)` стоит на **2026-07-12 15:41** — месяц. Читателей у колонки нет, `deals_freshness_monitor` смотрит `max(deal_date)`. ### Вердикт Постановка «успешный прогон загрузки не двигает отметку времени» на проде **не подтверждена**. Правка «судить о здоровье по времени прогона» остаётся нежелательной — но по более прочной причине, чем моя прошлая: время прогона и отметка данных отвечают на разные вопросы, и подмена второго первым теряет единственный сигнал, который переживает «зелёный прогон без работы». Профилактическая ценность разбора сохраняется в этой ветке комментариев — ради неё задачу держать открытой не нужно. **Если возвращать — то с новой формулировкой и своим критерием:** «`deals.scraped_at` insert-only» — узкий дефект с нулём читателей; заводить его имеет смысл в тот момент, когда читатель появится. Закрываю.
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#2691
No description provided.