Merge "AbstractRateLimiter: add option to burst on start-up"

This commit is contained in:
Zuul 2022-05-17 08:15:18 +00:00 committed by Gerrit Code Review
commit 4ff0226707
2 changed files with 35 additions and 6 deletions

View File

@ -3484,21 +3484,31 @@ class AbstractRateLimiter(object):
# 1,000 milliseconds = 1 second
clock_accuracy = 1000.0
def __init__(self, max_rate, rate_buffer=5, running_time=0):
def __init__(self, max_rate, rate_buffer=5, burst_after_idle=False,
running_time=0):
"""
:param max_rate: The maximum rate per second allowed for the process.
Must be > 0 to engage rate-limiting behavior.
:param rate_buffer: Number of seconds the rate counter can drop and be
allowed to catch up (at a faster than listed rate). A larger number
will result in larger spikes in rate but better average accuracy.
:param burst_after_idle: If False (the default) then the rate_buffer
allowance is lost after the rate limiter has not been called for
more than rate_buffer seconds. If True then the rate_buffer
allowance is preserved during idle periods which means that a burst
of requests may be granted immediately after the idle period.
:param running_time: The running time in milliseconds of the next
allowable request. Setting this to any time in the past will cause
the rate limiter to immediately allow requests; setting this to a
future time will cause the rate limiter to deny requests until that
time.
time. If ``burst_after_idle`` is True then this can
be set to current time (ms) to avoid an initial burst, or set to
running_time < (current time - rate_buffer ms) to allow an initial
burst.
"""
self.max_rate = max_rate
self.rate_buffer_ms = rate_buffer * self.clock_accuracy
self.burst_after_idle = burst_after_idle
self.running_time = running_time
self.time_per_incr = (self.clock_accuracy / self.max_rate
if self.max_rate else 0)
@ -3535,6 +3545,8 @@ class AbstractRateLimiter(object):
# Convert rate_buffer to milliseconds and compare
if now - self.running_time > self.rate_buffer_ms:
self.running_time = now
if self.burst_after_idle:
self.running_time -= self.rate_buffer_ms
if now >= self.running_time:
self.running_time += time_per_request
@ -3557,9 +3569,11 @@ class AbstractRateLimiter(object):
class EventletRateLimiter(AbstractRateLimiter):
def __init__(self, max_rate, rate_buffer=5, running_time=0):
def __init__(self, max_rate, rate_buffer=5, running_time=0,
burst_after_idle=False):
super(EventletRateLimiter, self).__init__(
max_rate, rate_buffer, running_time)
max_rate, rate_buffer=rate_buffer, running_time=running_time,
burst_after_idle=burst_after_idle)
def _sleep(self, seconds):
eventlet.sleep(seconds)

View File

@ -5900,11 +5900,12 @@ class TestEventletRateLimiter(unittest.TestCase):
mock_sleep.assert_not_called()
def _do_test(self, max_rate, running_time, start_time, rate_buffer,
incr_by=1.0):
burst_after_idle=False, incr_by=1.0):
rate_limiter = utils.EventletRateLimiter(
max_rate,
running_time=1000 * running_time, # msecs
rate_buffer=rate_buffer)
rate_buffer=rate_buffer,
burst_after_idle=burst_after_idle)
grant_times = []
current_time = [start_time]
@ -5972,6 +5973,20 @@ class TestEventletRateLimiter(unittest.TestCase):
grant_times = self._do_test(1, 0, 4, 3)
self.assertEqual([4, 5, 6, 7, 8], grant_times)
def test_burst_after_idle(self):
grant_times = self._do_test(1, 1, 4, 1, burst_after_idle=True)
self.assertEqual([4, 4, 5, 6, 7], grant_times)
grant_times = self._do_test(1, 1, 4, 2, burst_after_idle=True)
self.assertEqual([4, 4, 4, 5, 6], grant_times)
grant_times = self._do_test(1, 0, 4, 3, burst_after_idle=True)
self.assertEqual([4, 4, 4, 4, 5], grant_times)
# running_time = start_time prevents burst on start-up
grant_times = self._do_test(1, 4, 4, 3, burst_after_idle=True)
self.assertEqual([4, 5, 6, 7, 8], grant_times)
class TestRateLimitedIterator(unittest.TestCase):