def wrapper(*args, **kwargs): next_interval = self.retry_interval remaining = self.max_retries while True: try: return f(*args, **kwargs) except Exception as e: with excutils.save_and_reraise_exception() as ectxt: if remaining > 0: ectxt.reraise = not self._is_exception_expected(e) else: LOG.exception('DB exceeded retry limit.') # if it's a RetryRequest, we need to unpack it if isinstance(e, exceptions.RetryRequest): ectxt.type_ = type(e.inner_exc) ectxt.value = e.inner_exc LOG.debug("Performing DB retry for function %s", reflection.get_callable_name(f)) # NOTE(vsergeyev): We are using patched time module, so # this effectively yields the execution # context to another green thread. time.sleep(next_interval) if self.inc_retry_interval: next_interval = min(next_interval * 2, self.max_retry_interval) remaining -= 1
def __init__(self, functor, requires, name=None, provides=None, auto_extract=True, rebind=None, inject=None): if not six.callable(functor): raise ValueError("Function to use for map must be callable") f_args = reflection.get_callable_args(functor) if len(f_args) != 1: raise ValueError("%s arguments were provided. Map functor must " "take exactly 1 argument." % len(f_args)) if not misc.is_iterable(requires): raise TypeError("%s type was provided for requires. Requires " "must be an iterable." % type(requires)) if name is None: name = reflection.get_callable_name(functor) super(MapFunctorTask, self).__init__(name=name, provides=provides, inject=inject, requires=requires, rebind=rebind, auto_extract=auto_extract) self._functor = functor
def safe_func_wrapper(f, logger=None): try: f() except Exception as e: if logger: logger.error( 'function %s error: %s' % (reflection.get_callable_name(f), e.__class__.__name__)) if hasattr(logger, 'exception'): logger.exception('Safe error traceback')
def _idle_for(result, elapsed): delay = round(elapsed - interval, 2) if delay > 0: func_name = reflection.get_callable_name(self.f) LOG.warning( 'Function %(func_name)r run outlasted ' 'interval by %(delay).2f sec', { 'func_name': func_name, 'delay': delay }) return -delay if delay < 0 else 0
def _fetch_validate_factory(flow_factory): if isinstance(flow_factory, six.string_types): factory_fun = _fetch_factory(flow_factory) factory_name = flow_factory else: factory_fun = flow_factory factory_name = reflection.get_callable_name(flow_factory) try: reimported = _fetch_factory(factory_name) assert reimported == factory_fun except (ImportError, AssertionError): raise ValueError('Flow factory %r is not reimportable by name %s' % (factory_fun, factory_name)) return factory_name, factory_fun
def __call__(self, f): func_name = reflection.get_callable_name(f) def _func(*args, **kwargs): result = None try: if self._retry_count: LOG.debug( "Invoking %(func_name)s; retry count is " "%(retry_count)d.", { 'func_name': func_name, 'retry_count': self._retry_count }) result = f(*args, **kwargs) except self._exceptions: with excutils.save_and_reraise_exception() as ctxt: LOG.debug( "Exception which is in the suggested list of " "exceptions occurred while invoking function:" " %s.", func_name) if (self._max_retry_count != -1 and self._retry_count >= self._max_retry_count): LOG.debug( "Cannot retry %(func_name)s upon " "suggested exception " "since retry count (%(retry_count)d) " "reached max retry count " "(%(max_retry_count)d).", { 'retry_count': self._retry_count, 'max_retry_count': self._max_retry_count, 'func_name': func_name }) else: ctxt.reraise = False self._retry_count += 1 self._sleep_time += self._inc_sleep_time return self._sleep_time raise LoopingCallDone(result) @six.wraps(f) def func(*args, **kwargs): loop = DynamicLoopingCall(_func, *args, **kwargs) evt = loop.start(periodic_interval_max=self._max_sleep_time) LOG.debug("Waiting for function %s to return.", func_name) return evt.wait() return func
def _run_loop(self, idle_for_func, initial_delay=None, stop_on_exception=True): kind = self._KIND func_name = reflection.get_callable_name(self.f) func = self.f if stop_on_exception else _safe_wrapper( self.f, kind, func_name) if initial_delay: greenthread.sleep(initial_delay) try: watch = timeutils.StopWatch() while self._running: watch.restart() result = func(*self.args, **self.kw) watch.stop() if not self._running: break idle = idle_for_func(result, watch.elapsed()) LOG.trace( '%(kind)s %(func_name)r sleeping ' 'for %(idle).02f seconds', { 'func_name': func_name, 'idle': idle, 'kind': kind }) greenthread.sleep(idle) except LoopingCallDone as e: self.done.send(e.retvalue) except Exception: exc_info = sys.exc_info() try: LOG.error('%(kind)s %(func_name)r failed', { 'kind': kind, 'func_name': func_name }, exc_info=exc_info) self.done.send_exception(*exc_info) finally: del exc_info return else: self.done.send(True)
def __init__(self, execute, name=None, provides=None, requires=None, auto_extract=True, rebind=None, revert=None, version=None, inject=None): if not six.callable(execute): raise ValueError("Function to use for executing must be" " callable") if revert is not None: if not six.callable(revert): raise ValueError("Function to use for reverting must" " be callable") if name is None: name = reflection.get_callable_name(execute) super(FunctorTask, self).__init__(name, provides=provides, inject=inject) self._execute = execute self._revert = revert if version is not None: self.version = version mapping = self._build_arg_mapping(execute, requires, rebind, auto_extract) self.rebind, exec_requires, self.optional = mapping if revert: revert_mapping = self._build_arg_mapping(revert, requires, rebind, auto_extract) else: revert_mapping = (self.rebind, exec_requires, self.optional) (self.revert_rebind, revert_requires, self.revert_optional) = revert_mapping self.requires = exec_requires.union(revert_requires)