Заказчик прислал в поддержку ссылку на сохранённую оценку и увидел
«НЕДОСТАТОЧНО ДАННЫХ». Запись создана до фиксов #2823/#2825 и лежит в БД
мёртвой (median_price=0) со старым текстом объяснения, хотя новый расчёт
по тем же входным данным даёт 4 031 157 ₽. Таких строк 117 из 1071.
- GET оценки: если строка мертва (median_price <= 0/NULL) — пересчёт из
сохранённых входных данных с записью результата в ТУ ЖЕ строку, id и
ссылка не меняются. Строку с реальной ценой не трогаем никогда — это
сохранённый документ клиента.
- Защита от шторма и гонки: атомарный UPDATE с проверкой «всё ещё мертва»
и «давно не пробовали» прямо в БД (revival_attempted_at, окно 10 мин),
без TOCTOU между параллельными GET. Временная строка, которую создаёт
estimate_quality, удаляется, avito_imv_evaluations перепривязан до
удаления — иначе каскад снёс бы только что записанный кэш.
- Любая ошибка пересчёта → отдаём сохранённую строку и logger.exception,
GET не падает.
- relaxations / reliability теперь персистятся (миграция 255_*, idempotent)
— раньше при возврате к сохранённой оценке пропадал красный дисклеймер,
хотя цена построена на расширенной выборке. PDF читает их так же.
- Радиус в UI: добавлены 3000 и 5000 м (бэкенд принимал до 5000) плюс
подсказка, что «Авто» расширяет поиск, а явный радиус ограничивает —
заказчик выбрал «мах 2000», тем самым отрезав шаги каскада на 3 и 5 км.