From 344f78b795bc2d363c3d7820ab896e372419eca4 Mon Sep 17 00:00:00 2001 From: jze9 Date: Wed, 26 Aug 2026 16:32:02 +0500 Subject: [PATCH] =?UTF-8?q?fix(openalex):=20=D0=BE=D0=B3=D1=80=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D1=87=D0=B8=D1=82=D1=8C=20retry=20=D0=BD=D0=B0=20429=20?= =?UTF-8?q?=D0=B8=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D1=82=D1=8C=20?= =?UTF-8?q?=D1=8D=D0=BA=D1=81=D0=BF=D0=BE=D0=BD=D0=B5=D0=BD=D1=86=D0=B8?= =?UTF-8?q?=D0=B0=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20backoff?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Было: на 429 плоское ожидание 60с и retry без счётчика — при нескольких параллельных источниках (4 воркера) каждый продлевал общий rate limit сам, получался самоподдерживающийся затык без единого успешного запроса по 20+ минут. Теперь: до 5 попыток с экспоненциальным backoff (60/120/240/480/960с), после — источник сдаётся с тем, что успел собрать, вместо вечного retry. --- scripts/parsers/openalex.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/scripts/parsers/openalex.py b/scripts/parsers/openalex.py index 76ba240..bd7c694 100644 --- a/scripts/parsers/openalex.py +++ b/scripts/parsers/openalex.py @@ -93,6 +93,8 @@ class OpenAlexParser(BaseParser): cursor = "*" per_page = min(200, limit) total_fetched = 0 + rate_limit_retries = 0 + MAX_RATE_LIMIT_RETRIES = 5 while total_fetched < limit: params: dict[str, Any] = { @@ -135,6 +137,7 @@ class OpenAlexParser(BaseParser): yield works total_fetched += len(works) + rate_limit_retries = 0 # успешный запрос — сбросить счётчик # Следующий курсор cursor = data.get("meta", {}).get("next_cursor") @@ -147,8 +150,19 @@ class OpenAlexParser(BaseParser): except httpx.HTTPStatusError as e: logger.error(f"OpenAlex HTTP ошибка: {e.response.status_code}") if e.response.status_code == 429: - logger.warning("Rate limit! Ожидаем 60 секунд...") - time.sleep(60) + rate_limit_retries += 1 + 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 break except Exception as e: