voussoirkit/voussoirkit/ratelimiter.py
2018-12-17 22:10:00 -08:00

66 lines
2 KiB
Python

import time
class Ratelimiter:
def __init__(self, allowance, period=1, operation_cost=1, mode='sleep'):
'''
allowance:
Our spending balance per `period` seconds.
period:
The number of seconds over which we can perform `allowance` operations.
operation_cost:
The default amount to remove from our balance after each operation.
Pass a `cost` parameter to `self.limit` to use a nondefault value.
mode:
'sleep':
If we do not have the balance for an operation, sleep until we do.
Return True every time.
'reject':
If we do not have the balance for an operation, return False.
The cost is not subtracted, so hopefully we have enough next time.
'''
if mode not in ('sleep', 'reject'):
raise ValueError('Invalid mode %s' % repr(mode))
self.allowance = allowance
self.period = period
self.operation_cost = operation_cost
self.mode = mode
self.last_operation = time.time()
self.balance = 0
@property
def gain_rate(self):
return self.allowance / self.period
def limit(self, cost=None):
'''
See the main class docstring for info about cost and mode behavior.
'''
if cost is None:
cost = self.operation_cost
time_diff = time.time() - self.last_operation
self.balance += time_diff * self.gain_rate
self.balance = min(self.balance, self.allowance)
if self.balance >= cost:
self.balance -= cost
succesful = True
else:
if self.mode == 'reject':
succesful = False
else:
deficit = cost - self.balance
time_needed = deficit / self.gain_rate
time.sleep(time_needed)
self.balance = 0
succesful = True
self.last_operation = time.time()
return succesful