Refactor backoff looping call
This codebase can now be updated to use the refactored base class to avoid duplicating alot of the same code. Eventually we should also just move this to oslo if when/this review is accepted since that seems like a better home for this code in general. Change-Id: I387d60667f427824a64d52544638792429887ebf
This commit is contained in:
parent
b30efc1c7d
commit
04582bc219
@ -13,10 +13,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import random
|
import random
|
||||||
import sys
|
|
||||||
|
|
||||||
from eventlet import event
|
|
||||||
from eventlet import greenthread
|
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
from oslo_service import loopingcall
|
from oslo_service import loopingcall
|
||||||
|
|
||||||
@ -78,56 +75,42 @@ class BackOffLoopingCall(loopingcall.LoopingCallBase):
|
|||||||
timeout.
|
timeout.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
_KIND = 'Dynamic backoff interval looping call'
|
||||||
|
_RUN_ONLY_ONE_MESSAGE = ("A dynamic backoff interval looping call can"
|
||||||
|
" only run one function at a time")
|
||||||
|
|
||||||
|
def __init__(self, f=None, *args, **kw):
|
||||||
|
super(BackOffLoopingCall, self).__init__(f=f, *args, **kw)
|
||||||
|
self._error_time = 0
|
||||||
|
self._interval = 1
|
||||||
|
|
||||||
def start(self, initial_delay=None, starting_interval=1, timeout=300,
|
def start(self, initial_delay=None, starting_interval=1, timeout=300,
|
||||||
max_interval=300, jitter=0.75):
|
max_interval=300, jitter=0.75):
|
||||||
self._running = True
|
if self._thread is not None:
|
||||||
done = event.Event()
|
raise RuntimeError(self._RUN_ONLY_ONE_MESSAGE)
|
||||||
|
|
||||||
def _inner():
|
# Reset any prior state.
|
||||||
interval = starting_interval
|
self._error_time = 0
|
||||||
error_time = 0
|
self._interval = starting_interval
|
||||||
|
|
||||||
if initial_delay:
|
def _idle_for(success, _elapsed):
|
||||||
greenthread.sleep(initial_delay)
|
|
||||||
|
|
||||||
try:
|
|
||||||
while self._running:
|
|
||||||
no_error = self.f(*self.args, **self.kw)
|
|
||||||
if not self._running:
|
|
||||||
break
|
|
||||||
random_jitter = random.gauss(jitter, 0.1)
|
random_jitter = random.gauss(jitter, 0.1)
|
||||||
if no_error:
|
if success:
|
||||||
# Reset error state
|
# Reset error state now that it didn't error...
|
||||||
error_time = 0
|
self._interval = starting_interval
|
||||||
interval = starting_interval
|
self._error_time = 0
|
||||||
idle = interval * random_jitter
|
return self._interval * random_jitter
|
||||||
else:
|
else:
|
||||||
# Backoff
|
# Perform backoff
|
||||||
interval = min(interval * 2 * random_jitter,
|
self._interval = idle = min(
|
||||||
max_interval)
|
self._interval * 2 * random_jitter, max_interval)
|
||||||
idle = interval
|
|
||||||
|
|
||||||
# Don't go over timeout, end early if necessary. If
|
# Don't go over timeout, end early if necessary. If
|
||||||
# timeout is 0, keep going.
|
# timeout is 0, keep going.
|
||||||
if timeout > 0 and error_time + idle > timeout:
|
if timeout > 0 and self._error_time + idle > timeout:
|
||||||
raise LoopingCallTimeOut(
|
raise LoopingCallTimeOut(
|
||||||
'Looping call timed out after %.02f seconds'
|
'Looping call timed out after %.02f seconds'
|
||||||
% error_time)
|
% self._error_time)
|
||||||
error_time += idle
|
self._error_time += idle
|
||||||
|
return idle
|
||||||
|
|
||||||
LOG.debug('Dynamic looping call sleeping for %.02f '
|
return self._start(_idle_for, initial_delay=initial_delay)
|
||||||
'seconds', idle)
|
|
||||||
greenthread.sleep(idle)
|
|
||||||
except loopingcall.LoopingCallDone as e:
|
|
||||||
self.stop()
|
|
||||||
done.send(e.retvalue)
|
|
||||||
except Exception:
|
|
||||||
LOG.exception('in dynamic looping call')
|
|
||||||
done.send_exception(*sys.exc_info())
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
done.send(True)
|
|
||||||
|
|
||||||
self.done = done
|
|
||||||
greenthread.spawn(_inner)
|
|
||||||
return self.done
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user