Spaces:
Running
Running
Create utils/retry_mechanism.py
Browse files
insight_and_tasks/utils/retry_mechanism.py
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# utils/retry_mechanism.py
|
2 |
+
import time
|
3 |
+
import logging
|
4 |
+
from typing import Callable, Any, Tuple
|
5 |
+
|
6 |
+
# Configure logger for this module
|
7 |
+
logger = logging.getLogger(__name__)
|
8 |
+
|
9 |
+
class RetryMechanism:
|
10 |
+
"""External retry mechanism with exponential backoff"""
|
11 |
+
|
12 |
+
@staticmethod
|
13 |
+
def retry_with_backoff(
|
14 |
+
func: Callable,
|
15 |
+
max_retries: int = 3,
|
16 |
+
base_delay: float = 1.0,
|
17 |
+
exceptions: Tuple[type[Exception], ...] = (Exception,) # More specific type hint
|
18 |
+
) -> Any:
|
19 |
+
"""
|
20 |
+
Retries a function call with exponential backoff.
|
21 |
+
|
22 |
+
Args:
|
23 |
+
func: The function to call.
|
24 |
+
max_retries: Maximum number of retries.
|
25 |
+
base_delay: Base delay in seconds for backoff.
|
26 |
+
exceptions: A tuple of exception types to catch and retry on.
|
27 |
+
|
28 |
+
Returns:
|
29 |
+
The result of the function call if successful.
|
30 |
+
|
31 |
+
Raises:
|
32 |
+
The last exception encountered if all retries fail.
|
33 |
+
"""
|
34 |
+
last_exception = None
|
35 |
+
current_delay = base_delay
|
36 |
+
|
37 |
+
for attempt in range(max_retries + 1): # +1 for initial attempt
|
38 |
+
try:
|
39 |
+
logger.info(f"Attempt {attempt + 1}/{max_retries + 1} for function {func.__name__}")
|
40 |
+
result = func()
|
41 |
+
if attempt > 0: # Log if a retry was successful
|
42 |
+
logger.info(f"Function {func.__name__} succeeded on attempt {attempt + 1}")
|
43 |
+
return result
|
44 |
+
except exceptions as e:
|
45 |
+
last_exception = e
|
46 |
+
logger.warning(f"Attempt {attempt + 1} for {func.__name__} failed: {str(e)}")
|
47 |
+
|
48 |
+
if attempt < max_retries:
|
49 |
+
logger.info(f"Waiting {current_delay:.2f} seconds before retrying {func.__name__}...")
|
50 |
+
time.sleep(current_delay)
|
51 |
+
current_delay *= 2 # Exponential backoff
|
52 |
+
else:
|
53 |
+
logger.error(f"All {max_retries + 1} attempts for {func.__name__} failed.")
|
54 |
+
|
55 |
+
# If loop finishes, all retries failed, raise the last exception
|
56 |
+
if last_exception is not None:
|
57 |
+
raise last_exception
|
58 |
+
else:
|
59 |
+
# This case should ideally not be reached if func always raises on failure
|
60 |
+
# or returns successfully. Added for completeness.
|
61 |
+
raise RuntimeError(f"Function {func.__name__} failed after all retries without a specific exception.")
|