fix(#231): repair test_rate_limit_semaphore regression + docstring sync
Re-review feedback (PR #260 head 8191a85):
1. tests/scrapers/test_nspd_bulk_client.py:360 — test referenced bulk_mod._SEMAPHORE
which was renamed to _SEMAPHORE_LIMIT in previous commit. AttributeError на
полном pytest run. Заменено: assert bulk_mod._SEMAPHORE_LIMIT == 3 (smoke).
max_concurrent <= 3 уже валидирует throttling — capacity check был лишним.
2. app/services/scrapers/nspd_client.py docstrings (lines 483, 506) — обновлены
ссылки NSPDBulkClient._SEMAPHORE(3) → NSPDBulkClient._sem (per-instance,
capacity=3) чтобы соответствовать новой архитектуре.
This commit is contained in:
parent
8191a85102
commit
570c987e78
2 changed files with 8 additions and 7 deletions
|
|
@ -480,7 +480,7 @@ class NSPDClient:
|
|||
asyncio.run(). Предназначен для вызова из синхронного кода (Celery
|
||||
task, FastAPI sync handler).
|
||||
|
||||
Concurrency: NSPDBulkClient._SEMAPHORE(3) ограничивает параллельные
|
||||
Concurrency: NSPDBulkClient._sem (per-instance, capacity=3) ограничивает параллельные
|
||||
запросы. При grid_n=7 (49 ячеек) — все 49 ячеек запускаются одним
|
||||
gather; семафор пропускает не более 3 одновременно. Thread-safety:
|
||||
каждый вызов get_features_in_bbox_grid создаёт новый event loop
|
||||
|
|
@ -503,7 +503,7 @@ class NSPDClient:
|
|||
|
||||
Note:
|
||||
Не делает live HTTP вызовы если вызван с mock NSPDBulkClient.
|
||||
Rate-limit управляется семафором NSPDBulkClient._SEMAPHORE(3) +
|
||||
Rate-limit управляется семафором NSPDBulkClient._sem (per-instance, capacity=3) +
|
||||
asyncio.sleep(0.05) jitter — не через self.rate_ms.
|
||||
"""
|
||||
# Импортируем здесь чтобы избежать circular import:
|
||||
|
|
|
|||
|
|
@ -316,7 +316,7 @@ async def test_list_objects_in_building_parses(
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_rate_limit_semaphore_max_3_concurrent() -> None:
|
||||
"""Не более 3 одновременных запросов через _SEMAPHORE.
|
||||
"""Не более 3 одновременных запросов через per-instance self._sem (PR #260).
|
||||
|
||||
Мокируем httpx.AsyncClient.get (нижний уровень) с задержкой, чтобы
|
||||
реальный семафор работал. Считаем max in-flight внутри семафора.
|
||||
|
|
@ -332,7 +332,7 @@ async def test_rate_limit_semaphore_max_3_concurrent() -> None:
|
|||
|
||||
async def slow_get(*args: Any, **kwargs: Any) -> httpx.Response:
|
||||
nonlocal max_concurrent, current, call_count
|
||||
# Фиксируем вход — уже внутри семафора (httpx.get вызывается после async with _SEMAPHORE)
|
||||
# Фиксируем вход — уже внутри семафора (httpx.get вызывается после async with self._sem)
|
||||
async with lock:
|
||||
current += 1
|
||||
call_count += 1
|
||||
|
|
@ -353,11 +353,12 @@ async def test_rate_limit_semaphore_max_3_concurrent() -> None:
|
|||
tasks = [client.search_by_quarter(f"66:41:{i:07d}") for i in range(6)]
|
||||
await asyncio.gather(*tasks)
|
||||
|
||||
# _SEMAPHORE(3) → не более 3 одновременно внутри slow_get
|
||||
# self._sem(3) → не более 3 одновременно внутри slow_get
|
||||
assert max_concurrent <= 3, f"Expected ≤3 concurrent, got {max_concurrent}"
|
||||
assert call_count == 6 # все 6 вызовов прошли
|
||||
# Убедимся что _SEMAPHORE в модуле имеет нужное значение capacity
|
||||
assert bulk_mod._SEMAPHORE._value >= 0 # семафор сброшен после всех задач
|
||||
# NB: per-instance self._sem cleanup проверяется через max_concurrent <= 3 выше.
|
||||
# Module-level _SEMAPHORE удалён в PR #260 (cross-loop binding fix); smoke на capacity:
|
||||
assert bulk_mod._SEMAPHORE_LIMIT == 3
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue