U
    1i                     @  s   d Z ddlmZ ddlZddlZddlZddlZddlZddlm	Z	 ddl
mZ edZG dd dZG d	d
 d
ZddddddZdS )z.Rate limiting and retry logic for Google APIs.    )annotationsN)defaultdict)	HttpErrorseo_optimizerc                   @  s2   e Zd ZdZdddddZddd	d
ddZdS )RateLimiterz'Per-site QPM rate limiter. Thread-safe.  int)max_qpmc                 C  s   || _ t | _tt| _d S )N)r	   	threadingLock_lockr   list_timestamps)selfr	    r   0/opt/web_system/seo_auto/src/api/rate_limiter.py__init__   s    
zRateLimiter.__init__defaultstrNone)sitereturnc              	     s   | j | t }|d   fdd| j| D | j|< t| j| | jk rh| j| | W 5 Q R  dS | j| d }|  }W 5 Q R X tt|d q dS )z;Block until a request slot is available for the given site.g      N@c                   s   g | ]}| kr|qS r   r   ).0tZwindow_startr   r   
<listcomp>!   s     z'RateLimiter.acquire.<locals>.<listcomp>Nr   g?)	r   time	monotonicr   lenr	   appendsleepmax)r   r   nowZoldestZ	wait_timer   r   r   acquire   s    
zRateLimiter.acquireN)r   )r   )__name__
__module____qualname____doc__r   r#   r   r   r   r   r      s   r   c                   @  sN   e Zd ZdZdddddZedddd	Zd
dddZddddZdS )QuotaTrackerz7Daily quota tracker for URL Inspection API (2,000/day).  r   )daily_limitc                 C  s   || _ t | _d| _d S Nr   )r*   r
   r   r   _count)r   r*   r   r   r   r   3   s    
zQuotaTracker.__init__)r   c              
   C  s0   | j   td| j| j W  5 Q R  S Q R X d S r+   )r   r!   r*   r,   r   r   r   r   	remaining8   s    zQuotaTracker.remainingboolc              	   C  sH   | j 8 | j| jkr"W 5 Q R  dS |  jd7  _W 5 Q R  dS Q R X dS )z<Try to acquire a quota slot. Returns False if limit reached.F   TN)r   r,   r*   r-   r   r   r   r#   =   s
    zQuotaTracker.acquirer   c              	   C  s   | j  d| _W 5 Q R X d S r+   )r   r,   r-   r   r   r   resetE   s    zQuotaTracker.resetN)r)   )	r$   r%   r&   r'   r   propertyr.   r#   r1   r   r   r   r   r(   0   s   r(            ?r   float)max_retries
base_delayc                   s    fdd}|S )z>Decorator: exponential backoff retry on 429/500/503 HttpError.c                   s   t   fdd}|S )Nc                    s   t d D ]}z| |W   S  tk
r } zf|jr@|jjnd}|dkr|k r d|  tdd }td||d j| t	
| n W 5 d }~X Y qX qd S )Nr0   r   )i  i  i     z7API error %d on attempt %d/%d for %s, retrying in %.1fs)ranger   Zrespstatusrandomuniformloggerwarningr$   r   r    )argskwargsZattempter:   delay)r7   funcr6   r   r   wrapperN   s"        z6retry_on_api_error.<locals>.decorator.<locals>.wrapper)	functoolswraps)rC   rD   r7   r6   )rC   r   	decoratorM   s    z%retry_on_api_error.<locals>.decoratorr   )r6   r7   rH   r   rG   r   retry_on_api_errorJ   s    rI   )r3   r4   )r'   
__future__r   rE   loggingr;   r
   r   collectionsr   Zgoogleapiclient.errorsr   	getLoggerr=   r   r(   rI   r   r   r   r   <module>   s   
