fix(tradein/geocode): геокодировать только активные объявления (#2604) #2605
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#2605
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/tradein-geocode-queue-active-only"
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?
Проблема (замерено на проде)
geocode_missing_listings(nightly,app/tasks/geocode_missing.py) отбирал адреса поlat IS NULL+ backoff поgeocode_tried_at, но НЕ фильтровалis_active. Очередь на прод: 14294 строки, из нихis_active=false— 14074 (98.5%), активных — всего 220. Мёртвые строки — объявления чужих регионов (Новосибирск/Казань/Челябинск/Тюмень/Ижевск…), адрес вида «Новосибирская обл.,Новосибирск» без улицы и дома (13693 из 14294 без единой цифры в адресе).ORDER BY listings_count DESCставил такой мусор В НАЧАЛО очереди (у «Новосибирская обл.,Новосибирск» — 214 listings, у реального адреса — 1-2), поэтому весь Nominatim-бюджет (1 req/sec) съедался сверху и до настоящих адресов дело не доходило: 8 ночных прогонов подряд с 24 июля,checked329-592,saved0, каждый по 30-50 минут.Фикс
Добавлен
AND is_activeв SELECT очереди (app/tasks/geocode_missing.py). До: очередь 14294 строки (220 активных / 14074 мёртвых). После фикса SELECT видит только эти ~220 активных строк — мусор чужих регионов больше не конкурирует за место вLIMIT batch_size.Три решения (п.1-3 из задачи) — явно, с обоснованием
П.1 — нужен ли
is_activeв UPDATE (lat/lon)? Решение: НЕТ, оставлено без фильтра. Координаты — свойство физического адреса(address, city), а не свойство конкретного listing.is_active=falseдубликат этой же пары никогда не будет независимо отобран SELECT'ом (он навсегда исключён оттуда новым фильтром) — без unfiltered UPDATE такой дубликат остался бы сNULL lat/lonНАВСЕГДА, хотя ответ уже получен и оплачен Nominatim-вызовом активного листинга (нулевая доп. стоимость, чистый выигрыш: при реактивации листинг уже с координатами). Довод «за фильтр» (консистентность с SELECT) — чисто эстетический, не устраняет никакой ошибки данных.П.2 — та же логика для
geocode_tried_at(обе ветки:geo is Noneиexcept). Решение: тоже без фильтра, тем же обоснованием: backoff-метка привязана к тексту(address, city), а не к конкретному listing;is_active=falseдубликат и так навсегда исключён из будущих SELECT (фильтр там был бы no-op). Единственный случай где это имеет значение — реактивация листинга (is_active→ true): backoff уже стоит и корректно защищает от немедленного повтора заведомо неудачного адреса.П.3 — арифметика
addresses_total/ условие дренажа. 220 активных строк послеGROUP BY address, cityдают ≤220 уникальных пар (десятки на практике) — заведомо меньшеbatch_size=200в подавляющем большинстве прогонов.run_geocode_missing_listingsзавершится по веткеres.addresses_total < batch_sizeуже на первой итерации — это ПРАВИЛЬНОЕ поведение (очередь разгребена), не баг. Деления там нет вообще (толькоintсравнение); единственное деление в файле —rate = (idx+1)/elapsed if elapsed > 0 else 0— уже защищено guard'ом и не связано с этим изменением. Пустая очередь (addresses_total == 0) ловится отдельной веткой ВЫШЕ этой проверки.Falsification-прогон (git stash impl-файла, тесты оставлены)
test_geocode_missing_select_filters_is_activeактивен):1 failed, 27 passed.git stash pop):28 passed.Плюс 3 теста фиксируют решения по п.1/п.2 явно (
test_geocode_missing_success_update_not_filtered_by_is_active,test_geocode_missing_notfound_tried_at_update_not_filtered_by_is_active,test_geocode_missing_exception_tried_at_update_not_filtered_by_is_active) — решение задокументировано тестом, не только комментарием.Полный pytest (
tradein-mvp/backend)uv run pytest -q --deselect "tests/test_search_api.py::test_search_cache_hit"→ 2937 passed, 9 skipped, 1 deselected (0 failed). Deselect — известный pre-existing фейл (401 от RBAC-мидлвари, ordering-only, не трогали).Границы (не тронуто, по инструкции)
app/services/geocoder.py— не менялся.region_codeу чужих строк / скрапперы — отдельные пункты issue #2604 (2 и 3), не эта задача.Refs #2604