fix(tradein/deactivate): протухшие объявления с пустым сегментом больше не якорят оценку #2908
No reviewers
Labels
No labels
Fable 5 ревью
GG-форсайт
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
feedback/max
generative
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
ИРД
вторичка
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#2908
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/tradein-deactivate-null-segment"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Проблема
Настоящая находка разбора 15.08 — та, ради которой пришлось переписать пункт 2 аудита.
Протухло 97% и 94%. И эти строки идут в оценку: оценщик отбирает аналоги с условием
listing_segment IS NULL OR listing_segment = 'vtorichka'.Ни одна джоба деактивации их не трогает.
deactivate_stale_yandexи_cianскоуплены наsegments=['vtorichka'], а= ANY(...)по определению никогда не матчит NULL — этосемантика SQL, а не баг. Строка с пустым сегментом не попадает ни под один сценарий снятия
и живёт вечно.
Для контраста: у avito и domklik просроченных ноль — там всё работает.
Почему это бьёт по цене
Слои подбора «тот же дом» и «микрорадиус ≤500 м» фильтруют только по
is_active, безфильтра свежести — в отличие от основных слоёв, где он есть. То есть цена трёхмесячной
давности напрямую якорит сегодняшнюю оценку, и именно там, где вес аналога максимален.
Что сделано
Отдельный режим деактивации с явным предикатом
listing_segment IS NULL. Ровно на пустойсегмент — новостройки не трогаются никогда: их 16 000, оценщик их не берёт, и паушальный
TTL там рискует убить живой инвентарь, который cian и yandex не обходят полностью.
TTL взят консервативный: эти строки по определению никем не переобходятся, поэтому пол
переобхода к ним неприменим.
Замер эффекта
Первый прогон снимет 734 из 768 строк (95.6% протухшего среза). Останется 34 — все свежее
60 суток, они защищены.
Пул сравнимых: cian −2.7%, yandex −10.2%, суммарно −5.7%.
Формально просадка у yandex выше порога, который я сам поставил как сигнал тревоги, поэтому
проговариваю явно: риска для качества оценки здесь нет. Все 734 строки заморожены дольше
60 суток и в основной путь подбора уже не попадали — там фильтр свежести есть. Реальный эффект
правки лежит в слоях без этого фильтра, где это не потеря аналогов, а удаление мусора.
Побочная находка — не чинил
ON CONFLICT DO UPDATE SETвscraper_kit/base.pyникогда не обновляетlisting_segment,хотя SERP-парсеры cian и yandex вычисляют его детерминированно при каждом проходе. Поэтому
строка, родившаяся с пустым сегментом, не самочинится даже при живом ежедневном пересборе —
это объясняет хвост из двух десятков записей со свежим
last_seen_atи вечным NULL.Правка транспорта скраперов выходит за рамки этого PR. Зафиксировано в коде, вынесу отдельно.
Коллизия номеров миграций
Эта ветка и #2907 одновременно заняли
264_. Ровно тот класс проблемы, который долженпредотвращать неслитый #2786 — и он случился вживую, между двумя ветками одного дня.
Миграция перенумерована в
266_, номер поправлен во всех местах: имя файла, self-referenceв шапке, манифест, имена девяти тестов и константа в тесте.
Test plan
#2907, режим пустого сегмента отсюда)
следующие сутки. Сверить
scrape_runs:deactivatedдолжно быть около 734. Еслизначительно больше — немедленно откатывать, это значит, что предикат зацепил лишнее.