or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

async-programming-patterns.mdauthentication-and-credentials.mdconfiguration-and-settings.mddistributed-tracing-and-diagnostics.mderror-handling-and-exceptions.mdhttp-pipeline-and-policies.mdindex.mdpaging-and-result-iteration.mdpolling-and-long-running-operations.mdrest-api-abstraction.mdtransport-and-networking.mdutilities-and-helpers.md

polling-and-long-running-operations.mddocs/

0

# Polling and Long-Running Operations

1

2

Azure Core provides polling mechanisms for handling Azure long-running operations (LROs) with customizable polling strategies, automatic status checking, and comprehensive async support. This enables consistent handling of operations that may take minutes or hours to complete.

3

4

## Core Polling Classes

5

6

Base classes for implementing polling functionality for both synchronous and asynchronous operations.

7

8

```python { .api }

9

from azure.core.polling import LROPoller, AsyncLROPoller, PollingMethod, AsyncPollingMethod

10

from typing import Optional, Any, Callable

11

12

class LROPoller:

13

"""Synchronous long-running operation poller."""

14

def __init__(

15

self,

16

client: Any,

17

initial_response: Any,

18

deserialization_callback: Callable,

19

polling_method: PollingMethod,

20

**kwargs

21

): ...

22

23

def result(self, timeout: Optional[float] = None) -> Any:

24

"""Return the result of the long-running operation."""

25

...

26

27

def wait(self, timeout: Optional[float] = None) -> None:

28

"""Wait for the operation to complete."""

29

...

30

31

def done(self) -> bool:

32

"""Check if the operation has completed."""

33

...

34

35

def status(self) -> str:

36

"""Get the current status of the operation."""

37

...

38

39

def polling_method(self) -> PollingMethod:

40

"""Get the polling method being used."""

41

...

42

43

def continuation_token(self) -> str:

44

"""Get continuation token for resuming the operation."""

45

...

46

47

class AsyncLROPoller:

48

"""Asynchronous long-running operation poller."""

49

def __init__(

50

self,

51

client: Any,

52

initial_response: Any,

53

deserialization_callback: Callable,

54

polling_method: AsyncPollingMethod,

55

**kwargs

56

): ...

57

58

async def result(self, timeout: Optional[float] = None) -> Any:

59

"""Return the result of the long-running operation."""

60

...

61

62

async def wait(self, timeout: Optional[float] = None) -> None:

63

"""Wait for the operation to complete."""

64

...

65

66

def done(self) -> bool:

67

"""Check if the operation has completed."""

68

...

69

70

def status(self) -> str:

71

"""Get the current status of the operation."""

72

...

73

74

def polling_method(self) -> AsyncPollingMethod:

75

"""Get the polling method being used."""

76

...

77

78

def continuation_token(self) -> str:

79

"""Get continuation token for resuming the operation."""

80

...

81

```

82

83

## Polling Method Interfaces

84

85

Base interfaces for implementing custom polling strategies.

86

87

```python { .api }

88

from abc import ABC, abstractmethod

89

90

class PollingMethod(ABC):

91

"""Base synchronous polling method interface."""

92

@abstractmethod

93

def initialize(self, client: Any, initial_response: Any, deserialization_callback: Callable) -> None:

94

"""Initialize the polling method."""

95

...

96

97

@abstractmethod

98

def run(self) -> None:

99

"""Execute polling until completion."""

100

...

101

102

@abstractmethod

103

def status(self) -> str:

104

"""Get current operation status."""

105

...

106

107

@abstractmethod

108

def finished(self) -> bool:

109

"""Check if operation is finished."""

110

...

111

112

@abstractmethod

113

def resource(self) -> Any:

114

"""Get the final resource."""

115

...

116

117

class AsyncPollingMethod(ABC):

118

"""Base asynchronous polling method interface."""

119

@abstractmethod

120

async def initialize(self, client: Any, initial_response: Any, deserialization_callback: Callable) -> None:

121

"""Initialize the polling method."""

122

...

123

124

@abstractmethod

125

async def run(self) -> None:

126

"""Execute polling until completion."""

127

...

128

129

@abstractmethod

130

def status(self) -> str:

131

"""Get current operation status."""

132

...

133

134

@abstractmethod

135

def finished(self) -> bool:

136

"""Check if operation is finished."""

137

...

138

139

@abstractmethod

140

def resource(self) -> Any:

141

"""Get the final resource."""

142

...

143

```

144

145

## Capabilities

146

147

### No-Operation Polling

148

149

For operations that complete immediately or don't require polling.

150

151

```python { .api }

152

from azure.core.polling import NoPolling, AsyncNoPolling

153

154

class NoPolling(PollingMethod):

155

"""No-op polling implementation for immediate operations."""

156

def __init__(self): ...

157

158

class AsyncNoPolling(AsyncPollingMethod):

159

"""Async no-op polling implementation for immediate operations."""

160

def __init__(self): ...

161

```

162

163

### Async Poller Utilities

164

165

Utility functions for working with async pollers.

166

167

```python { .api }

168

from azure.core.polling import async_poller

169

170

def async_poller(func):

171

"""Decorator for creating async pollers from sync polling methods."""

172

...

173

```

174

175

## Usage Examples

176

177

### Basic Synchronous Polling

178

179

```python

180

from azure.core.polling import LROPoller

181

from azure.core import PipelineClient

182

import time

183

184

def start_long_running_operation():

185

"""Example of starting and polling a long-running operation."""

186

client = PipelineClient(base_url="https://api.example.com")

187

188

# Start the operation (this would be service-specific)

189

initial_response = client.send_request("POST", "/operations/create")

190

191

# Create poller (in real usage, this would be created by the service client)

192

# poller = LROPoller(client, initial_response, deserialize_callback, polling_method)

193

194

# Wait for completion

195

# result = poller.result(timeout=300) # 5 minute timeout

196

# print(f"Operation completed with result: {result}")

197

198

return initial_response

199

200

def check_operation_status(poller):

201

"""Check the status of an ongoing operation."""

202

print(f"Current status: {poller.status()}")

203

print(f"Is done: {poller.done()}")

204

205

if not poller.done():

206

print("Operation still in progress...")

207

# Could wait more or check later

208

poller.wait(timeout=60) # Wait up to 1 minute

209

210

if poller.done():

211

result = poller.result()

212

print(f"Operation completed: {result}")

213

```

214

215

### Asynchronous Polling

216

217

```python

218

import asyncio

219

from azure.core.polling import AsyncLROPoller

220

221

async def start_async_long_running_operation():

222

"""Example of async long-running operation polling."""

223

# client = AsyncPipelineClient(base_url="https://api.example.com")

224

225

# Start the operation

226

# initial_response = await client.send_request("POST", "/operations/create")

227

228

# Create async poller

229

# poller = AsyncLROPoller(client, initial_response, deserialize_callback, async_polling_method)

230

231

# Wait for completion asynchronously

232

# result = await poller.result(timeout=300)

233

# print(f"Async operation completed: {result}")

234

235

pass

236

237

async def monitor_operation_progress(poller):

238

"""Monitor operation progress with periodic status checks."""

239

while not poller.done():

240

print(f"Status: {poller.status()}")

241

await asyncio.sleep(5) # Check every 5 seconds

242

243

result = await poller.result()

244

print(f"Operation completed: {result}")

245

246

# asyncio.run(start_async_long_running_operation())

247

```

248

249

### Resuming Operations with Continuation Tokens

250

251

```python

252

def resume_operation_from_token(continuation_token):

253

"""Resume a long-running operation using a continuation token."""

254

# In real usage, you would recreate the poller from the token

255

# poller = service_client.resume_operation(continuation_token)

256

257

# Check current status

258

# print(f"Resumed operation status: {poller.status()}")

259

260

# Continue waiting for completion

261

# result = poller.result()

262

# return result

263

264

pass

265

266

def save_operation_state(poller):

267

"""Save operation state for later resumption."""

268

if not poller.done():

269

token = poller.continuation_token()

270

# Save token to database or file for later use

271

print(f"Operation token saved: {token}")

272

return token

273

return None

274

```

275

276

### Error Handling with Polling

277

278

```python

279

from azure.core.exceptions import AzureError, HttpResponseError

280

281

def poll_with_error_handling(poller):

282

"""Handle errors during polling operations."""

283

try:

284

# Wait for operation with timeout

285

result = poller.result(timeout=600) # 10 minute timeout

286

print(f"Operation succeeded: {result}")

287

return result

288

289

except HttpResponseError as e:

290

if e.status_code == 404:

291

print("Operation not found - may have been cancelled")

292

elif e.status_code >= 500:

293

print(f"Server error during operation: {e.status_code}")

294

else:

295

print(f"HTTP error: {e.status_code} - {e.reason}")

296

raise

297

298

except AzureError as e:

299

print(f"Azure service error: {e.message}")

300

301

# Check if operation can be resumed

302

if hasattr(e, 'continuation_token') and e.continuation_token:

303

print("Operation can be resumed with token:", e.continuation_token)

304

raise

305

306

except TimeoutError:

307

print("Operation timed out")

308

309

# Save state for later resumption

310

if not poller.done():

311

token = poller.continuation_token()

312

print(f"Save this token to resume later: {token}")

313

raise

314

```

315

316

### Custom Polling Strategy

317

318

```python

319

from azure.core.polling import PollingMethod

320

import time

321

322

class CustomPollingMethod(PollingMethod):

323

"""Custom polling method with exponential backoff."""

324

325

def __init__(self, initial_delay=1, max_delay=60, backoff_factor=2):

326

self.initial_delay = initial_delay

327

self.max_delay = max_delay

328

self.backoff_factor = backoff_factor

329

self.current_delay = initial_delay

330

self._client = None

331

self._operation_url = None

332

self._finished = False

333

self._status = "InProgress"

334

self._resource = None

335

336

def initialize(self, client, initial_response, deserialization_callback):

337

self._client = client

338

self._deserialize = deserialization_callback

339

340

# Extract operation URL from initial response

341

if hasattr(initial_response, 'headers'):

342

self._operation_url = initial_response.headers.get('Location')

343

344

# Check if already completed

345

if initial_response.status_code in [200, 201]:

346

self._finished = True

347

self._status = "Succeeded"

348

self._resource = self._deserialize(initial_response)

349

350

def run(self):

351

while not self._finished:

352

time.sleep(self.current_delay)

353

354

# Poll the operation status

355

response = self._client.send_request("GET", self._operation_url)

356

357

if response.status_code == 200:

358

# Operation completed successfully

359

self._finished = True

360

self._status = "Succeeded"

361

self._resource = self._deserialize(response)

362

elif response.status_code == 202:

363

# Still in progress

364

self._status = "InProgress"

365

366

# Increase delay for next poll (exponential backoff)

367

self.current_delay = min(

368

self.current_delay * self.backoff_factor,

369

self.max_delay

370

)

371

else:

372

# Error occurred

373

self._finished = True

374

self._status = "Failed"

375

raise HttpResponseError(f"Operation failed: {response.status_code}")

376

377

def status(self):

378

return self._status

379

380

def finished(self):

381

return self._finished

382

383

def resource(self):

384

return self._resource

385

386

# Usage

387

def use_custom_polling():

388

custom_method = CustomPollingMethod(initial_delay=2, max_delay=30)

389

# poller = LROPoller(client, initial_response, deserialize_func, custom_method)

390

# result = poller.result()

391

```

392

393

### Async Custom Polling

394

395

```python

396

import asyncio

397

from azure.core.polling import AsyncPollingMethod

398

399

class AsyncCustomPollingMethod(AsyncPollingMethod):

400

"""Custom async polling method."""

401

402

def __init__(self, initial_delay=1, max_delay=60):

403

self.initial_delay = initial_delay

404

self.max_delay = max_delay

405

self.current_delay = initial_delay

406

self._client = None

407

self._operation_url = None

408

self._finished = False

409

self._status = "InProgress"

410

self._resource = None

411

412

async def initialize(self, client, initial_response, deserialization_callback):

413

self._client = client

414

self._deserialize = deserialization_callback

415

416

if hasattr(initial_response, 'headers'):

417

self._operation_url = initial_response.headers.get('Location')

418

419

if initial_response.status_code in [200, 201]:

420

self._finished = True

421

self._status = "Succeeded"

422

self._resource = self._deserialize(initial_response)

423

424

async def run(self):

425

while not self._finished:

426

await asyncio.sleep(self.current_delay)

427

428

response = await self._client.send_request("GET", self._operation_url)

429

430

if response.status_code == 200:

431

self._finished = True

432

self._status = "Succeeded"

433

self._resource = self._deserialize(response)

434

elif response.status_code == 202:

435

self._status = "InProgress"

436

self.current_delay = min(self.current_delay * 2, self.max_delay)

437

else:

438

self._finished = True

439

self._status = "Failed"

440

raise HttpResponseError(f"Operation failed: {response.status_code}")

441

442

def status(self):

443

return self._status

444

445

def finished(self):

446

return self._finished

447

448

def resource(self):

449

return self._resource

450

```

451

452

## Best Practices

453

454

### Polling Configuration

455

456

- Set appropriate timeouts based on expected operation duration

457

- Use exponential backoff to reduce server load

458

- Implement maximum retry limits to prevent infinite polling

459

- Consider operation complexity when setting initial polling intervals

460

461

### Resource Management

462

463

- Always use timeouts to prevent infinite waiting

464

- Save continuation tokens for long-running operations

465

- Properly handle poller cleanup in exception scenarios

466

- Use async pollers with async code for better resource utilization

467

468

### Error Handling

469

470

- Handle different types of errors appropriately (network, service, timeout)

471

- Implement retry logic for transient failures during polling

472

- Provide meaningful error messages to users

473

- Log polling progress and errors for debugging

474

475

### Performance Considerations

476

477

- Use appropriate polling intervals to balance responsiveness and server load

478

- Consider using webhooks or server-sent events for real-time updates when available

479

- Batch multiple operations when possible to reduce polling overhead

480

- Monitor polling patterns to optimize intervals for your specific use case