or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

async-support.mdcallbacks-hooks.mdcore-decorator.mdindex.mdretry-strategies.mdstop-conditions.mdutilities.mdwait-strategies.md

wait-strategies.mddocs/

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.