CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-tenacity

Retry code until it succeeds

Overview
Eval results
Files

wait-strategies.mddocs/

Wait Strategies

Wait strategies determine how long to wait between retry attempts. Tenacity provides 9+ sophisticated backoff algorithms including exponential backoff, jitter, fixed delays, and strategy composition to optimize retry timing for different scenarios.

Base Classes

wait_base

from tenacity.wait import wait_base

class wait_base(ABC):
    """
    Abstract base class for all wait strategies.
    
    Provides arithmetic operators for combining wait strategies
    and defines the interface all wait strategies must implement.
    """
    
    @abstractmethod
    def __call__(self, retry_state: RetryCallState) -> float:
        """
        Calculate wait time before next retry attempt.
        
        Parameters:
        - retry_state: Complete state of current retry session
        
        Returns:
        Wait time in seconds (float)
        """
    
    def __add__(self, other: 'wait_base') -> 'wait_combine':
        """Add wait times from two strategies."""
    
    def __radd__(self, other: Union[int, float, 'wait_base']) -> 'wait_combine':
        """Support for sum() builtin and reverse addition."""

WaitBaseT Type

from tenacity.wait import WaitBaseT

WaitBaseT = Union[wait_base, Callable[[RetryCallState], Union[float, int]]]

Basic Wait Strategies

wait_none

from tenacity import wait_none

class wait_none(wait_fixed):
    """
    No wait between retries (0 seconds).
    
    Inherits from wait_fixed with wait time of 0.
    Useful for immediate retries without delay.
    """
    
    def __init__(self):
        """Initialize with 0 second wait time."""
        super().__init__(0)

wait_fixed

from tenacity import wait_fixed
from tenacity._utils import time_unit_type

class wait_fixed(wait_base):
    """
    Fixed wait time between all retries.
    
    Simplest wait strategy - same delay between every attempt.
    """
    
    def __init__(self, wait: time_unit_type):
        """
        Initialize with fixed wait time.
        
        Parameters:
        - wait: Fixed time to wait between attempts
               Can be int/float (seconds) or timedelta
        """

Usage Examples

from datetime import timedelta

# No delay between retries
@retry(wait=wait_none())
def immediate_retry():
    pass

# Fixed 2 second delay
@retry(wait=wait_fixed(2))
def fixed_delay():
    pass

# Fixed delay using timedelta
@retry(wait=wait_fixed(timedelta(seconds=5)))
def fixed_delay_timedelta():
    pass

Random Wait Strategies

wait_random

from tenacity import wait_random

class wait_random(wait_base):
    """
    Random wait time within specified range.
    
    Generates uniformly distributed random wait times between
    min and max values. Helps avoid thundering herd problems.
    """
    
    def __init__(
        self, 
        min: time_unit_type = 0, 
        max: time_unit_type = 1
    ):
        """
        Initialize with random wait range.
        
        Parameters:
        - min: Minimum wait time (inclusive)
        - max: Maximum wait time (inclusive)
        """

Usage Examples

# Random wait between 0 and 1 second (default)
@retry(wait=wait_random())
def random_delay_default():
    pass

# Random wait between 1 and 5 seconds
@retry(wait=wait_random(min=1, max=5))
def random_delay_range():
    pass

Incremental Wait Strategies

wait_incrementing

from tenacity import wait_incrementing
from tenacity._utils import MAX_WAIT

class wait_incrementing(wait_base):
    """
    Incrementally increasing wait time.
    
    Increases wait time by fixed increment on each attempt.
    Useful for gradually backing off without exponential growth.
    """
    
    def __init__(
        self,
        start: time_unit_type = 0,
        increment: time_unit_type = 100,
        max: time_unit_type = MAX_WAIT
    ):
        """
        Initialize with incremental parameters.
        
        Parameters:
        - start: Starting wait time for first retry
        - increment: Amount to increase wait time each attempt  
        - max: Maximum wait time (caps the increment)
        """

Usage Examples

# Start at 1 second, increment by 2 seconds each attempt
@retry(wait=wait_incrementing(start=1, increment=2))
def incremental_backoff():
    pass
    # Attempt 1: wait 1s
    # Attempt 2: wait 3s  
    # Attempt 3: wait 5s
    # etc.

# Capped incremental backoff
@retry(wait=wait_incrementing(start=0, increment=5, max=30))
def capped_incremental():
    pass

Exponential Backoff Strategies

wait_exponential

from tenacity import wait_exponential

class wait_exponential(wait_base):
    """
    Exponential backoff with fixed intervals.
    
    Classic exponential backoff algorithm. Wait time grows
    exponentially: multiplier * (exp_base ^ (attempt_number - 1))
    """
    
    def __init__(
        self,
        multiplier: Union[int, float] = 1,
        max: time_unit_type = MAX_WAIT,
        exp_base: Union[int, float] = 2,
        min: time_unit_type = 0
    ):
        """
        Initialize exponential backoff parameters.
        
        Parameters:
        - multiplier: Multiplier for exponential calculation
        - max: Maximum wait time (caps exponential growth)
        - exp_base: Base for exponent calculation (default 2)
        - min: Minimum wait time (floor for calculation)
        """

wait_random_exponential (Full Jitter)

from tenacity import wait_random_exponential, wait_full_jitter

class wait_random_exponential(wait_exponential):
    """
    Exponential backoff with random jitter (Full Jitter algorithm).
    
    Implements AWS's Full Jitter algorithm: random(0, exponential_result).
    Prevents thundering herd while maintaining exponential backoff benefits.
    """
    
    def __init__(
        self,
        multiplier: Union[int, float] = 1, 
        max: time_unit_type = MAX_WAIT,
        exp_base: Union[int, float] = 2,
        min: time_unit_type = 0
    ):
        """Same parameters as wait_exponential, but with random jitter applied."""

# Alias for wait_random_exponential
wait_full_jitter = wait_random_exponential

wait_exponential_jitter

from tenacity import wait_exponential_jitter

class wait_exponential_jitter(wait_base):
    """
    Exponential backoff with configurable jitter.
    
    More flexible jitter implementation allowing custom jitter amounts.
    Formula: min(max, exponential_result + random(-jitter, jitter))
    """
    
    def __init__(
        self,
        initial: float = 1,
        max: float = MAX_WAIT, 
        exp_base: float = 2,
        jitter: float = 1
    ):
        """
        Initialize exponential backoff with jitter.
        
        Parameters:
        - initial: Initial wait time
        - max: Maximum wait time
        - exp_base: Base for exponential calculation
        - jitter: Maximum jitter amount (±jitter seconds)
        """

Exponential Usage Examples

# Standard exponential backoff: 2, 4, 8, 16 seconds...
@retry(wait=wait_exponential(multiplier=2, min=2, max=60))
def exponential_api_call():
    pass

# Exponential with full jitter (recommended for distributed systems)
@retry(wait=wait_random_exponential(multiplier=1, min=4, max=10))
def jittered_api_call():
    pass

# Custom exponential with base 3
@retry(wait=wait_exponential(multiplier=1, exp_base=3, max=100))
def base3_exponential():
    pass

# Exponential with configurable jitter
@retry(wait=wait_exponential_jitter(initial=1, jitter=0.5, max=30))
def custom_jitter():
    pass

Strategy Combination

wait_combine

from tenacity import wait_combine

class wait_combine(wait_base):
    """
    Sum multiple wait strategies.
    
    Adds together the wait times from multiple strategies.
    Useful for combining fixed base delays with variable components.
    """
    
    def __init__(self, *strategies: wait_base):
        """
        Initialize with strategies to combine.
        
        Parameters:
        - *strategies: Variable number of wait strategies to sum
        """

wait_chain

from tenacity import wait_chain

class wait_chain(wait_base):
    """
    Chain wait strategies in sequence.
    
    Uses each strategy in turn, then repeats the last strategy.
    Allows different wait patterns for early vs. later attempts.
    """
    
    def __init__(self, *strategies: wait_base):
        """
        Initialize with strategies to chain.
        
        Parameters:
        - *strategies: Wait strategies to use in sequence
        """

Combination Examples

# Combine fixed delay + random jitter
combined_wait = wait_combine(
    wait_fixed(3),           # Base 3 second delay
    wait_random(0, 2)        # Plus 0-2 seconds random
)

@retry(wait=combined_wait)
def combined_strategy():
    pass

# Chain different strategies for different phases
chained_wait = wait_chain(
    wait_fixed(1),           # First few attempts: 1 second
    wait_fixed(5),           # Next attempts: 5 seconds  
    wait_exponential(multiplier=2)  # Final attempts: exponential
)

@retry(wait=chained_wait)
def phased_backoff():
    pass

Arithmetic Operations

# Use + operator to combine strategies
base_delay = wait_fixed(2)
jitter = wait_random(0, 1)
combined = base_delay + jitter

# Chain multiple additions
complex_wait = wait_fixed(1) + wait_random(0, 2) + wait_exponential(multiplier=0.5)

@retry(wait=complex_wait)
def arithmetic_combination():
    pass

# Use sum() for multiple strategies
strategies = [wait_fixed(1), wait_random(0, 1), wait_exponential(multiplier=0.5)]
summed_wait = sum(strategies)

Advanced Usage Patterns

AWS-Style Exponential Backoff

# AWS SDK-style backoff with full jitter
@retry(
    wait=wait_random_exponential(multiplier=1, max=20),
    stop=stop_after_attempt(10),
    retry=retry_if_exception_type((ConnectionError, TimeoutError))
)
def aws_api_call():
    pass

Database Connection Retry

# Conservative database retry with incremental backoff
@retry(
    wait=wait_incrementing(start=2, increment=2, max=30),
    stop=stop_after_attempt(5),
    retry=retry_if_exception_type(ConnectionError)
)
def connect_to_db():
    pass

Web Scraping with Politeness

# Polite web scraping with randomized delays
@retry(
    wait=wait_combine(
        wait_fixed(1),        # Minimum 1 second between requests
        wait_random(0, 3)     # Plus 0-3 seconds random delay
    ),
    stop=stop_after_attempt(3),
    retry=retry_if_exception_type((requests.ConnectionError, requests.Timeout))
)
def scrape_webpage():
    pass

Circuit Breaker Integration

# Exponential backoff with circuit breaker timing
@retry(
    wait=wait_chain(
        wait_fixed(0.1),      # Fast initial retries
        wait_exponential(multiplier=1, min=1, max=60)  # Then exponential
    ),
    stop=stop_after_delay(300),  # 5 minute circuit open time
    retry=retry_if_exception_type(ServiceUnavailableError)
)
def circuit_breaker_call():
    pass

Custom Wait Strategy

# Custom fibonacci-style backoff
class wait_fibonacci(wait_base):
    def __init__(self, max_wait=MAX_WAIT):
        self.max_wait = max_wait
    
    def __call__(self, retry_state):
        attempt = retry_state.attempt_number
        if attempt == 1:
            return min(1, self.max_wait)
        elif attempt == 2:  
            return min(1, self.max_wait)
        else:
            # Simplified fibonacci: fib(n) = fib(n-1) + fib(n-2)
            a, b = 1, 1
            for _ in range(attempt - 2):
                a, b = b, a + b
            return min(b, self.max_wait)

@retry(wait=wait_fibonacci(max_wait=60))
def fibonacci_backoff():
    pass

Time-Based Dynamic Adjustment

# Dynamic wait based on time of day (lighter load during off-hours)
class wait_time_aware(wait_base):
    def __call__(self, retry_state):
        import datetime
        hour = datetime.datetime.now().hour
        
        # Longer waits during business hours (9 AM - 5 PM)
        if 9 <= hour <= 17:
            base_wait = 10
        else:
            base_wait = 2
            
        # Still apply exponential backoff
        multiplier = 2 ** (retry_state.attempt_number - 1)
        return min(base_wait * multiplier, 300)  # Max 5 minutes

@retry(wait=wait_time_aware())
def time_sensitive_operation():
    pass

Performance Optimization

# Efficient wait for high-frequency retries
@retry(
    wait=wait_none(),  # No delay for immediate retries
    stop=stop_after_attempt(3),
    retry=retry_if_exception_type(TransientError)
)
def high_frequency_operation():
    pass

# Bounded exponential for long-running services
@retry(
    wait=wait_exponential(multiplier=1, min=1, max=60),  # Cap at 1 minute
    stop=stop_never,  # Retry indefinitely  
    retry=retry_if_exception_type(RecoverableError)
)
def service_operation():
    pass

Testing Wait Strategies

# Fast waits for testing
@retry(
    wait=wait_fixed(0.1),  # 100ms for fast tests
    stop=stop_after_attempt(3)
)
def test_operation():
    pass

# Zero wait for unit tests
@retry(
    wait=wait_none(),      # No delay in tests
    stop=stop_after_attempt(2) 
)
def unit_test_function():
    pass

Constants and Utilities

from tenacity._utils import MAX_WAIT, time_unit_type, to_seconds

# Maximum wait time constant
MAX_WAIT: float  # sys.maxsize / 2

# Type for time specifications  
time_unit_type = Union[int, float, timedelta]

# Convert time units to seconds
def to_seconds(time_unit: time_unit_type) -> float:
    """Convert int/float/timedelta to seconds."""

This comprehensive coverage of wait strategies provides sophisticated control over retry timing, enabling optimal backoff behavior for various scenarios from high-frequency operations to long-running distributed systems.

Install with Tessl CLI

npx tessl i tessl/pypi-tenacity

docs

async-support.md

callbacks-hooks.md

core-decorator.md

index.md

retry-strategies.md

stop-conditions.md

utilities.md

wait-strategies.md

tile.json