|
7 | 7 | from dataclasses import dataclass, field |
8 | 8 | from typing import TYPE_CHECKING |
9 | 9 |
|
10 | | -from aws_durable_execution_sdk_python.config import Duration, JitterStrategy |
| 10 | +from aws_durable_execution_sdk_python.config import ( |
| 11 | + BackoffStrategy, |
| 12 | + Duration, |
| 13 | + JitterStrategy, |
| 14 | +) |
11 | 15 |
|
12 | 16 | if TYPE_CHECKING: |
13 | 17 | from collections.abc import Callable |
@@ -49,6 +53,7 @@ class RetryStrategyConfig: |
49 | 53 | default_factory=lambda: Duration.from_minutes(5) |
50 | 54 | ) # 5 minutes |
51 | 55 | backoff_rate: Numeric = 2.0 |
| 56 | + backoff_strategy: BackoffStrategy = field(default=BackoffStrategy.EXPONENTIAL) |
52 | 57 | jitter_strategy: JitterStrategy = field(default=JitterStrategy.FULL) |
53 | 58 | retryable_errors: list[str | re.Pattern] | None = None |
54 | 59 | retryable_error_types: list[type[Exception]] | None = None |
@@ -103,10 +108,12 @@ def retry_strategy(error: Exception, attempts_made: int) -> RetryDecision: |
103 | 108 | if not is_retryable_error_message and not is_retryable_error_type: |
104 | 109 | return RetryDecision.no_retry() |
105 | 110 |
|
106 | | - # Calculate delay with exponential backoff |
107 | | - base_delay: float = min( |
108 | | - config.initial_delay_seconds * (config.backoff_rate ** (attempts_made - 1)), |
109 | | - config.max_delay_seconds, |
| 111 | + # Calculate delay using configured backoff strategy |
| 112 | + base_delay: float = config.backoff_strategy.calculate_base_delay( |
| 113 | + initial_delay_seconds=config.initial_delay_seconds, |
| 114 | + backoff_rate=config.backoff_rate, |
| 115 | + attempts_made=attempts_made, |
| 116 | + max_delay_seconds=config.max_delay_seconds, |
110 | 117 | ) |
111 | 118 | # Apply jitter to get final delay |
112 | 119 | delay_with_jitter: float = config.jitter_strategy.apply_jitter(base_delay) |
@@ -172,3 +179,42 @@ def critical(cls) -> Callable[[Exception, int], RetryDecision]: |
172 | 179 | jitter_strategy=JitterStrategy.NONE, |
173 | 180 | ) |
174 | 181 | ) |
| 182 | + |
| 183 | + @classmethod |
| 184 | + def fixed_wait(cls) -> Callable[[Exception, int], RetryDecision]: |
| 185 | + """Constant delay between retries with no backoff.""" |
| 186 | + return create_retry_strategy( |
| 187 | + RetryStrategyConfig( |
| 188 | + max_attempts=5, |
| 189 | + initial_delay=Duration.from_seconds(5), |
| 190 | + max_delay=Duration.from_minutes(5), |
| 191 | + backoff_strategy=BackoffStrategy.FIXED, |
| 192 | + jitter_strategy=JitterStrategy.NONE, |
| 193 | + ) |
| 194 | + ) |
| 195 | + |
| 196 | + @classmethod |
| 197 | + def linear_backoff(cls) -> Callable[[Exception, int], RetryDecision]: |
| 198 | + """Linearly increasing delay between retries.""" |
| 199 | + return create_retry_strategy( |
| 200 | + RetryStrategyConfig( |
| 201 | + max_attempts=5, |
| 202 | + initial_delay=Duration.from_seconds(5), |
| 203 | + max_delay=Duration.from_minutes(5), |
| 204 | + backoff_strategy=BackoffStrategy.LINEAR, |
| 205 | + jitter_strategy=JitterStrategy.FULL, |
| 206 | + ) |
| 207 | + ) |
| 208 | + |
| 209 | + @classmethod |
| 210 | + def slow(cls) -> Callable[[Exception, int], RetryDecision]: |
| 211 | + """Long delays for operations that need extended recovery time.""" |
| 212 | + return create_retry_strategy( |
| 213 | + RetryStrategyConfig( |
| 214 | + max_attempts=8, |
| 215 | + initial_delay=Duration.from_seconds(30), |
| 216 | + max_delay=Duration.from_minutes(10), |
| 217 | + backoff_rate=2, |
| 218 | + jitter_strategy=JitterStrategy.FULL, |
| 219 | + ) |
| 220 | + ) |
0 commit comments