43
43
import urllib .parse
44
44
import warnings
45
45
from dataclasses import dataclass
46
+ from datetime import datetime
47
+ from email .utils import parsedate_to_datetime
46
48
from time import sleep
47
49
from typing import Any , Dict , List , Optional , Tuple , Union
48
50
@@ -347,6 +349,14 @@ def retry(self, func, args, kwargs, err, attempt):
347
349
sleep (delay )
348
350
349
351
352
+ class _RetryAfterSleep (object ):
353
+ def __init__ (self , retry_after_header ):
354
+ self ._retry_after_header = retry_after_header
355
+
356
+ def retry (self ):
357
+ sleep (self ._retry_after_header )
358
+
359
+
350
360
class TrinoRequest (object ):
351
361
"""
352
362
Manage the HTTP requests of a Trino query.
@@ -523,9 +533,9 @@ def max_attempts(self, value) -> None:
523
533
self ._handle_retry ,
524
534
handled_exceptions = self ._exceptions ,
525
535
conditions = (
526
- # need retry when there is no exception but the status code is 502, 503, or 504
536
+ # need retry when there is no exception but the status code is 429, 502, 503, or 504
527
537
lambda response : getattr (response , "status_code" , None )
528
- in (502 , 503 , 504 ),
538
+ in (429 , 502 , 503 , 504 ),
529
539
),
530
540
max_attempts = self ._max_attempts ,
531
541
)
@@ -887,7 +897,12 @@ def decorated(*args, **kwargs):
887
897
try :
888
898
result = func (* args , ** kwargs )
889
899
if any (guard (result ) for guard in conditions ):
890
- handle_retry .retry (func , args , kwargs , None , attempt )
900
+ if result .status_code == 429 and "Retry-After" in result .headers :
901
+ retry_after = _parse_retry_after_header (result .headers .get ("Retry-After" ))
902
+ handle_retry_sleep = _RetryAfterSleep (retry_after )
903
+ handle_retry_sleep .retry ()
904
+ else :
905
+ handle_retry .retry (func , args , kwargs , None , attempt )
891
906
continue
892
907
return result
893
908
except Exception as err :
@@ -904,3 +919,14 @@ def decorated(*args, **kwargs):
904
919
return decorated
905
920
906
921
return wrapper
922
+
923
+
924
+ def _parse_retry_after_header (retry_after ):
925
+ if isinstance (retry_after , int ):
926
+ return retry_after
927
+ elif isinstance (retry_after , str ) and retry_after .isdigit ():
928
+ return int (retry_after )
929
+ else :
930
+ retry_date = parsedate_to_datetime (retry_after )
931
+ now = datetime .utcnow ()
932
+ return (retry_date - now ).total_seconds ()
0 commit comments