Skip to content

Commit b5fe55d

Browse files
committed
feat: limit in-flight operations in map/parallel
max_concurrency previously sized the thread pool only, so branches that suspended (invoke, wait, callbacks) released their thread and the next item started immediately. All items were initiated at once regardless of the configured limit. Rework the concurrency executor around a single coordinator loop: - The calling thread owns all branch state and schedules work; worker threads only run branches and report outcomes on a queue. No locks remain in the module. - max_concurrency now bounds in-flight branches. A suspended branch keeps its slot until it reaches a terminal state, matching the JS and Java SDKs. When every in-flight branch is suspended, the parent suspends with the earliest resume timestamp, or indefinitely. - Branches start in index order and operation ids derive from the branch index, so scheduling stays deterministic across invocations. - Timed suspends resume in-process via the event queue timeout, replacing the TimerScheduler background thread. - Consolidate completion logic (thresholds and reason inference) into CompletionPolicy, replacing ExecutionCounters and the duplicated logic in BatchResult. - Omit never-started branches from BatchResult, matching the JS SDK. - Stop scheduling when a branch is orphaned: an ancestor completed, so all further checkpoints under it would be rejected. - Record the completion decision in map/parallel summaries and obey it on replay: summaries carry the started-branch index set (or its complement, whichever is smaller) and replay reconstructs the exact live result instead of re-deriving statuses from child checkpoints. Summaries without the record fall back to checkpoint derivation. This also reconstructs FLAT results on replay, which checkpoint derivation could not discriminate. - Validate config bounds at construction, matching the Java SDK: max_concurrency and min_successful must be at least 1, tolerated_failure_count non-negative, tolerated_failure_percentage within 0-100. Previously max_concurrency=0 silently meant unlimited and min_successful=0 silently meant all items. Closes #279
1 parent 272c1f4 commit b5fe55d

10 files changed

Lines changed: 2176 additions & 2712 deletions

File tree

packages/aws-durable-execution-sdk-python-examples/test/map/test_map_completion.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,13 @@ def test_reproduce_completion_config_behavior_with_detailed_logging(durable_runn
2121

2222
result_data = deserialize_operation_payload(result.result)
2323

24-
# 5 items are processed 2 of them succeeded. We exit early because min_successful is 2.
25-
# Additionally, failure_count shows 0 because failed items have retry strategies configured and are still retrying
24+
# max_concurrency=3 starts items 0-2. The two failing items hold their
25+
# concurrency slots while retrying, so only the first success frees a
26+
# slot for item 3. min_successful=2 is reached before item 4 can start,
27+
# so 4 items appear in the result and the fifth never starts.
28+
# failure_count shows 0 because failed items have retry strategies configured and are still retrying
2629
# when execution completes. Failures aren't finalized until retries complete, so they don't appear in the failure_count.
27-
assert result_data["totalItems"] == 5
30+
assert result_data["totalItems"] == 4
2831
assert result_data["successfulCount"] == 2
2932
assert result_data["failedCount"] == 0
3033
assert result_data["hasFailures"] is False

packages/aws-durable-execution-sdk-python/src/aws_durable_execution_sdk_python/concurrency/executor.py

Lines changed: 309 additions & 307 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)