diff --git a/tradein-mvp/backend/app/api/v1/admin.py b/tradein-mvp/backend/app/api/v1/admin.py index 8946a303..a38c47bb 100644 --- a/tradein-mvp/backend/app/api/v1/admin.py +++ b/tradein-mvp/backend/app/api/v1/admin.py @@ -5,8 +5,8 @@ from __future__ import annotations -import asyncio import logging +import time from typing import Annotated, Literal from fastapi import APIRouter, Depends, Header, HTTPException @@ -145,8 +145,9 @@ async def geocode_missing( """Геокодинг listings ИЛИ deals у которых нет lat/lon (используя address). target=listings (по умолч.) — объявления Cian/N1; target=deals — сделки Росреестра. - Чанк-обработка: limit=100 → ~150-200с (Nominatim 1 req/sec). cron вызывает - в цикле пока `remaining` > 0. + Чанк-обработка с бюджетом по времени (~240с, заведомо меньше cron + `curl -m 320`): за вызов геокодим сколько успеваем, остаток уходит в + `remaining`, cron вызывает в цикле пока `remaining` > 0. geocode_tried_at: после КАЖДОЙ попытки (успех/провал) ставим NOW(). Failed- адреса не выбираются повторно 7 дней → cron-loop завершается, не зацикливается. @@ -175,9 +176,21 @@ async def geocode_missing( {"limit": limit}, ).mappings().all() + # Бюджет по времени: геокодинг упирается в Nominatim (1 req/sec + typo-тиры + # на провалах), 100 адресов могут не уложиться в cron-таймаут `curl -m 320`. + # Выходим из цикла заведомо раньше — remaining > 0 заставит cron продолжить. + budget_sec = 240.0 + started = time.monotonic() + geocoded = 0 skipped = 0 for row in rows: + if time.monotonic() - started > budget_sec: + logger.info( + "geocode-missing: бюджет %.0fс исчерпан, обработано %d — cron продолжит", + budget_sec, geocoded + skipped, + ) + break clean = _clean_address_for_geocode(row["address"]) result = await geocode(clean, db) if result is None: