Merge in RR/mini_queue from add-storage-handle to master
Squashed commit of the following:
commit 03e542d2a65802c28735873fae184209f0c83553
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date: Wed Feb 16 11:55:34 2022 +0100
Quickfix typo
commit b4d538e9445187435d87c5cf8ce1f4e448021129
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date: Wed Feb 16 11:41:42 2022 +0100
added prefetch count and make channel function
commit d46d1375e387d36641c06b062a8ccc54f114ef4c
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date: Wed Feb 16 11:20:39 2022 +0100
black on M.s request
commit bc47b20312a978f19b08531804bf42b00f0a88f0
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date: Wed Feb 16 11:19:57 2022 +0100
changed response
commit 9a475ecd8df9ca007e5f7fe146483b6403eccc3b
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date: Wed Feb 16 10:15:08 2022 +0100
.
commit 108bc3ea90d867575db8c1b1503c9df859222485
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date: Wed Feb 16 09:56:56 2022 +0100
quickrestore
commit ae04d17d8d041f612d86117e8e96c96ddffcbde3
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date: Wed Feb 16 09:37:30 2022 +0100
refactor
commit 68051a72eb93868eba8adba234258b9e5373ecaa
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date: Wed Feb 16 08:50:59 2022 +0100
added answer file template for rancher
commit 09ef45ead51c07732a20133acad0b8b2ae7d0a61
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date: Wed Feb 16 08:26:05 2022 +0100
Quickfix inconsistency
commit d925b0f3f91f29403c88fb6149566ec966af2973
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date: Wed Feb 16 08:20:40 2022 +0100
Quick refactor
commit 48795455cde8d97ed98e58c3004a87a26f331352
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date: Tue Feb 15 17:46:45 2022 +0100
bluckckck
commit 80e58efab0269dc513990f83b14ceb36b3e4dd8e
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date: Tue Feb 15 17:45:49 2022 +0100
Quick restatus setting
commit 83f276ee13348a678b7da84e25ca844dd348b4c9
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date: Tue Feb 15 17:30:16 2022 +0100
Quickreset to working status
commit d44cdcf922250639a6832cc3e16d0d967d9853fb
Author: Julius Unverfehrt <Julius.Unverfehrt@iqser.com>
Date: Tue Feb 15 14:44:26 2022 +0100
added storage handle for minio WIP
117 lines
3.6 KiB
Python
117 lines
3.6 KiB
Python
import functools
|
|
import logging
|
|
import time
|
|
from functools import partial, wraps
|
|
from math import exp
|
|
from typing import Tuple, Type, Callable
|
|
|
|
|
|
def invocation_counter(var: str):
|
|
def inner(func):
|
|
invocation_count = 0
|
|
|
|
@functools.wraps(func)
|
|
def inner(*args, **kwargs):
|
|
nonlocal invocation_count
|
|
invocation_count += 1
|
|
|
|
if var not in kwargs:
|
|
kwargs[var] = invocation_count
|
|
|
|
return func(*args, **kwargs)
|
|
|
|
return inner
|
|
|
|
return inner
|
|
|
|
|
|
class NoAttemptsLeft(Exception):
|
|
pass
|
|
|
|
|
|
class MaxTimeoutReached(Exception):
|
|
pass
|
|
|
|
|
|
class _MethodDecoratorAdaptor(object):
|
|
def __init__(self, decorator, func):
|
|
self.decorator = decorator
|
|
self.func = func
|
|
|
|
def __call__(self, *args, **kwargs):
|
|
return self.decorator(self.func)(*args, **kwargs)
|
|
|
|
def __get__(self, obj, objtype):
|
|
return partial(self.__call__, obj)
|
|
|
|
|
|
def auto_adapt_to_methods(decorator):
|
|
"""Allows you to use the same decorator on methods and functions,
|
|
hiding the self argument from the decorator."""
|
|
|
|
def adapt(func):
|
|
return _MethodDecoratorAdaptor(decorator, func)
|
|
|
|
return adapt
|
|
|
|
|
|
def max_attempts(
|
|
n_attempts: int = 5, exceptions: Tuple[Type[Exception]] = None, timeout: float = 0.1, max_timeout: float = 10
|
|
) -> Callable:
|
|
"""Function decorator that attempts to run the wrapped function a certain number of times. Timeouts increase
|
|
exponentially according to `Tₖ ≔ t eᵏ`, where `t` is the timeout factor `timeout` and `k` is the attempt number.
|
|
If `∑ᵢ Tᵢ > mₜ` at the `i-th` attempt, where `mₜ` is the maximum timeout, then the function raises
|
|
MaxTimeoutReached. If `k > mₐ`, where `mₐ` is the maximum number of attempts allowed, then the function
|
|
raises NoAttemptsLeft.
|
|
|
|
Args:
|
|
n_attempts: Number of times to attempt running the wrapped function.
|
|
exceptions: Exceptions to catch for a re-attempt.
|
|
timeout: Timeout factor in seconds.
|
|
max_timeout: Maximum allowed timeout.
|
|
|
|
Raises:
|
|
MaxTimeoutReached
|
|
NoAttemptsLeft
|
|
|
|
Returns:
|
|
Wrapped function.
|
|
"""
|
|
if not exceptions:
|
|
exceptions = (Exception,)
|
|
assert isinstance(exceptions, tuple)
|
|
|
|
@auto_adapt_to_methods
|
|
def inner(func):
|
|
@wraps(func)
|
|
def inner(*args, **kwargs):
|
|
def run_attempt(attempt, timeout_aggr=0):
|
|
if attempt:
|
|
try:
|
|
return func(*args, **kwargs)
|
|
except exceptions as err:
|
|
|
|
attempt_num = n_attempts - attempt + 1
|
|
next_timeout = timeout * exp(attempt_num - 1) # start with timeout * e^0 = timeout
|
|
|
|
logging.warn(f"{func.__name__} failed; attempt {attempt_num} of {n_attempts}")
|
|
|
|
time_left = max(0, max_timeout - timeout_aggr)
|
|
if time_left:
|
|
sleep_for = min(next_timeout, time_left)
|
|
time.sleep(sleep_for)
|
|
return run_attempt(attempt - 1, timeout_aggr + sleep_for)
|
|
else:
|
|
logging.exception(err)
|
|
raise MaxTimeoutReached(
|
|
f"{func.__name__} reached maximum timeout ({max_timeout}) after {attempt_num} attempts."
|
|
)
|
|
else:
|
|
raise NoAttemptsLeft(f"{func.__name__} failed {n_attempts} times; all attempts expended.")
|
|
|
|
return run_attempt(n_attempts)
|
|
|
|
return inner
|
|
|
|
return inner
|