1
1
import json
2
2
import time
3
-
4
3
import requests
5
4
import backoff
6
5
import singer
7
6
from singer import metrics
8
7
9
8
LOGGER = singer .get_logger ()
10
9
11
-
12
10
class RateLimitException (Exception ):
13
11
pass
14
12
15
-
16
13
class MetricsRateLimitException (Exception ):
17
14
pass
18
15
19
-
20
16
class Client (object ):
21
17
BASE_URL = 'https://api2.frontapp.com'
22
18
23
19
def __init__ (self , config ):
24
20
self .token = 'Bearer ' + config .get ('token' )
25
21
self .session = requests .Session ()
26
-
27
22
self .calls_remaining = None
28
23
self .limit_reset = None
29
24
30
25
def url (self , path ):
31
26
return self .BASE_URL + path
32
27
28
+ # Log backoff retries
29
+ def log_backoff (details ):
30
+ LOGGER .warning (f"[Backoff] Retrying { details ['target' ].__name__ } in { details ['wait' ]} s due to { details ['exception' ]} " )
31
+
32
+ # Retry strategy: Max 5 retries or 60 seconds total
33
33
@backoff .on_exception (backoff .expo ,
34
34
RateLimitException ,
35
- max_tries = 10 ,
36
- factor = 2 )
35
+ max_tries = 5 ,
36
+ max_time = 60 ,
37
+ on_backoff = log_backoff )
37
38
def request (self , method , url , ** kwargs ):
38
39
if self .calls_remaining is not None and self .calls_remaining == 0 :
39
40
wait = self .limit_reset - int (time .monotonic ())
@@ -53,13 +54,12 @@ def request(self, method, url, **kwargs):
53
54
with metrics .http_request_timer (endpoint ) as timer :
54
55
response = requests .request (method , url , ** kwargs )
55
56
timer .tags [metrics .Tag .http_status_code ] = response .status_code
56
-
57
-
58
57
else :
59
58
response = requests .request (method , url , ** kwargs )
60
59
61
- self .calls_remaining = int (response .headers ['X-Ratelimit-Remaining' ])
62
- self .limit_reset = int (float (response .headers ['X-Ratelimit-Reset' ]))
60
+ #Handled missing headers to avoid KeyError
61
+ self .calls_remaining = int (response .headers .get ('X-Ratelimit-Remaining' , 1 ))
62
+ self .limit_reset = int (float (response .headers .get ('X-Ratelimit-Reset' , time .monotonic () + 1 )))
63
63
64
64
if response .status_code in [429 , 503 ]:
65
65
raise RateLimitException (response .text )
@@ -83,10 +83,9 @@ def create_report(self, path, data, **kwargs):
83
83
response = self .request ('post' , url , ** kwargs )
84
84
if response .json ().get ('_links' , {}).get ('self' ):
85
85
return response .json ()['_links' ]['self' ]
86
-
87
86
return {}
88
87
89
88
def list_metrics (self , path , ** kwargs ):
90
89
url = self .url (path )
91
90
response = self .request ('get' , url , ** kwargs )
92
- return response .json ().get ('_results' , [])
91
+ return response .json ().get ('_results' , [])
0 commit comments