• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SwissDataScienceCenter / renku-data-services / 14382014257

10 Apr 2025 01:42PM UTC coverage: 86.576% (+0.2%) from 86.351%
14382014257

Pull #759

github

web-flow
Merge 470ff1568 into 74eb7d965
Pull Request #759: feat: add new service cache and migrations

412 of 486 new or added lines in 15 files covered. (84.77%)

18 existing lines in 6 files now uncovered.

20232 of 23369 relevant lines covered (86.58%)

1.53 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

31.25
/components/renku_data_services/notebooks/util/retries.py
1
"""Methods for retrying requests."""
2

3
import asyncio
2✔
4
import functools
2✔
5
from collections.abc import Awaitable, Callable
2✔
6
from time import sleep
2✔
7
from typing import Concatenate, ParamSpec, TypeVar
2✔
8

9
from renku_data_services.notebooks.errors.intermittent import RetryTimeoutError
2✔
10

11
_RetType = TypeVar("_RetType")
2✔
12
_Params = ParamSpec("_Params")
2✔
13

14

15
def retry_with_exponential_backoff(
2✔
16
    should_retry: Callable[[_RetType], bool],
17
    num_retries: int = 10,
18
    initial_wait_ms: int = 20,
19
    multiplier: float = 2.0,
20
) -> Callable[[Callable[Concatenate[_Params], _RetType]], Callable[Concatenate[_Params], _RetType]]:
21
    """Retries the wrapped function with an exponential backoff.
22

23
    The should_retry "callback" is passed the results from calling the wrapped function.
24
    If the response is true, the function is called again, otherwise the loop ends and
25
    the result of the wrapped function is returned.
26

27
    With the default values the wait times start with 20ms and then double every iteration.
28
    """
29

30
    def decorator_retry(func: Callable[Concatenate[_Params], _RetType]) -> Callable[Concatenate[_Params], _RetType]:
×
31
        @functools.wraps(func)
×
32
        def wrapper_retry(*args: _Params.args, **kwargs: _Params.kwargs) -> _RetType:
×
33
            for i in range(num_retries):
×
34
                res = func(*args, **kwargs)
×
35
                if not should_retry(res):
×
36
                    return res
×
37
                sleep(initial_wait_ms * (multiplier**i) / 1000)
×
38
            raise RetryTimeoutError(f"Retrying the function {func.__name__} timed out after {num_retries} retries.")
×
39

40
        return wrapper_retry
×
41

42
    return decorator_retry
×
43

44

45
def retry_with_exponential_backoff_async(
2✔
46
    should_retry: Callable[[_RetType], bool],
47
    num_retries: int = 10,
48
    initial_wait_ms: int = 20,
49
    multiplier: float = 2.0,
50
) -> Callable[
51
    [Callable[Concatenate[_Params], Awaitable[_RetType]]], Callable[Concatenate[_Params], Awaitable[_RetType]]
52
]:
53
    """Retries the wrapped function with an exponential backoff.
54

55
    The should_retry "callback" is passed the results from calling the wrapped function.
56
    If the response is true, the function is called again, otherwise the loop ends and
57
    the result of the wrapped function is returned.
58

59
    With the default values the wait times start with 20ms and then double every iteration.
60
    """
61

UNCOV
62
    def decorator_retry(
×
63
        func: Callable[Concatenate[_Params], Awaitable[_RetType]],
64
    ) -> Callable[Concatenate[_Params], Awaitable[_RetType]]:
UNCOV
65
        @functools.wraps(func)
×
UNCOV
66
        async def wrapper_retry(*args: _Params.args, **kwargs: _Params.kwargs) -> _RetType:
×
UNCOV
67
            for i in range(num_retries):
×
UNCOV
68
                res = await func(*args, **kwargs)
×
UNCOV
69
                if not should_retry(res):
×
UNCOV
70
                    return res
×
71

72
                await asyncio.sleep(initial_wait_ms * (multiplier**i) / 1000)
×
73
            raise RetryTimeoutError(f"Retrying the function {func.__name__} timed out after {num_retries} retries.")
×
74

UNCOV
75
        return wrapper_retry
×
76

UNCOV
77
    return decorator_retry
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc