prioritize interactive requests over background checks in shared Lichess-token queue
All checks were successful
CI/CD Pipeline / build-and-deploy (push) Successful in 15s
All checks were successful
CI/CD Pipeline / build-and-deploy (push) Successful in 15s
/today, /yesterday, /week were queuing behind the entire periodic-check backlog on the same rate-limited LICHESS_APP_TOKEN (observed ~2min wait on prod for a single command). Add a two-tier priority queue to SharedTokenGate: interactive on-demand requests jump ahead of background periodic checks, which still drain normally when nothing interactive is waiting. Verified locally end-to-end (isolated queue unit tests, live request with priority correctly reaching Lichess, and a real successful accuracy fetch rendered through StatsFormatter). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
0dbd0c400b
commit
d4eb61f994
6 changed files with 76 additions and 25 deletions
|
|
@ -84,11 +84,16 @@ class LichessClient:
|
|||
|
||||
headers = {'Authorization': f'Bearer {self.app_token}'} if self.app_token else {}
|
||||
|
||||
# Держим shared_token_gate на весь запрос-ответ и с паузой после —
|
||||
# LICHESS_APP_TOKEN общий с get_games_of_period, и Lichess не
|
||||
# пускает 2+ запроса подряд без ощутимой паузы между ними.
|
||||
async with self.shared_token_gate:
|
||||
# пускает 2+ запроса подряд без ощутимой паузы между ними — держим
|
||||
# shared_token_gate на весь запрос-ответ. get_user_activity вызывается
|
||||
# только из интерактивных команд (/today и т.п.), периодические проверки
|
||||
# его не используют — приоритет всегда "interactive", параметризовать не нужно.
|
||||
await self.shared_token_gate.acquire(priority="interactive")
|
||||
try:
|
||||
response = await self.client.get(url, headers=headers)
|
||||
finally:
|
||||
self.shared_token_gate.release()
|
||||
logger.info(f"🔍 Lichess API response status: {response.status_code} for {username}")
|
||||
response.raise_for_status() # Проверяем статус ответа
|
||||
|
||||
|
|
@ -113,19 +118,22 @@ class LichessClient:
|
|||
logger.error(traceback.format_exc())
|
||||
raise
|
||||
|
||||
async def get_games_of_period(self, username: str, since_ms: int, until_ms: int, rated_only: bool = True) -> Optional[List[Dict[str, Any]]]:
|
||||
async def get_games_of_period(self, username: str, since_ms: int, until_ms: int, rated_only: bool = True, priority: str = "background") -> Optional[List[Dict[str, Any]]]:
|
||||
"""
|
||||
Получает игры пользователя за определенный период.
|
||||
|
||||
|
||||
Lichess API возвращает игры в формате NDJSON (Newline Delimited JSON),
|
||||
где каждая строка содержит JSON объект с информацией об игре.
|
||||
|
||||
|
||||
Args:
|
||||
username: Имя пользователя на Lichess
|
||||
since_ms: Начало периода в миллисекундах (Unix timestamp * 1000)
|
||||
until_ms: Конец периода в миллисекундах (Unix timestamp * 1000)
|
||||
rated_only: Только рейтинговые игры (по умолчанию True)
|
||||
|
||||
priority: "high" для интерактивных команд (/today и т.п.), чтобы не
|
||||
ждать позади бэклога фоновых периодических проверок в общей
|
||||
очереди shared_token_gate; "background" (по умолчанию) для них.
|
||||
|
||||
Returns:
|
||||
Список игр в формате JSON или None при ошибке
|
||||
|
||||
|
|
@ -157,15 +165,20 @@ class LichessClient:
|
|||
if self.app_token:
|
||||
headers['Authorization'] = f'Bearer {self.app_token}'
|
||||
|
||||
logger.info(f"Запрос игр для {username} с {since_ms} по {until_ms}")
|
||||
logger.info(f"Запрос игр для {username} с {since_ms} по {until_ms} (priority={priority})")
|
||||
|
||||
# Держим shared_token_gate на весь запрос-ответ и с паузой после —
|
||||
# LICHESS_APP_TOKEN общий с get_user_activity, и Lichess не пускает
|
||||
# 2+ запроса подряд без ощутимой паузы между ними (даже строго
|
||||
# последовательные с интервалом ~100-300мс ловили 429 "Please only
|
||||
# run 1 request(s) at a time" под реальной нагрузкой на проде).
|
||||
async with self.shared_token_gate:
|
||||
# priority="interactive" (например, довесочный accuracy-fetch в /today)
|
||||
# обгоняет фоновые периодические проверки в этой же очереди.
|
||||
await self.shared_token_gate.acquire(priority=priority)
|
||||
try:
|
||||
response = await self.client.get(url, params=params, headers=headers)
|
||||
finally:
|
||||
self.shared_token_gate.release()
|
||||
response.raise_for_status() # Проверяем статус ответа
|
||||
|
||||
# Парсим NDJSON (Newline Delimited JSON)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue