fix(tradein): single-run guard на avito-city-sweep admin-endpoint
Параллельные avito-sweep'ы делят один прокси-IP → удвоенный rate → Avito бан. Incident 2026-05-31: runs #26 (08:31-08:42) и #27 (08:38-08:43) шли одновременно → оба banned (скомпрометировало delay-эксперимент). Scheduler уже защищён (has_running_run + advisory lock в _claim_run), но ручной admin-endpoint POST /scrape/avito-city-sweep guard'а не имел. - admin.py: has_running_run('avito_city_sweep') → HTTPException 409 перед create_run. - test: monkeypatch has_running_run→True → 409.
This commit is contained in:
parent
70838c7dfe
commit
d9d6caa044
2 changed files with 34 additions and 0 deletions
|
|
@ -22,6 +22,7 @@ from app.schemas.trade_in import ScheduleConfig, ScheduleConfigUpdate
|
||||||
from app.services import cian_session as cian_session_svc
|
from app.services import cian_session as cian_session_svc
|
||||||
from app.services import scrape_runs as runs_mod
|
from app.services import scrape_runs as runs_mod
|
||||||
from app.services.geocoder import geocode
|
from app.services.geocoder import geocode
|
||||||
|
from app.services.scheduler import has_running_run
|
||||||
from app.services.scrape_pipeline import (
|
from app.services.scrape_pipeline import (
|
||||||
run_avito_city_sweep,
|
run_avito_city_sweep,
|
||||||
run_cian_city_sweep,
|
run_cian_city_sweep,
|
||||||
|
|
@ -634,7 +635,22 @@ async def start_avito_city_sweep(
|
||||||
|
|
||||||
Coop cancel: POST /scrape/avito-city-sweep/{run_id}/cancel помечает run,
|
Coop cancel: POST /scrape/avito-city-sweep/{run_id}/cancel помечает run,
|
||||||
pipeline проверяет статус каждый anchor.
|
pipeline проверяет статус каждый anchor.
|
||||||
|
|
||||||
|
Single-run guard: второй sweep запустить нельзя, пока первый running —
|
||||||
|
параллельные раны делят один прокси-IP → удвоенный rate → Avito бан
|
||||||
|
(incident 2026-05-31: runs #26+#27 шли одновременно → оба banned).
|
||||||
|
Scheduler уже защищён (has_running_run + advisory lock); этот guard
|
||||||
|
закрывает ручной admin-триггер.
|
||||||
"""
|
"""
|
||||||
|
if has_running_run(db, "avito_city_sweep"):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=409,
|
||||||
|
detail=(
|
||||||
|
"avito city-sweep уже выполняется — одновременно допустим только один "
|
||||||
|
"(параллельные раны делят прокси-IP → Avito бан). Дождитесь завершения "
|
||||||
|
"или отмените текущий через POST /scrape/avito-city-sweep/{run_id}/cancel."
|
||||||
|
),
|
||||||
|
)
|
||||||
run_id = runs_mod.create_run(db, source="avito_city_sweep", params=payload.model_dump())
|
run_id = runs_mod.create_run(db, source="avito_city_sweep", params=payload.model_dump())
|
||||||
|
|
||||||
async def _sweep_task() -> None:
|
async def _sweep_task() -> None:
|
||||||
|
|
|
||||||
|
|
@ -104,3 +104,21 @@ def test_get_imv_benchmark_unavailable(app):
|
||||||
# endpoint вернёт 404 потому что fallback тоже не находит address → нужен estimate row
|
# endpoint вернёт 404 потому что fallback тоже не находит address → нужен estimate row
|
||||||
# Этот тест проверяет что эндпоинт mounted; 404 ОК
|
# Этот тест проверяет что эндпоинт mounted; 404 ОК
|
||||||
assert r.status_code in (200, 404)
|
assert r.status_code in (200, 404)
|
||||||
|
|
||||||
|
|
||||||
|
def test_avito_city_sweep_single_run_guard(app, monkeypatch):
|
||||||
|
"""#single-run: второй avito sweep при уже running → 409.
|
||||||
|
|
||||||
|
Параллельные sweep'ы делят один прокси-IP → удвоенный rate → Avito бан
|
||||||
|
(incident 2026-05-31: runs #26+#27 одновременно → оба banned).
|
||||||
|
"""
|
||||||
|
from app.api.v1 import admin as admin_module
|
||||||
|
|
||||||
|
monkeypatch.setattr(admin_module, "has_running_run", lambda db, source: True)
|
||||||
|
client = TestClient(app)
|
||||||
|
r = client.post(
|
||||||
|
"/api/v1/admin/scrape/avito-city-sweep",
|
||||||
|
json={"pages_per_anchor": 1, "request_delay_sec": 5.0},
|
||||||
|
)
|
||||||
|
assert r.status_code == 409
|
||||||
|
assert "уже выполняется" in r.json()["detail"]
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue