Julius Unverfehrt 4edf04ab24 Pull request #1: Add storage handle
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
2022-02-16 11:57:52 +01:00

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