o
    i?                      @  s~   d Z ddlmZ ddlZddlZddlmZmZ ddlZddl	m
Z
 eh dZeh dZG dd	 d	Zed
ddddZdS )u  重试策略 — 按 HTTP 状态码和错误类型分层决策

设计原则：
  - 4xx（客户端错误）：不重试（服务器能正常响应，是请求本身有问题）
  - 429 / 503：限流/过载，指数退避后重试
  - 5xx（非503）：服务端临时错误，有限重试
  - 网络错误（超时、连接断开）：重试
  - 404 / 410：资源不存在/已删除，永不重试
    )annotationsN)OptionalType)logger>	                     >             c                   @  sV   e Zd ZdZ				dd ddZed!ddZed"ddZd#d$ddZd%ddZ	dS )&RetryPolicyu   
    可配置的重试策略。

    Usage::
        policy = RetryPolicy(max_retries=3, base_delay=2.0)
        async with httpx.AsyncClient() as client:
            response = await policy.execute(client.get, url, headers=...)
              @      N@333333?max_retriesint
base_delayfloat	max_delayjitterc                 C  s   || _ || _|| _|| _dS )u  
        Args:
            max_retries:  最大重试次数（不含首次）
            base_delay:   初始等待秒数，指数退避基数
            max_delay:    退避上限（秒）
            jitter:       随机扰动比例（0~1），避免惊群效应
        Nr   r   r   r   )selfr   r   r   r    r!   Z/lsinfo/ai/hellotax_ai/data_center/backend/app/services/tax_data_processor/retry_policy.py__init__8   s   
zRetryPolicy.__init__status_codereturnboolc                 C  s    | t v rdS | tv rdS | dkS )u0   根据 HTTP 状态码判断是否应该重试。FTr   )_NO_RETRY_STATUS_RETRY_STATUS)r$   r!   r!   r"   should_retry_statusO   s
   zRetryPolicy.should_retry_statusexcBaseExceptionc                 C  s   t | tjtjtjtjttfS )u-   根据异常类型判断是否应该重试。)
isinstancehttpxTimeoutExceptionConnectError	ReadErrorRemoteProtocolErrorConnectionErrorTimeoutError)r*   r!   r!   r"   should_retry_errorY   s   zRetryPolicy.should_retry_errorNattemptOptional[int]c                 C  sT   |dkr	| j d n| j }t|d|d   | j}|dt| j | j 9 }t|dS )u   
        计算本次退避等待时间。

        Args:
            attempt:     当前重试次数（从 1 开始）
            status_code: HTTP 状态码（429 时使用较长初始延迟）

        Returns:
            等待秒数
        r   r         g?)r   minr   randomuniformr   max)r    r5   r$   basedelayr!   r!   r"   compute_delaye   s   
zRetryPolicy.compute_delayhttpx.Responsec           	        s  d}d}|| j krzr||i |I dH }|jdk r|W S | |js9td|j d|r0|d nd  |  || j krOtd| j  d|j  |  | |d	 |j}td
|j d|d	  d| j  d|dd	 t	|I dH  |d	7 }W nt t
jy     ty } z`| |stdt|j d|   |}|| j krtd| j  dt|j d|   | |d	 }tdt|j d|d	  d| j  d|dd	 t	|I dH  |d	7 }W Y d}~nd}~ww || j ks
|r|td)u  
        执行 HTTP 请求，按策略自动重试。

        Args:
            request_fn:  可调用对象，如 client.get / client.post
            *args/**kwargs: 透传给 request_fn

        Returns:
            httpx.Response

        Raises:
            httpx.HTTPStatusError: 达到最大重试后仍失败
            Exception: 网络错误达到最大重试后仍失败
        Nr   r   u   [retry] 不可重试状态码 u   ，终止:  u   [retry] 达到最大重试 (z	) status=r8   z[retry] status=z	 attempt=/u    等待 z.1fsu   [retry] 不可重试异常 z: z) error=z[retry] zretry policy internal error)r   r$   r)   r   warningraise_for_statuserrorr?   asynciosleepr-   HTTPStatusError	Exceptionr4   type__name__RuntimeError)	r    
request_fnargskwargslast_excr5   respr>   r*   r!   r!   r"   execute{   st   






 
5zRetryPolicy.execute)r   r   r   r   )r   r   r   r   r   r   r   r   )r$   r   r%   r&   )r*   r+   r%   r&   )N)r5   r   r$   r6   r%   r   )r%   r@   )
rL   
__module____qualname____doc__r#   staticmethodr)   r4   r?   rS   r!   r!   r!   r"   r   .   s    	r   r   r   r   r   r   )rV   
__future__r   rG   r:   typingr   r   r-   logurur   	frozensetr'   r(   r   default_retry_policyr!   r!   r!   r"   <module>   s"    
	 
