fix(tradein/domclick): «система защиты от протечек» в объявлении читалась как блок QRATOR #3268
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
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#3268
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/3267-block-marker-in-listing-text"
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?
Найдено первым же сквозным прогоном свипа после #3266.
Что произошло
Прогон 5363 (30.08, уже с подзапросами): семь запросов подряд
HTTP 200, страницы офферов по 103-128 КБ, 69 лотов собрано, ошибок ноль — и на восьмом свип объявил блок QRATOR, оборвал все комнатные корзины и забанил живой узел пула на 6 часов.Ответ, на котором это случилось, был совершенно нормальным:
HTTP 200, 107 183 байта, двадцать валидных офферов. Совпал маркер«система защиты»— внутри описания квартиры:Продавец написал про защиту от протечек. Этого хватило, чтобы положить весь свип.
Причина
_extract_jsonискалDOMCLICK_BLOCK_MARKERSподстрокой по всему телу и делал это до разбора JSON — то есть и по пользовательскому тексту. Помимо «система защиты» в списке естьcaptcha,qrator,access denied: любой из них может встретиться в описании объявления, названии агентства или ссылке.Показательно, что комментарий над проверкой прямо объяснял, почему сканируется весь ответ («block-маркер может стоять за пределами head в крупных challenge-страницах»), — расширение области поиска и создало ложные срабатывания.
Правка
Порядок перевёрнут: сначала разбор JSON, маркеры — только если разбор не удался. Разобранный JSON нужной формы блок-страницей быть не может (QRATOR отдаёт HTML), поэтому валидный разбор сам по себе доказывает отсутствие блока.
Это не новое правило, а выравнивание с уже существующим: в
detail.pyмаркеры смотрят только когда__SSR_STATE__не найден, то есть исключительно на неуспешных страницах. Свип был единственным местом с обратной логикой.Заодно: мусор без маркеров теперь
ValueError, а не блок. За неразобранный ответ неизвестной природы узел банить нельзя — цена ошибки шесть часов простоя узла.Тесты
test_3267_block_marker_vs_listing_text.py, 11 проверок: каждый из семи маркеров внутри описания не считается блоком; дословный фрагмент объявления из прогона 5363; настоящая блок-страница по-прежнему блок; прежний путь с<pre>-обёрткой по-прежнему разбирается; мусор без маркеров — parse-ошибка, а не бан.Проверено мутацией: со старым порядком 8 проверок из 11 краснеют. Прогон целиком —
261 passed, 1 skipped.Что это меняет на проде
Свип уже собирает лоты (69 за неполный прогон против 0 во всех прошлых). Эта правка убирает единственную причину его обрыва, которую видно в логе 5363. Останется ли что-то ещё — покажет следующий сквозной прогон после деплоя.
_extract_json искал маркеры блока подстрокой по ВСЕМУ телу ответа — до разбора JSON, то есть и по пользовательским описаниям объявлений. Прод 30.08, прогон 5363: продавец написал в описании квартиры «🔹 Система защиты от протечек». Подстрока совпала с маркером «система защиты», ответ на 107 183 байта с двадцатью валидными офферами был объявлен блок- страницей, свип оборвал все комнатные корзины и забанил живой узел пула на 6 часов. Совпасть так же могут «captcha» и «qrator» — в тексте объявления, в имени агентства, в ссылке. Порядок перевёрнут: сначала разбор JSON, маркеры — только если разбор не удался. Разобранный JSON нужной формы блок-страницей быть не может, QRATOR отдаёт HTML, так что валидный разбор сам по себе доказывает отсутствие блока. Тот же порядок давно применён в detail.py — там маркеры смотрят только когда __SSR_STATE__ не найден; свип был единственным местом с обратной логикой. Мусор без маркеров теперь ValueError, а не блок: за неразобранный ответ неизвестной природы узел банить нельзя. Тест проверен мутацией: со старым порядком 8 проверок из 11 краснеют, включая дословный фрагмент описания из прогона 5363.Поправка по нумерации: ветка и коммит ссылаются на
#3267, а этот номер занят другим PR (Авито, warm context), который уже смержен. Тикет под эту правку — #3272, PR закрывает его.Отдельно: CI покраснел не из-за содержимого этого PR.
1 failed, 5186 passed, упавший —test_fifth_concurrent_estimate_gets_fast_429, и не ассертом, аTimeout (>120.0s) from pytest-timeout. Это гонка наsleep(0.05)в тесте потолка одновременных оценок, разобрана в #3270, чинится в #3271. После её мержа этот PR надо обновить из main, чтобы CI перегнался.