fix(openalex): ограничить retry на 429 и добавить экспоненциальный backoff
Было: на 429 плоское ожидание 60с и retry без счётчика — при нескольких параллельных источниках (4 воркера) каждый продлевал общий rate limit сам, получался самоподдерживающийся затык без единого успешного запроса по 20+ минут. Теперь: до 5 попыток с экспоненциальным backoff (60/120/240/480/960с), после — источник сдаётся с тем, что успел собрать, вместо вечного retry.
This commit is contained in:
@@ -93,6 +93,8 @@ class OpenAlexParser(BaseParser):
|
|||||||
cursor = "*"
|
cursor = "*"
|
||||||
per_page = min(200, limit)
|
per_page = min(200, limit)
|
||||||
total_fetched = 0
|
total_fetched = 0
|
||||||
|
rate_limit_retries = 0
|
||||||
|
MAX_RATE_LIMIT_RETRIES = 5
|
||||||
|
|
||||||
while total_fetched < limit:
|
while total_fetched < limit:
|
||||||
params: dict[str, Any] = {
|
params: dict[str, Any] = {
|
||||||
@@ -135,6 +137,7 @@ class OpenAlexParser(BaseParser):
|
|||||||
|
|
||||||
yield works
|
yield works
|
||||||
total_fetched += len(works)
|
total_fetched += len(works)
|
||||||
|
rate_limit_retries = 0 # успешный запрос — сбросить счётчик
|
||||||
|
|
||||||
# Следующий курсор
|
# Следующий курсор
|
||||||
cursor = data.get("meta", {}).get("next_cursor")
|
cursor = data.get("meta", {}).get("next_cursor")
|
||||||
@@ -147,8 +150,19 @@ class OpenAlexParser(BaseParser):
|
|||||||
except httpx.HTTPStatusError as e:
|
except httpx.HTTPStatusError as e:
|
||||||
logger.error(f"OpenAlex HTTP ошибка: {e.response.status_code}")
|
logger.error(f"OpenAlex HTTP ошибка: {e.response.status_code}")
|
||||||
if e.response.status_code == 429:
|
if e.response.status_code == 429:
|
||||||
logger.warning("Rate limit! Ожидаем 60 секунд...")
|
rate_limit_retries += 1
|
||||||
time.sleep(60)
|
if rate_limit_retries > MAX_RATE_LIMIT_RETRIES:
|
||||||
|
logger.error(
|
||||||
|
f"OpenAlex: {MAX_RATE_LIMIT_RETRIES} подряд 429 — сдаюсь, "
|
||||||
|
f"забрано {total_fetched}/{limit}"
|
||||||
|
)
|
||||||
|
break
|
||||||
|
# Экспоненциальный backoff: 60/120/240/480/960с — при нескольких
|
||||||
|
# параллельных воркерах плоское ожидание 60с не давало общему
|
||||||
|
# лимиту освободиться, каждый воркер продлевал блокировку сам.
|
||||||
|
wait = 60 * (2 ** (rate_limit_retries - 1))
|
||||||
|
logger.warning(f"Rate limit! Попытка {rate_limit_retries}/{MAX_RATE_LIMIT_RETRIES}, ждём {wait}с...")
|
||||||
|
time.sleep(wait)
|
||||||
continue
|
continue
|
||||||
break
|
break
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
Reference in New Issue
Block a user