File size: 2,412 Bytes
f813a3c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# utils/retry_mechanism.py
import time
import logging
from typing import Callable, Any, Tuple

# Configure logger for this module
logger = logging.getLogger(__name__)

class RetryMechanism:
    """External retry mechanism with exponential backoff"""

    @staticmethod
    def retry_with_backoff(
        func: Callable,
        max_retries: int = 3,
        base_delay: float = 1.0,
        exceptions: Tuple[type[Exception], ...] = (Exception,) # More specific type hint
    ) -> Any:
        """
        Retries a function call with exponential backoff.

        Args:
            func: The function to call.
            max_retries: Maximum number of retries.
            base_delay: Base delay in seconds for backoff.
            exceptions: A tuple of exception types to catch and retry on.

        Returns:
            The result of the function call if successful.

        Raises:
            The last exception encountered if all retries fail.
        """
        last_exception = None
        current_delay = base_delay

        for attempt in range(max_retries + 1):  # +1 for initial attempt
            try:
                logger.info(f"Attempt {attempt + 1}/{max_retries + 1} for function {func.__name__}")
                result = func()
                if attempt > 0: # Log if a retry was successful
                    logger.info(f"Function {func.__name__} succeeded on attempt {attempt + 1}")
                return result
            except exceptions as e:
                last_exception = e
                logger.warning(f"Attempt {attempt + 1} for {func.__name__} failed: {str(e)}")

                if attempt < max_retries:
                    logger.info(f"Waiting {current_delay:.2f} seconds before retrying {func.__name__}...")
                    time.sleep(current_delay)
                    current_delay *= 2  # Exponential backoff
                else:
                    logger.error(f"All {max_retries + 1} attempts for {func.__name__} failed.")

        # If loop finishes, all retries failed, raise the last exception
        if last_exception is not None:
            raise last_exception
        else:
            # This case should ideally not be reached if func always raises on failure
            # or returns successfully. Added for completeness.
            raise RuntimeError(f"Function {func.__name__} failed after all retries without a specific exception.")