0
# Wait Strategies
1
2
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.
3
4
## Base Classes
5
6
### wait_base
7
8
```python { .api }
9
from tenacity.wait import wait_base
10
11
class wait_base(ABC):
12
"""
13
Abstract base class for all wait strategies.
14
15
Provides arithmetic operators for combining wait strategies
16
and defines the interface all wait strategies must implement.
17
"""
18
19
@abstractmethod
20
def __call__(self, retry_state: RetryCallState) -> float:
21
"""
22
Calculate wait time before next retry attempt.
23
24
Parameters:
25
- retry_state: Complete state of current retry session
26
27
Returns:
28
Wait time in seconds (float)
29
"""
30
31
def __add__(self, other: 'wait_base') -> 'wait_combine':
32
"""Add wait times from two strategies."""
33
34
def __radd__(self, other: Union[int, float, 'wait_base']) -> 'wait_combine':
35
"""Support for sum() builtin and reverse addition."""
36
```
37
38
### WaitBaseT Type
39
40
```python { .api }
41
from tenacity.wait import WaitBaseT
42
43
WaitBaseT = Union[wait_base, Callable[[RetryCallState], Union[float, int]]]
44
```
45
46
## Basic Wait Strategies
47
48
### wait_none
49
50
```python { .api }
51
from tenacity import wait_none
52
53
class wait_none(wait_fixed):
54
"""
55
No wait between retries (0 seconds).
56
57
Inherits from wait_fixed with wait time of 0.
58
Useful for immediate retries without delay.
59
"""
60
61
def __init__(self):
62
"""Initialize with 0 second wait time."""
63
super().__init__(0)
64
```
65
66
### wait_fixed
67
68
```python { .api }
69
from tenacity import wait_fixed
70
from tenacity._utils import time_unit_type
71
72
class wait_fixed(wait_base):
73
"""
74
Fixed wait time between all retries.
75
76
Simplest wait strategy - same delay between every attempt.
77
"""
78
79
def __init__(self, wait: time_unit_type):
80
"""
81
Initialize with fixed wait time.
82
83
Parameters:
84
- wait: Fixed time to wait between attempts
85
Can be int/float (seconds) or timedelta
86
"""
87
```
88
89
### Usage Examples
90
91
```python { .api }
92
from datetime import timedelta
93
94
# No delay between retries
95
@retry(wait=wait_none())
96
def immediate_retry():
97
pass
98
99
# Fixed 2 second delay
100
@retry(wait=wait_fixed(2))
101
def fixed_delay():
102
pass
103
104
# Fixed delay using timedelta
105
@retry(wait=wait_fixed(timedelta(seconds=5)))
106
def fixed_delay_timedelta():
107
pass
108
```
109
110
## Random Wait Strategies
111
112
### wait_random
113
114
```python { .api }
115
from tenacity import wait_random
116
117
class wait_random(wait_base):
118
"""
119
Random wait time within specified range.
120
121
Generates uniformly distributed random wait times between
122
min and max values. Helps avoid thundering herd problems.
123
"""
124
125
def __init__(
126
self,
127
min: time_unit_type = 0,
128
max: time_unit_type = 1
129
):
130
"""
131
Initialize with random wait range.
132
133
Parameters:
134
- min: Minimum wait time (inclusive)
135
- max: Maximum wait time (inclusive)
136
"""
137
```
138
139
### Usage Examples
140
141
```python { .api }
142
# Random wait between 0 and 1 second (default)
143
@retry(wait=wait_random())
144
def random_delay_default():
145
pass
146
147
# Random wait between 1 and 5 seconds
148
@retry(wait=wait_random(min=1, max=5))
149
def random_delay_range():
150
pass
151
```
152
153
## Incremental Wait Strategies
154
155
### wait_incrementing
156
157
```python { .api }
158
from tenacity import wait_incrementing
159
from tenacity._utils import MAX_WAIT
160
161
class wait_incrementing(wait_base):
162
"""
163
Incrementally increasing wait time.
164
165
Increases wait time by fixed increment on each attempt.
166
Useful for gradually backing off without exponential growth.
167
"""
168
169
def __init__(
170
self,
171
start: time_unit_type = 0,
172
increment: time_unit_type = 100,
173
max: time_unit_type = MAX_WAIT
174
):
175
"""
176
Initialize with incremental parameters.
177
178
Parameters:
179
- start: Starting wait time for first retry
180
- increment: Amount to increase wait time each attempt
181
- max: Maximum wait time (caps the increment)
182
"""
183
```
184
185
### Usage Examples
186
187
```python { .api }
188
# Start at 1 second, increment by 2 seconds each attempt
189
@retry(wait=wait_incrementing(start=1, increment=2))
190
def incremental_backoff():
191
pass
192
# Attempt 1: wait 1s
193
# Attempt 2: wait 3s
194
# Attempt 3: wait 5s
195
# etc.
196
197
# Capped incremental backoff
198
@retry(wait=wait_incrementing(start=0, increment=5, max=30))
199
def capped_incremental():
200
pass
201
```
202
203
## Exponential Backoff Strategies
204
205
### wait_exponential
206
207
```python { .api }
208
from tenacity import wait_exponential
209
210
class wait_exponential(wait_base):
211
"""
212
Exponential backoff with fixed intervals.
213
214
Classic exponential backoff algorithm. Wait time grows
215
exponentially: multiplier * (exp_base ^ (attempt_number - 1))
216
"""
217
218
def __init__(
219
self,
220
multiplier: Union[int, float] = 1,
221
max: time_unit_type = MAX_WAIT,
222
exp_base: Union[int, float] = 2,
223
min: time_unit_type = 0
224
):
225
"""
226
Initialize exponential backoff parameters.
227
228
Parameters:
229
- multiplier: Multiplier for exponential calculation
230
- max: Maximum wait time (caps exponential growth)
231
- exp_base: Base for exponent calculation (default 2)
232
- min: Minimum wait time (floor for calculation)
233
"""
234
```
235
236
### wait_random_exponential (Full Jitter)
237
238
```python { .api }
239
from tenacity import wait_random_exponential, wait_full_jitter
240
241
class wait_random_exponential(wait_exponential):
242
"""
243
Exponential backoff with random jitter (Full Jitter algorithm).
244
245
Implements AWS's Full Jitter algorithm: random(0, exponential_result).
246
Prevents thundering herd while maintaining exponential backoff benefits.
247
"""
248
249
def __init__(
250
self,
251
multiplier: Union[int, float] = 1,
252
max: time_unit_type = MAX_WAIT,
253
exp_base: Union[int, float] = 2,
254
min: time_unit_type = 0
255
):
256
"""Same parameters as wait_exponential, but with random jitter applied."""
257
258
# Alias for wait_random_exponential
259
wait_full_jitter = wait_random_exponential
260
```
261
262
### wait_exponential_jitter
263
264
```python { .api }
265
from tenacity import wait_exponential_jitter
266
267
class wait_exponential_jitter(wait_base):
268
"""
269
Exponential backoff with configurable jitter.
270
271
More flexible jitter implementation allowing custom jitter amounts.
272
Formula: min(max, exponential_result + random(-jitter, jitter))
273
"""
274
275
def __init__(
276
self,
277
initial: float = 1,
278
max: float = MAX_WAIT,
279
exp_base: float = 2,
280
jitter: float = 1
281
):
282
"""
283
Initialize exponential backoff with jitter.
284
285
Parameters:
286
- initial: Initial wait time
287
- max: Maximum wait time
288
- exp_base: Base for exponential calculation
289
- jitter: Maximum jitter amount (±jitter seconds)
290
"""
291
```
292
293
### Exponential Usage Examples
294
295
```python { .api }
296
# Standard exponential backoff: 2, 4, 8, 16 seconds...
297
@retry(wait=wait_exponential(multiplier=2, min=2, max=60))
298
def exponential_api_call():
299
pass
300
301
# Exponential with full jitter (recommended for distributed systems)
302
@retry(wait=wait_random_exponential(multiplier=1, min=4, max=10))
303
def jittered_api_call():
304
pass
305
306
# Custom exponential with base 3
307
@retry(wait=wait_exponential(multiplier=1, exp_base=3, max=100))
308
def base3_exponential():
309
pass
310
311
# Exponential with configurable jitter
312
@retry(wait=wait_exponential_jitter(initial=1, jitter=0.5, max=30))
313
def custom_jitter():
314
pass
315
```
316
317
## Strategy Combination
318
319
### wait_combine
320
321
```python { .api }
322
from tenacity import wait_combine
323
324
class wait_combine(wait_base):
325
"""
326
Sum multiple wait strategies.
327
328
Adds together the wait times from multiple strategies.
329
Useful for combining fixed base delays with variable components.
330
"""
331
332
def __init__(self, *strategies: wait_base):
333
"""
334
Initialize with strategies to combine.
335
336
Parameters:
337
- *strategies: Variable number of wait strategies to sum
338
"""
339
```
340
341
### wait_chain
342
343
```python { .api }
344
from tenacity import wait_chain
345
346
class wait_chain(wait_base):
347
"""
348
Chain wait strategies in sequence.
349
350
Uses each strategy in turn, then repeats the last strategy.
351
Allows different wait patterns for early vs. later attempts.
352
"""
353
354
def __init__(self, *strategies: wait_base):
355
"""
356
Initialize with strategies to chain.
357
358
Parameters:
359
- *strategies: Wait strategies to use in sequence
360
"""
361
```
362
363
### Combination Examples
364
365
```python { .api }
366
# Combine fixed delay + random jitter
367
combined_wait = wait_combine(
368
wait_fixed(3), # Base 3 second delay
369
wait_random(0, 2) # Plus 0-2 seconds random
370
)
371
372
@retry(wait=combined_wait)
373
def combined_strategy():
374
pass
375
376
# Chain different strategies for different phases
377
chained_wait = wait_chain(
378
wait_fixed(1), # First few attempts: 1 second
379
wait_fixed(5), # Next attempts: 5 seconds
380
wait_exponential(multiplier=2) # Final attempts: exponential
381
)
382
383
@retry(wait=chained_wait)
384
def phased_backoff():
385
pass
386
```
387
388
### Arithmetic Operations
389
390
```python { .api }
391
# Use + operator to combine strategies
392
base_delay = wait_fixed(2)
393
jitter = wait_random(0, 1)
394
combined = base_delay + jitter
395
396
# Chain multiple additions
397
complex_wait = wait_fixed(1) + wait_random(0, 2) + wait_exponential(multiplier=0.5)
398
399
@retry(wait=complex_wait)
400
def arithmetic_combination():
401
pass
402
403
# Use sum() for multiple strategies
404
strategies = [wait_fixed(1), wait_random(0, 1), wait_exponential(multiplier=0.5)]
405
summed_wait = sum(strategies)
406
```
407
408
## Advanced Usage Patterns
409
410
### AWS-Style Exponential Backoff
411
412
```python { .api }
413
# AWS SDK-style backoff with full jitter
414
@retry(
415
wait=wait_random_exponential(multiplier=1, max=20),
416
stop=stop_after_attempt(10),
417
retry=retry_if_exception_type((ConnectionError, TimeoutError))
418
)
419
def aws_api_call():
420
pass
421
```
422
423
### Database Connection Retry
424
425
```python { .api }
426
# Conservative database retry with incremental backoff
427
@retry(
428
wait=wait_incrementing(start=2, increment=2, max=30),
429
stop=stop_after_attempt(5),
430
retry=retry_if_exception_type(ConnectionError)
431
)
432
def connect_to_db():
433
pass
434
```
435
436
### Web Scraping with Politeness
437
438
```python { .api }
439
# Polite web scraping with randomized delays
440
@retry(
441
wait=wait_combine(
442
wait_fixed(1), # Minimum 1 second between requests
443
wait_random(0, 3) # Plus 0-3 seconds random delay
444
),
445
stop=stop_after_attempt(3),
446
retry=retry_if_exception_type((requests.ConnectionError, requests.Timeout))
447
)
448
def scrape_webpage():
449
pass
450
```
451
452
### Circuit Breaker Integration
453
454
```python { .api }
455
# Exponential backoff with circuit breaker timing
456
@retry(
457
wait=wait_chain(
458
wait_fixed(0.1), # Fast initial retries
459
wait_exponential(multiplier=1, min=1, max=60) # Then exponential
460
),
461
stop=stop_after_delay(300), # 5 minute circuit open time
462
retry=retry_if_exception_type(ServiceUnavailableError)
463
)
464
def circuit_breaker_call():
465
pass
466
```
467
468
### Custom Wait Strategy
469
470
```python { .api }
471
# Custom fibonacci-style backoff
472
class wait_fibonacci(wait_base):
473
def __init__(self, max_wait=MAX_WAIT):
474
self.max_wait = max_wait
475
476
def __call__(self, retry_state):
477
attempt = retry_state.attempt_number
478
if attempt == 1:
479
return min(1, self.max_wait)
480
elif attempt == 2:
481
return min(1, self.max_wait)
482
else:
483
# Simplified fibonacci: fib(n) = fib(n-1) + fib(n-2)
484
a, b = 1, 1
485
for _ in range(attempt - 2):
486
a, b = b, a + b
487
return min(b, self.max_wait)
488
489
@retry(wait=wait_fibonacci(max_wait=60))
490
def fibonacci_backoff():
491
pass
492
```
493
494
### Time-Based Dynamic Adjustment
495
496
```python { .api }
497
# Dynamic wait based on time of day (lighter load during off-hours)
498
class wait_time_aware(wait_base):
499
def __call__(self, retry_state):
500
import datetime
501
hour = datetime.datetime.now().hour
502
503
# Longer waits during business hours (9 AM - 5 PM)
504
if 9 <= hour <= 17:
505
base_wait = 10
506
else:
507
base_wait = 2
508
509
# Still apply exponential backoff
510
multiplier = 2 ** (retry_state.attempt_number - 1)
511
return min(base_wait * multiplier, 300) # Max 5 minutes
512
513
@retry(wait=wait_time_aware())
514
def time_sensitive_operation():
515
pass
516
```
517
518
### Performance Optimization
519
520
```python { .api }
521
# Efficient wait for high-frequency retries
522
@retry(
523
wait=wait_none(), # No delay for immediate retries
524
stop=stop_after_attempt(3),
525
retry=retry_if_exception_type(TransientError)
526
)
527
def high_frequency_operation():
528
pass
529
530
# Bounded exponential for long-running services
531
@retry(
532
wait=wait_exponential(multiplier=1, min=1, max=60), # Cap at 1 minute
533
stop=stop_never, # Retry indefinitely
534
retry=retry_if_exception_type(RecoverableError)
535
)
536
def service_operation():
537
pass
538
```
539
540
### Testing Wait Strategies
541
542
```python { .api }
543
# Fast waits for testing
544
@retry(
545
wait=wait_fixed(0.1), # 100ms for fast tests
546
stop=stop_after_attempt(3)
547
)
548
def test_operation():
549
pass
550
551
# Zero wait for unit tests
552
@retry(
553
wait=wait_none(), # No delay in tests
554
stop=stop_after_attempt(2)
555
)
556
def unit_test_function():
557
pass
558
```
559
560
## Constants and Utilities
561
562
```python { .api }
563
from tenacity._utils import MAX_WAIT, time_unit_type, to_seconds
564
565
# Maximum wait time constant
566
MAX_WAIT: float # sys.maxsize / 2
567
568
# Type for time specifications
569
time_unit_type = Union[int, float, timedelta]
570
571
# Convert time units to seconds
572
def to_seconds(time_unit: time_unit_type) -> float:
573
"""Convert int/float/timedelta to seconds."""
574
```
575
576
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.