"""入隧/使用命令的有限整事务锁冲突重试,不重放结果未知的连接错误。""" import asyncio import secrets from collections.abc import Awaitable, Callable from functools import wraps from typing import ParamSpec, TypeVar from sqlalchemy.exc import OperationalError P = ParamSpec('P') R = TypeVar('R') def retry_mysql_lock_conflicts(operation: Callable[P, Awaitable[R]]) -> Callable[P, Awaitable[R]]: """仅用于异常出口已 rollback 的完整业务命令;所有重试沿用原payload/key。""" max_attempts = 3 @wraps(operation) async def wrapped(*args: P.args, **kwargs: P.kwargs) -> R: for attempt in range(max_attempts): try: return await operation(*args, **kwargs) except OperationalError as exc: # noqa: PERF203 -- 有限整事务重试必须逐次捕获锁冲突 errno = exc.orig.args[0] if getattr(exc.orig, 'args', ()) else None # 3572=NOWAIT冲突,1213=死锁,1205=锁等待超时;不重试断连/未知提交结果。 if errno not in {3572, 1213, 1205} or attempt == max_attempts - 1: raise await asyncio.sleep(0.05 * (attempt + 1) + secrets.randbelow(20) / 1000) raise AssertionError('锁冲突重试未返回结果') return wrapped