aws-durable-execution-sdk-python 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. aws_durable_execution_sdk_python/.gitignore +0 -0
  2. aws_durable_execution_sdk_python/__about__.py +4 -0
  3. aws_durable_execution_sdk_python/__init__.py +34 -0
  4. aws_durable_execution_sdk_python/botocore/data/lambdainternal/2015-03-31/service-2.json +7864 -0
  5. aws_durable_execution_sdk_python/concurrency/__init__.py +0 -0
  6. aws_durable_execution_sdk_python/concurrency/executor.py +436 -0
  7. aws_durable_execution_sdk_python/concurrency/models.py +469 -0
  8. aws_durable_execution_sdk_python/config.py +499 -0
  9. aws_durable_execution_sdk_python/context.py +551 -0
  10. aws_durable_execution_sdk_python/exceptions.py +374 -0
  11. aws_durable_execution_sdk_python/execution.py +428 -0
  12. aws_durable_execution_sdk_python/identifier.py +14 -0
  13. aws_durable_execution_sdk_python/lambda_service.py +1034 -0
  14. aws_durable_execution_sdk_python/logger.py +131 -0
  15. aws_durable_execution_sdk_python/operation/__init__.py +1 -0
  16. aws_durable_execution_sdk_python/operation/callback.py +123 -0
  17. aws_durable_execution_sdk_python/operation/child.py +162 -0
  18. aws_durable_execution_sdk_python/operation/invoke.py +119 -0
  19. aws_durable_execution_sdk_python/operation/map.py +137 -0
  20. aws_durable_execution_sdk_python/operation/parallel.py +122 -0
  21. aws_durable_execution_sdk_python/operation/step.py +269 -0
  22. aws_durable_execution_sdk_python/operation/wait.py +53 -0
  23. aws_durable_execution_sdk_python/operation/wait_for_condition.py +235 -0
  24. aws_durable_execution_sdk_python/py.typed +1 -0
  25. aws_durable_execution_sdk_python/retries.py +174 -0
  26. aws_durable_execution_sdk_python/serdes.py +502 -0
  27. aws_durable_execution_sdk_python/state.py +790 -0
  28. aws_durable_execution_sdk_python/suspend.py +84 -0
  29. aws_durable_execution_sdk_python/threading.py +222 -0
  30. aws_durable_execution_sdk_python/types.py +180 -0
  31. aws_durable_execution_sdk_python/waits.py +130 -0
  32. aws_durable_execution_sdk_python-1.0.0.dist-info/METADATA +679 -0
  33. aws_durable_execution_sdk_python-1.0.0.dist-info/RECORD +36 -0
  34. aws_durable_execution_sdk_python-1.0.0.dist-info/WHEEL +4 -0
  35. aws_durable_execution_sdk_python-1.0.0.dist-info/licenses/LICENSE +175 -0
  36. aws_durable_execution_sdk_python-1.0.0.dist-info/licenses/NOTICE +1 -0
@@ -0,0 +1,679 @@
1
+ Metadata-Version: 2.4
2
+ Name: aws-durable-execution-sdk-python
3
+ Version: 1.0.0
4
+ Summary: This the Python SDK for AWS Lambda Durable Functions.
5
+ Project-URL: Documentation, https://github.com/aws/aws-durable-execution-sdk-python#readme
6
+ Project-URL: Issues, https://github.com/aws/aws-durable-execution-sdk-python/issues
7
+ Project-URL: Source, https://github.com/aws/aws-durable-execution-sdk-python
8
+ Author-email: yaythomas <tgaigher@amazon.com>
9
+ License-Expression: Apache-2.0
10
+ License-File: LICENSE
11
+ License-File: NOTICE
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Programming Language :: Python
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Programming Language :: Python :: Implementation :: CPython
19
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: boto3>=1.40.30
22
+ Description-Content-Type: text/markdown
23
+
24
+ # AWS Durable Execution SDK for Python
25
+
26
+ [![PyPI - Version](https://img.shields.io/pypi/v/aws-durable-execution-sdk-python.svg)](https://pypi.org/project/aws-durable-execution-sdk-python)
27
+ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/aws-durable-execution-sdk-python.svg)](https://pypi.org/project/aws-durable-execution-sdk-python)
28
+
29
+ -----
30
+
31
+ ## Table of Contents
32
+
33
+ - [Installation](#installation)
34
+ - [License](#license)
35
+
36
+ ## Installation
37
+
38
+ ```console
39
+ pip install aws-durable-execution-sdk-python
40
+ ```
41
+
42
+ ## Developers
43
+ Please see [CONTRIBUTING.md](CONTRIBUTING.md). It contains the testing guide, sample commands and instructions
44
+ for how to contribute to this package.
45
+
46
+ tldr; use `hatch` and it will manage virtual envs and dependencies for you, so you don't have to do it manually.
47
+
48
+ ## Core Architecture
49
+ The entry-point that consumers of the SDK interact with is the DurableContext.
50
+
51
+ ### DurableContext Operations
52
+ - **Core Methods**: `set_logger`, `step`, `invoke`, `map`, `parallel`, `run_in_child_context`, `wait`, `create_callback`, `wait_for_callback`, `wait_for_condition`
53
+ - **Thread Safety**: Uses `OrderedCounter` for generating sequential step IDs
54
+ - **State Management**: Delegates to `ExecutionState` for checkpointing
55
+
56
+ ### Concurrency Implementation
57
+ - **Map/Parallel**: Both inherit from `ConcurrentExecutor` abstract base class
58
+ - **Thread Pool**: Uses `ThreadPoolExecutor` for concurrent execution
59
+ - **State Tracking**: `ExecutableWithState` manages individual task lifecycle
60
+ - **Completion Logic**: `ExecutionCounters` tracks success/failure criteria
61
+ - **Suspension**: `TimerScheduler` handles timed suspensions and resumptions
62
+
63
+ ### Configuration System
64
+ - **Modular Configs**: Separate config classes for each operation type
65
+ - **Completion Control**: `CompletionConfig` defines success/failure criteria
66
+ - **Serialization**: `SerDes` interface for custom serialization
67
+
68
+ ### Operation Handlers
69
+ - **Separation of Concerns**: Each operation has dedicated handler function
70
+ - **Checkpointing**: All operations integrate with execution state checkpointing
71
+ - **Error Handling**: Consistent error handling and retry logic across operations
72
+
73
+
74
+ ```mermaid
75
+ classDiagram
76
+ class DurableContext {
77
+ -ExecutionState state
78
+ -Any lambda_context
79
+ -str _parent_id
80
+ -OrderedCounter _step_counter
81
+ -LogInfo _log_info
82
+ -Logger logger
83
+
84
+ +set_logger(LoggerInterface new_logger)
85
+ +step(Callable func, str name, StepConfig config) T
86
+ +invoke(str function_name, P payload, str name, InvokeConfig config) R
87
+ +map(Sequence inputs, Callable func, str name, MapConfig config) BatchResult
88
+ +parallel(Sequence functions, str name, ParallelConfig config) BatchResult
89
+ +run_in_child_context(Callable func, str name, ChildConfig config) T
90
+ +wait(int seconds, str name)
91
+ +create_callback(str name, CallbackConfig config) Callback
92
+ +wait_for_callback(Callable submitter, str name, WaitForCallbackConfig config) Any
93
+ +wait_for_condition(Callable check, WaitForConditionConfig config, str name) T
94
+ }
95
+
96
+ class DurableContextProtocol {
97
+ <<interface>>
98
+ +step(Callable func, str name, StepConfig config) T
99
+ +run_in_child_context(Callable func, str name, ChildConfig config) T
100
+ +map(Sequence inputs, Callable func, str name, MapConfig config) BatchResult
101
+ +parallel(Sequence functions, str name, ParallelConfig config) BatchResult
102
+ +wait(int seconds, str name)
103
+ +create_callback(str name, CallbackConfig config) Callback
104
+ }
105
+
106
+ class OrderedCounter {
107
+ -OrderedLock _lock
108
+ -int _counter
109
+ +increment() int
110
+ +decrement() int
111
+ +get_current() int
112
+ }
113
+
114
+ class ExecutionState {
115
+ +str durable_execution_arn
116
+ +get_checkpoint_result(str operation_id) CheckpointedResult
117
+ +create_checkpoint(OperationUpdate operation_update)
118
+ }
119
+
120
+ class Logger {
121
+ +LoggerInterface logger
122
+ +LogInfo info
123
+ +with_log_info(LogInfo info) Logger
124
+ +from_log_info(LoggerInterface logger, LogInfo info) Logger
125
+ }
126
+
127
+ DurableContext ..|> DurableContextProtocol : implements
128
+ DurableContext --> ExecutionState : uses
129
+ DurableContext --> OrderedCounter : contains
130
+ DurableContext --> Logger : contains
131
+ ```
132
+
133
+ ## Operation Handlers
134
+ The `DurableContext` calls operation handlers, which contain the execution logic for each operation.
135
+
136
+ ```mermaid
137
+ classDiagram
138
+ class DurableContext {
139
+ +step(Callable func, str name, StepConfig config) T
140
+ +invoke(str function_name, P payload, str name, InvokeConfig config) R
141
+ +map(Sequence inputs, Callable func, str name, MapConfig config) BatchResult
142
+ +parallel(Sequence functions, str name, ParallelConfig config) BatchResult
143
+ +run_in_child_context(Callable func, str name, ChildConfig config) T
144
+ +wait(int seconds, str name)
145
+ +create_callback(str name, CallbackConfig config) Callback
146
+ +wait_for_callback(Callable submitter, str name, WaitForCallbackConfig config) Any
147
+ +wait_for_condition(Callable check, WaitForConditionConfig config, str name) T
148
+ }
149
+
150
+ class step_handler {
151
+ <<function>>
152
+ +step_handler(Callable func, ExecutionState state, OperationIdentifier op_id, StepConfig config, Logger logger) T
153
+ }
154
+
155
+ class invoke_handler {
156
+ <<function>>
157
+ +invoke_handler(str function_name, P payload, ExecutionState state, OperationIdentifier op_id, InvokeConfig config) R
158
+ }
159
+
160
+ class map_handler {
161
+ <<function>>
162
+ +map_handler(Sequence items, Callable func, MapConfig config, ExecutionState state, Callable run_in_child_context) BatchResult
163
+ }
164
+
165
+ class parallel_handler {
166
+ <<function>>
167
+ +parallel_handler(Sequence callables, ParallelConfig config, ExecutionState state, Callable run_in_child_context) BatchResult
168
+ }
169
+
170
+ class child_handler {
171
+ <<function>>
172
+ +child_handler(Callable func, ExecutionState state, OperationIdentifier op_id, ChildConfig config) T
173
+ }
174
+
175
+ class wait_handler {
176
+ <<function>>
177
+ +wait_handler(int seconds, ExecutionState state, OperationIdentifier op_id)
178
+ }
179
+
180
+ class create_callback_handler {
181
+ <<function>>
182
+ +create_callback_handler(ExecutionState state, OperationIdentifier op_id, CallbackConfig config) str
183
+ }
184
+
185
+ class wait_for_callback_handler {
186
+ <<function>>
187
+ +wait_for_callback_handler(DurableContext context, Callable submitter, str name, WaitForCallbackConfig config) Any
188
+ }
189
+
190
+ class wait_for_condition_handler {
191
+ <<function>>
192
+ +wait_for_condition_handler(Callable check, WaitForConditionConfig config, ExecutionState state, OperationIdentifier op_id, Logger logger) T
193
+ }
194
+
195
+ DurableContext --> step_handler : calls
196
+ DurableContext --> invoke_handler : calls
197
+ DurableContext --> map_handler : calls
198
+ DurableContext --> parallel_handler : calls
199
+ DurableContext --> child_handler : calls
200
+ DurableContext --> wait_handler : calls
201
+ DurableContext --> create_callback_handler : calls
202
+ DurableContext --> wait_for_callback_handler : calls
203
+ DurableContext --> wait_for_condition_handler : calls
204
+ ```
205
+
206
+ ## Configuration Module Classes
207
+
208
+ ```mermaid
209
+ classDiagram
210
+ class StepConfig {
211
+ +Callable retry_strategy
212
+ +StepSemantics step_semantics
213
+ +SerDes serdes
214
+ }
215
+
216
+ class InvokeConfig~P,R~ {
217
+ +int timeout_seconds
218
+ +SerDes~P~ serdes_payload
219
+ +SerDes~R~ serdes_result
220
+ }
221
+
222
+ class MapConfig {
223
+ +int max_concurrency
224
+ +ItemBatcher item_batcher
225
+ +CompletionConfig completion_config
226
+ +SerDes serdes
227
+ }
228
+
229
+ class ParallelConfig {
230
+ +int max_concurrency
231
+ +CompletionConfig completion_config
232
+ +SerDes serdes
233
+ }
234
+
235
+ class ChildConfig~T~ {
236
+ +SerDes serdes
237
+ +OperationSubType sub_type
238
+ +Callable~T,str~ summary_generator
239
+ }
240
+
241
+ class CallbackConfig {
242
+ +int timeout_seconds
243
+ +int heartbeat_timeout_seconds
244
+ +SerDes serdes
245
+ }
246
+
247
+ class WaitForCallbackConfig {
248
+ +Callable retry_strategy
249
+ }
250
+
251
+ class WaitForConditionConfig~T~ {
252
+ +Callable wait_strategy
253
+ +T initial_state
254
+ +SerDes serdes
255
+ }
256
+
257
+ class CompletionConfig {
258
+ +int min_successful
259
+ +int tolerated_failure_count
260
+ +float tolerated_failure_percentage
261
+ +first_successful()$ CompletionConfig
262
+ +all_completed()$ CompletionConfig
263
+ +all_successful()$ CompletionConfig
264
+ }
265
+
266
+ class ItemBatcher~T~ {
267
+ +int max_items_per_batch
268
+ +float max_item_bytes_per_batch
269
+ +T batch_input
270
+ }
271
+
272
+ WaitForCallbackConfig --|> CallbackConfig : extends
273
+ MapConfig --> CompletionConfig : contains
274
+ MapConfig --> ItemBatcher : contains
275
+ ParallelConfig --> CompletionConfig : contains
276
+ ```
277
+
278
+ ## Types and Protocols Module
279
+
280
+ ```mermaid
281
+ classDiagram
282
+ class DurableContextProtocol {
283
+ <<interface>>
284
+ +step(Callable func, str name, StepConfig config) T
285
+ +run_in_child_context(Callable func, str name, ChildConfig config) T
286
+ +map(Sequence inputs, Callable func, str name, MapConfig config) BatchResult
287
+ +parallel(Sequence functions, str name, ParallelConfig config) BatchResult
288
+ +wait(int seconds, str name)
289
+ +create_callback(str name, CallbackConfig config) Callback
290
+ }
291
+
292
+ class LoggerInterface {
293
+ <<interface>>
294
+ +debug(object msg, *args, Mapping extra)
295
+ +info(object msg, *args, Mapping extra)
296
+ +warning(object msg, *args, Mapping extra)
297
+ +error(object msg, *args, Mapping extra)
298
+ +exception(object msg, *args, Mapping extra)
299
+ }
300
+
301
+ class CallbackProtocol~C_co~ {
302
+ <<interface>>
303
+ +str callback_id
304
+ +result() C_co
305
+ }
306
+
307
+ class BatchResultProtocol~T~ {
308
+ <<interface>>
309
+ +get_results() list~T~
310
+ }
311
+
312
+ class StepContext {
313
+ +LoggerInterface logger
314
+ }
315
+
316
+ class WaitForConditionCheckContext {
317
+ +LoggerInterface logger
318
+ }
319
+
320
+ class OperationContext {
321
+ +LoggerInterface logger
322
+ }
323
+
324
+ StepContext --|> OperationContext : extends
325
+ WaitForConditionCheckContext --|> OperationContext : extends
326
+ ```
327
+
328
+ ## SerDes Module Classes
329
+
330
+ ```mermaid
331
+ classDiagram
332
+ class SerDes~T~ {
333
+ <<abstract>>
334
+ +serialize(T value, SerDesContext context) str
335
+ +deserialize(str data, SerDesContext context) T
336
+ }
337
+
338
+ class JsonSerDes~T~ {
339
+ +serialize(T value, SerDesContext context) str
340
+ +deserialize(str data, SerDesContext context) T
341
+ }
342
+
343
+ class SerDesContext {
344
+ +str operation_id
345
+ +str durable_execution_arn
346
+ }
347
+
348
+ class serialize {
349
+ <<function>>
350
+ +serialize(SerDes serdes, T value, str operation_id, str durable_execution_arn) str
351
+ }
352
+
353
+ class deserialize {
354
+ <<function>>
355
+ +deserialize(SerDes serdes, str data, str operation_id, str durable_execution_arn) T
356
+ }
357
+
358
+ JsonSerDes ..|> SerDes : implements
359
+ serialize --> SerDes : uses
360
+ deserialize --> SerDes : uses
361
+ SerDes --> SerDesContext : uses
362
+ ```
363
+
364
+ ## Concurrency Architecture - Map and Parallel Operations
365
+
366
+ ```mermaid
367
+ classDiagram
368
+ class ConcurrentExecutor~CallableType,ResultType~ {
369
+ <<abstract>>
370
+ +list~Executable~ executables
371
+ +int max_concurrency
372
+ +CompletionConfig completion_config
373
+ +ExecutionCounters counters
374
+ +list~ExecutableWithState~ executables_with_state
375
+ +Event _completion_event
376
+ +SuspendExecution _suspend_exception
377
+
378
+ +execute(ExecutionState state, Callable run_in_child_context) BatchResult~ResultType~
379
+ +execute_item(DurableContext child_context, Executable executable)* ResultType
380
+ +should_execution_suspend() SuspendResult
381
+ -_on_task_complete(ExecutableWithState exe_state, Future future, TimerScheduler scheduler)
382
+ -_create_result() BatchResult~ResultType~
383
+ }
384
+
385
+ class MapExecutor~T,R~ {
386
+ +Sequence~T~ items
387
+ +execute_item(DurableContext child_context, Executable executable) R
388
+ +from_items(Sequence items, Callable func, MapConfig config)$ MapExecutor
389
+ }
390
+
391
+ class ParallelExecutor {
392
+ +execute_item(DurableContext child_context, Executable executable) R
393
+ +from_callables(Sequence callables, ParallelConfig config)$ ParallelExecutor
394
+ }
395
+
396
+ class Executable~CallableType~ {
397
+ +int index
398
+ +CallableType func
399
+ }
400
+
401
+ class ExecutableWithState~CallableType,ResultType~ {
402
+ +Executable~CallableType~ executable
403
+ -BranchStatus _status
404
+ -Future _future
405
+ -float _suspend_until
406
+ -ResultType _result
407
+ -Exception _error
408
+
409
+ +run(Future future)
410
+ +suspend()
411
+ +suspend_with_timeout(float timestamp)
412
+ +complete(ResultType result)
413
+ +fail(Exception error)
414
+ +reset_to_pending()
415
+ +can_resume() bool
416
+ +is_running() bool
417
+ }
418
+
419
+ class ExecutionCounters {
420
+ +int total_tasks
421
+ +int min_successful
422
+ +int success_count
423
+ +int failure_count
424
+ -Lock _lock
425
+
426
+ +complete_task()
427
+ +fail_task()
428
+ +should_complete() bool
429
+ +is_all_completed() bool
430
+ +is_min_successful_reached() bool
431
+ +is_failure_tolerance_exceeded() bool
432
+ }
433
+
434
+ class TimerScheduler {
435
+ +Callable resubmit_callback
436
+ -list _pending_resumes
437
+ -Lock _lock
438
+ -Event _shutdown
439
+ -Thread _timer_thread
440
+
441
+ +schedule_resume(ExecutableWithState exe_state, float resume_time)
442
+ +shutdown()
443
+ -_timer_loop()
444
+ }
445
+
446
+ class BatchResult~R~ {
447
+ +list~BatchItem~R~~ all
448
+ +CompletionReason completion_reason
449
+ +succeeded() list~BatchItem~R~~
450
+ +failed() list~BatchItem~R~~
451
+ +get_results() list~R~
452
+ +throw_if_error()
453
+ }
454
+
455
+ class BatchItem~R~ {
456
+ +int index
457
+ +BatchItemStatus status
458
+ +R result
459
+ +ErrorObject error
460
+ }
461
+
462
+ MapExecutor --|> ConcurrentExecutor : extends
463
+ ParallelExecutor --|> ConcurrentExecutor : extends
464
+ ConcurrentExecutor --> ExecutableWithState : manages
465
+ ConcurrentExecutor --> ExecutionCounters : uses
466
+ ConcurrentExecutor --> TimerScheduler : uses
467
+ ConcurrentExecutor --> BatchResult : creates
468
+ ExecutableWithState --> Executable : contains
469
+ BatchResult --> BatchItem : contains
470
+ ```
471
+
472
+ ## Concurrency Flow
473
+
474
+ ```mermaid
475
+ sequenceDiagram
476
+ participant DC as DurableContext
477
+ participant MH as map_handler
478
+ participant ME as MapExecutor
479
+ participant CE as ConcurrentExecutor
480
+ participant TP as ThreadPoolExecutor
481
+ participant TS as TimerScheduler
482
+ participant EC as ExecutionCounters
483
+
484
+ DC->>MH: map(inputs, func, config)
485
+ MH->>ME: MapExecutor.from_items()
486
+ ME->>CE: execute(state, run_in_child_context)
487
+
488
+ CE->>TP: ThreadPoolExecutor(max_workers)
489
+ CE->>TS: TimerScheduler(resubmitter)
490
+ CE->>EC: ExecutionCounters(total, min_successful)
491
+
492
+ loop For each executable
493
+ CE->>TP: submit_task(executable_with_state)
494
+ TP->>CE: execute_item_in_child_context()
495
+ CE->>DC: run_in_child_context(child_func)
496
+ DC->>ME: execute_item(child_context, executable)
497
+ end
498
+
499
+ par Task Completion Handling
500
+ TP->>CE: on_task_complete(future)
501
+ CE->>EC: complete_task() / fail_task()
502
+ CE->>CE: should_execution_suspend()
503
+ alt Should Complete
504
+ CE->>CE: _completion_event.set()
505
+ else Should Suspend
506
+ CE->>TS: schedule_resume(exe_state, timestamp)
507
+ end
508
+ end
509
+
510
+ CE->>CE: _completion_event.wait()
511
+ CE->>CE: _create_result()
512
+ CE->>DC: BatchResult
513
+ ```
514
+
515
+ ## Threading and Locking
516
+
517
+ ```mermaid
518
+ classDiagram
519
+ class OrderedLock {
520
+ -Lock _lock
521
+ -deque~Event~ _waiters
522
+ -bool _is_broken
523
+ -Exception _exception
524
+
525
+ +acquire() bool
526
+ +release()
527
+ +reset()
528
+ +is_broken() bool
529
+ +__enter__() OrderedLock
530
+ +__exit__(exc_type, exc_val, exc_tb)
531
+ }
532
+
533
+ class OrderedCounter {
534
+ -OrderedLock _lock
535
+ -int _counter
536
+
537
+ +increment() int
538
+ +decrement() int
539
+ +get_current() int
540
+ }
541
+
542
+ class Event {
543
+ <<threading.Event>>
544
+ +set()
545
+ +wait()
546
+ }
547
+
548
+ class Lock {
549
+ <<threading.Lock>>
550
+ +acquire()
551
+ +release()
552
+ }
553
+
554
+ OrderedCounter --> OrderedLock : uses
555
+ OrderedLock --> Lock : contains
556
+ OrderedLock --> Event : manages queue of
557
+ ```
558
+
559
+ ## Checkpointing System
560
+
561
+ The SDK invokes the AWS Lambda checkpoint API to persist execution state. Checkpoints are batched for efficiency and can be either
562
+ synchronous (blocking) or asynchronous (non-blocking). Critical checkpoints are blocking,
563
+ meaning that execution will not proceed until the checkpoint call has successfully completed.
564
+
565
+ ### Checkpoint Types
566
+
567
+ Checkpoints are categorized by their action (START, SUCCEED, FAIL) and whether they are critical to execution correctness:
568
+
569
+ | Operation Type | Action | Is Sync? | Rationale |
570
+ |---------------|--------|----------|-----------|
571
+ | Step (AtMostOncePerRetry) | START | Yes | Prevents duplicate execution - must wait for confirmation |
572
+ | Step (AtLeastOncePerRetry) | START | No | Performance optimization - idempotent operations can retry |
573
+ | Step | SUCCEED/FAIL | Yes | Ensures result persisted before returning to caller |
574
+ | Callback | START | Yes | Must wait for API to generate callback ID |
575
+ | Callback | SUCCEED/FAIL | Yes | Ensures callback result persisted |
576
+ | Invoke | START | Yes | Ensures chained invoke recorded before proceeding |
577
+ | Invoke | SUCCEED/FAIL | Yes | Ensures invoke result persisted |
578
+ | Context (Child) | START | No | Fire-and-forget for performance - parent tracks completion |
579
+ | Context (Child) | SUCCEED/FAIL | Yes | Ensures child result available to parent |
580
+ | Wait | START | No | Observability only - no blocking needed |
581
+ | Wait | SUCCEED | Yes | Ensures wait completion recorded |
582
+ | Wait for Condition | START | No | Observability only - condition check is idempotent |
583
+ | Wait for Condition | SUCCEED/FAIL | Yes | Ensures condition result persisted |
584
+ | Empty Checkpoint | N/A | Yes (default) | Refreshes checkpoint token and operations list |
585
+
586
+ ### Synchronous vs Asynchronous Checkpoints
587
+
588
+ **Synchronous Checkpoints (is_sync=True, default)**:
589
+ - Block the caller until the checkpoint is processed by the background thread
590
+ - Ensure the checkpoint is persisted before continuing execution
591
+ - Safe default for correctness
592
+ - Used for critical operations where confirmation is required
593
+
594
+ **Asynchronous Checkpoints (is_sync=False, opt-in)**:
595
+ - Return immediately without waiting for the checkpoint to complete
596
+ - Performance optimization for specific use cases
597
+ - Used for observability checkpoints and fire-and-forget operations
598
+ - Only safe when the operation is idempotent or non-critical
599
+
600
+ ### Checkpoint Batching
601
+
602
+ The SDK uses a background thread to batch multiple checkpoint operations into a single API call for efficiency. This reduces API overhead and
603
+ improves throughput.
604
+
605
+ ```mermaid
606
+ sequenceDiagram
607
+ participant MT as Main Thread
608
+ participant Q as Checkpoint Queue
609
+ participant BT as Background Thread
610
+ participant API as Durable Functions API
611
+
612
+ Note over MT,API: Synchronous Checkpoint Flow
613
+ MT->>Q: Enqueue operation + completion event
614
+ MT->>MT: Block on completion event
615
+ BT->>Q: Collect batch (up to 1 second or 750KB)
616
+ BT->>API: POST /checkpoint (batched operations)
617
+ API-->>BT: New checkpoint token + operations
618
+ BT->>BT: Update execution state
619
+ BT->>MT: Signal completion event
620
+ MT->>MT: Resume execution
621
+
622
+ Note over MT,API: Asynchronous Checkpoint Flow
623
+ MT->>Q: Enqueue operation (no event)
624
+ MT->>MT: Continue immediately
625
+ BT->>Q: Collect batch (up to 1 second or 750KB)
626
+ BT->>API: POST /checkpoint (batched operations)
627
+ API-->>BT: New checkpoint token + operations
628
+ BT->>BT: Update execution state
629
+ ```
630
+
631
+ ### Batching Configuration
632
+
633
+ Checkpoint batching is controlled by `CheckpointBatcherConfig`:
634
+
635
+ ```python
636
+ @dataclass(frozen=True)
637
+ class CheckpointBatcherConfig:
638
+ max_batch_size_bytes: int = 750 * 1024 # 750KB
639
+ max_batch_time_seconds: float = 1.0 # 1 second
640
+ max_batch_operations: int | float = float("inf") # No limit
641
+ ```
642
+
643
+ The background thread collects operations until one of these limits is reached:
644
+ 1. Batch size exceeds 750KB
645
+ 2. 1 second has elapsed since the first operation
646
+ 3. Maximum operation count is reached (unlimited by default)
647
+
648
+ ### Concurrency Management
649
+
650
+ The checkpointing system handles concurrent operations (map/parallel) by tracking parent-child relationships:
651
+
652
+ 1. When a CONTEXT operation completes (SUCCEED/FAIL), all descendant operations are marked as orphaned
653
+ 2. Orphaned operations are rejected if they attempt to checkpoint
654
+ 3. This prevents child operations from checkpointing after their parent has already completed
655
+ 4. Uses a single lock (`_parent_done_lock`) to coordinate completion and checkpoint validation
656
+
657
+ ### Error Handling
658
+
659
+ When a checkpoint fails in the background thread:
660
+
661
+ 1. **Error Signaling**: The background thread creates a `BackgroundThreadError` wrapping the original exception
662
+ 2. **Event Notification**: All completion events (both in the current batch and queued operations) are signaled with this error
663
+ 3. **Immediate Propagation**: Synchronous callers waiting on `create_checkpoint(is_sync=True)` immediately receive the `BackgroundThreadError`
664
+ 4. **Future Prevention**: A failure event (`_checkpointing_failed`) is set to prevent any future checkpoint attempts
665
+ 5. **Clean Termination**: The background thread exits cleanly after signaling all waiting operations
666
+
667
+ For **synchronous operations** (default `is_sync=True`):
668
+ - The main thread receives `BackgroundThreadError` immediately when calling `create_checkpoint()`
669
+ - This prevents further execution with corrupted state
670
+
671
+ For **asynchronous operations** (`is_sync=False`):
672
+ - The error is detected on the next synchronous checkpoint attempt
673
+ - The `_checkpointing_failed` event causes immediate failure before queuing
674
+
675
+ This ensures no code continues executing after a checkpoint failure, maintaining execution state integrity.
676
+
677
+ ## License
678
+
679
+ This project is licensed under the [Apache-2.0 License](LICENSE).