56
56
API_VERSTRING = "v1/"
57
57
58
58
class CountingBackoff (object ):
59
- def __init__ (self , maximum_retries = 10 , timeout_success_equivalent = None ):
60
- # type: (int, Optional[float]) -> None
59
+ def __init__ (self , maximum_retries = 10 , timeout_success_equivalent = None , delay_cap = 10.0 ):
60
+ # type: (int, Optional[float], float ) -> None
61
61
self .number_of_retries = 0
62
62
self .maximum_retries = maximum_retries
63
63
self .timeout_success_equivalent = timeout_success_equivalent
64
64
self .last_attempt_time = 0.0
65
+ self .delay_cap = delay_cap
65
66
66
67
def keep_going (self ):
67
68
# type: () -> bool
@@ -93,9 +94,9 @@ def fail(self):
93
94
super (RandomExponentialBackoff , self ).fail ()
94
95
# Exponential growth with ratio sqrt(2); compute random delay
95
96
# between x and 2x where x is growing exponentially
96
- delay_scale = int ( 2 ** ( self . number_of_retries / 2.0 - 1 )) + 1
97
- delay = delay_scale + random .randint ( 1 , delay_scale )
98
- message = "Sleeping for %ss [max %s] before retrying." % (delay , delay_scale * 2 )
97
+ delay_base = 0.5
98
+ delay = random .random () * min ( self . delay_cap , delay_base * ( 2 ** self . number_of_retries ) )
99
+ message = "Sleeping for %ss before retrying." % (delay ,)
99
100
try :
100
101
logger .warning (message )
101
102
except NameError :
@@ -509,13 +510,10 @@ def do_api_query(self, orig_request, url, method="POST",
509
510
query_state = {
510
511
'had_error_retry' : False ,
511
512
'request' : request ,
512
- 'failures' : 0 ,
513
513
} # type: Dict[str, Any]
514
514
515
515
def error_retry (error_string ):
516
- # type: (str) -> bool
517
- if not self .retry_on_errors or query_state ["failures" ] >= 10 :
518
- return False
516
+ # type: (str) -> None
519
517
if self .verbose :
520
518
if not query_state ["had_error_retry" ]:
521
519
sys .stdout .write ("zulip API(%s): connection error%s -- retrying." %
@@ -525,14 +523,6 @@ def error_retry(error_string):
525
523
sys .stdout .write ("." )
526
524
sys .stdout .flush ()
527
525
query_state ["request" ]["dont_block" ] = json .dumps (True )
528
- delay_cap = 10
529
- delay_base = 0.5
530
- delay_time = random .random () * delay_cap
531
- if query_state ["failures" ] <= 5 :
532
- delay_time = random .random () * min (delay_cap , delay_base * (2 ** query_state ["failures" ]))
533
- time .sleep (delay_time )
534
- query_state ["failures" ] += 1
535
- return True
536
526
537
527
def end_error_retry (succeeded ):
538
528
# type: (bool) -> None
@@ -541,8 +531,8 @@ def end_error_retry(succeeded):
541
531
print ("Success!" )
542
532
else :
543
533
print ("Failed!" )
544
-
545
- while True :
534
+ backoff = RandomExponentialBackoff ( timeout_success_equivalent = 300 )
535
+ while backoff . keep_going () :
546
536
try :
547
537
if method == "GET" :
548
538
kwarg = "params"
@@ -565,7 +555,9 @@ def end_error_retry(succeeded):
565
555
566
556
# On 50x errors, try again after a short sleep
567
557
if str (res .status_code ).startswith ('5' ):
568
- if error_retry (" (server %s)" % (res .status_code ,)):
558
+ error_retry (" (server %s)" % (res .status_code ,))
559
+ backoff .fail ()
560
+ if backoff .keep_going ():
569
561
continue
570
562
# Otherwise fall through and process the python-requests error normally
571
563
except (requests .exceptions .Timeout , requests .exceptions .SSLError ) as e :
@@ -591,7 +583,9 @@ def end_error_retry(succeeded):
591
583
# in an invalid site.
592
584
raise UnrecoverableNetworkError ('cannot connect to server ' + self .base_url )
593
585
594
- if error_retry ("" ):
586
+ error_retry ("" )
587
+ backoff .fail ()
588
+ if backoff .keep_going ():
595
589
continue
596
590
end_error_retry (False )
597
591
return {'msg' : "Connection error:\n %s" % traceback .format_exc (),
@@ -615,6 +609,7 @@ def end_error_retry(succeeded):
615
609
end_error_retry (False )
616
610
return {'msg' : "Unexpected error from the server" , "result" : "http-error" ,
617
611
"status_code" : res .status_code }
612
+ return {'msg' : "Unexpected error from the server" , "result" : "unexpected-error" }
618
613
619
614
def call_endpoint (self , url = None , method = "POST" , request = None ,
620
615
longpolling = False , files = None , timeout = None ):
0 commit comments