1from __future__ import absolute_import 2 3from decimal import Decimal 4 5 6def _Decimal(v): 7 if not isinstance(v, Decimal): 8 return Decimal(str(v)) 9 return v 10 11 12class BackoffTimer(object): 13 """ 14 This is a timer that is smart about backing off exponentially when there are problems 15 """ 16 def __init__(self, min_interval, max_interval, ratio=.25, short_length=10, long_length=250): 17 assert isinstance(min_interval, (int, float, Decimal)) 18 assert isinstance(max_interval, (int, float, Decimal)) 19 20 self.min_interval = _Decimal(min_interval) 21 self.max_interval = _Decimal(max_interval) 22 23 self.max_short_timer = (self.max_interval - self.min_interval) * _Decimal(ratio) 24 self.max_long_timer = (self.max_interval - self.min_interval) * (1 - _Decimal(ratio)) 25 self.short_unit = self.max_short_timer / _Decimal(short_length) 26 self.long_unit = self.max_long_timer / _Decimal(long_length) 27 28 self.short_interval = Decimal(0) 29 self.long_interval = Decimal(0) 30 self.update_interval() 31 32 def success(self): 33 """Update the timer to reflect a successfull call""" 34 if self.interval == 0.0: 35 return 36 self.short_interval -= self.short_unit 37 self.long_interval -= self.long_unit 38 self.short_interval = max(self.short_interval, Decimal(0)) 39 self.long_interval = max(self.long_interval, Decimal(0)) 40 self.update_interval() 41 42 def failure(self): 43 """Update the timer to reflect a failed call""" 44 self.short_interval += self.short_unit 45 self.long_interval += self.long_unit 46 self.short_interval = min(self.short_interval, self.max_short_timer) 47 self.long_interval = min(self.long_interval, self.max_long_timer) 48 self.update_interval() 49 50 def update_interval(self): 51 self.interval = float(self.min_interval + self.short_interval + self.long_interval) 52 53 def get_interval(self): 54 return self.interval 55