为 IO Rank 添加重试机制

This commit is contained in:
2023-11-22 15:45:40 +08:00
parent b773fb44a1
commit 7cdb0f3547
2 changed files with 43 additions and 4 deletions

View File

@@ -14,6 +14,7 @@ from sqlalchemy import select
from ...db import create_or_update_bind from ...db import create_or_update_bind
from ...utils.exception import MessageFormatError, RequestError, WhatTheFuckError from ...utils.exception import MessageFormatError, RequestError, WhatTheFuckError
from ...utils.request import splice_url from ...utils.request import splice_url
from ...utils.retry import retry
from ...utils.typing import GameType from ...utils.typing import GameType
from .. import Processor as ProcessorMeta from .. import Processor as ProcessorMeta
from .cache import Cache from .cache import Cache
@@ -153,10 +154,11 @@ class Processor(ProcessorMeta):
@scheduler.scheduled_job('cron', hour='0,6,12,18', minute=0) @scheduler.scheduled_job('cron', hour='0,6,12,18', minute=0)
@retry(exception_type=RequestError, delay=timedelta(minutes=15))
async def get_io_rank_data() -> None: async def get_io_rank_data() -> None:
league_all: LeagueAll = parse_raw_as(LeagueAll, await Cache.get(splice_url([BASE_URL, 'users/lists/league/all']))) # type: ignore[arg-type] league_all: LeagueAll = parse_raw_as(LeagueAll, await Cache.get(splice_url([BASE_URL, 'users/lists/league/all']))) # type: ignore[arg-type]
if isinstance(league_all, LeagueAllFailed): if isinstance(league_all, LeagueAllFailed):
raise RequestError(f'用户Solo数据请求错误:\n{league_all.error}') raise RequestError(f'排行榜数据请求错误:\n{league_all.error}')
def pps(user: LeagueAllUser) -> float: def pps(user: LeagueAllUser) -> float:
return user.league.pps return user.league.pps
@@ -212,8 +214,8 @@ async def get_io_rank_data() -> None:
@driver.on_startup @driver.on_startup
async def check_rank_data() -> None: async def _() -> None:
async with get_session() as session: async with get_session() as session:
latest_time = await session.scalar(select(IORank.create_time).order_by(IORank.id.desc()).limit(1)) latest_time = await session.scalar(select(IORank.create_time).order_by(IORank.id.desc()).limit(1))
if latest_time is None or datetime.now(tz=UTC) - latest_time.replace(tzinfo=UTC) > timedelta(hours=6): if latest_time is None or datetime.now(tz=UTC) - latest_time.replace(tzinfo=UTC) > timedelta(hours=6):
await get_io_rank_data() await get_io_rank_data()

View File

@@ -0,0 +1,37 @@
from asyncio import sleep
from collections.abc import Awaitable, Callable
from datetime import timedelta
from functools import wraps
from typing import TypeVar, cast
from nonebot.log import logger
T = TypeVar('T')
def retry(
max_attempts: int = 3,
exception_type: type[BaseException] | tuple[type[BaseException], ...] = Exception,
delay: timedelta | None = None,
) -> Callable[[Callable[..., Awaitable[T]]], Callable[..., Awaitable[T]]]:
def decorator(func: Callable[..., Awaitable[T]]) -> Callable[..., Awaitable[T]]:
@wraps(func)
async def wrapper(*args, **kwargs) -> T: # noqa: ANN002, ANN003
attempts = 0
while attempts < max_attempts + 1:
try:
return await func(*args, **kwargs)
except exception_type as e: # noqa: PERF203
logger.exception(e)
attempts += 1
if attempts <= max_attempts:
if delay is not None:
await sleep(delay.total_seconds())
logger.debug(f'Retrying: {func.__name__} ({attempts}/{max_attempts})')
continue
raise
raise RuntimeError('Unexpectedly reached the end of the retry loop')
return cast(Callable[..., Awaitable[T]], wrapper)
return decorator