aws-durable-execution-sdk-python 1.3.0__py3-none-any.whl → 1.5.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.
- aws_durable_execution_sdk_python/__about__.py +1 -1
- aws_durable_execution_sdk_python/__init__.py +9 -0
- aws_durable_execution_sdk_python/concurrency/executor.py +40 -13
- aws_durable_execution_sdk_python/concurrency/models.py +88 -17
- aws_durable_execution_sdk_python/config.py +108 -14
- aws_durable_execution_sdk_python/context.py +125 -18
- aws_durable_execution_sdk_python/exceptions.py +100 -42
- aws_durable_execution_sdk_python/execution.py +57 -51
- aws_durable_execution_sdk_python/lambda_service.py +12 -0
- aws_durable_execution_sdk_python/operation/child.py +43 -29
- aws_durable_execution_sdk_python/operation/map.py +15 -2
- aws_durable_execution_sdk_python/operation/parallel.py +25 -4
- aws_durable_execution_sdk_python/operation/step.py +2 -2
- aws_durable_execution_sdk_python/serdes.py +1 -5
- aws_durable_execution_sdk_python/state.py +146 -39
- aws_durable_execution_sdk_python/types.py +3 -1
- {aws_durable_execution_sdk_python-1.3.0.dist-info → aws_durable_execution_sdk_python-1.5.0.dist-info}/METADATA +6 -29
- aws_durable_execution_sdk_python-1.5.0.dist-info/RECORD +36 -0
- {aws_durable_execution_sdk_python-1.3.0.dist-info → aws_durable_execution_sdk_python-1.5.0.dist-info}/WHEEL +1 -1
- aws_durable_execution_sdk_python-1.3.0.dist-info/RECORD +0 -36
- {aws_durable_execution_sdk_python-1.3.0.dist-info → aws_durable_execution_sdk_python-1.5.0.dist-info}/licenses/LICENSE +0 -0
- {aws_durable_execution_sdk_python-1.3.0.dist-info → aws_durable_execution_sdk_python-1.5.0.dist-info}/licenses/NOTICE +0 -0
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
"""AWS Lambda Durable Executions Python SDK."""
|
|
2
2
|
|
|
3
|
+
# Package metadata
|
|
4
|
+
from aws_durable_execution_sdk_python.__about__ import __version__
|
|
5
|
+
|
|
3
6
|
# Main context - used in every durable function
|
|
4
7
|
# Helper decorators - commonly used for step functions
|
|
5
8
|
# Concurrency
|
|
6
9
|
from aws_durable_execution_sdk_python.concurrency.models import BatchResult
|
|
10
|
+
from aws_durable_execution_sdk_python.config import ParallelBranch
|
|
7
11
|
from aws_durable_execution_sdk_python.context import (
|
|
8
12
|
DurableContext,
|
|
13
|
+
durable_parallel_branch,
|
|
9
14
|
durable_step,
|
|
10
15
|
durable_wait_for_callback,
|
|
11
16
|
durable_with_child_context,
|
|
@@ -24,14 +29,18 @@ from aws_durable_execution_sdk_python.execution import durable_execution
|
|
|
24
29
|
# Essential context types - passed to user functions
|
|
25
30
|
from aws_durable_execution_sdk_python.types import StepContext
|
|
26
31
|
|
|
32
|
+
|
|
27
33
|
__all__ = [
|
|
28
34
|
"BatchResult",
|
|
29
35
|
"DurableContext",
|
|
30
36
|
"DurableExecutionsError",
|
|
31
37
|
"InvocationError",
|
|
38
|
+
"ParallelBranch",
|
|
32
39
|
"StepContext",
|
|
33
40
|
"ValidationError",
|
|
41
|
+
"__version__",
|
|
34
42
|
"durable_execution",
|
|
43
|
+
"durable_parallel_branch",
|
|
35
44
|
"durable_step",
|
|
36
45
|
"durable_wait_for_callback",
|
|
37
46
|
"durable_with_child_context",
|
|
@@ -20,7 +20,10 @@ from aws_durable_execution_sdk_python.concurrency.models import (
|
|
|
20
20
|
ExecutionCounters,
|
|
21
21
|
SuspendResult,
|
|
22
22
|
)
|
|
23
|
-
from aws_durable_execution_sdk_python.config import
|
|
23
|
+
from aws_durable_execution_sdk_python.config import (
|
|
24
|
+
ChildConfig,
|
|
25
|
+
NestingType,
|
|
26
|
+
)
|
|
24
27
|
from aws_durable_execution_sdk_python.exceptions import (
|
|
25
28
|
OrphanedChildException,
|
|
26
29
|
SuspendExecution,
|
|
@@ -30,6 +33,7 @@ from aws_durable_execution_sdk_python.identifier import OperationIdentifier
|
|
|
30
33
|
from aws_durable_execution_sdk_python.lambda_service import ErrorObject
|
|
31
34
|
from aws_durable_execution_sdk_python.operation.child import child_handler
|
|
32
35
|
|
|
36
|
+
|
|
33
37
|
if TYPE_CHECKING:
|
|
34
38
|
from collections.abc import Callable
|
|
35
39
|
|
|
@@ -142,6 +146,7 @@ class ConcurrentExecutor(ABC, Generic[CallableType, ResultType]):
|
|
|
142
146
|
serdes: SerDes | None,
|
|
143
147
|
item_serdes: SerDes | None = None,
|
|
144
148
|
summary_generator: SummaryGenerator | None = None,
|
|
149
|
+
nesting_type: NestingType = NestingType.NESTED,
|
|
145
150
|
):
|
|
146
151
|
"""Initialize ConcurrentExecutor.
|
|
147
152
|
|
|
@@ -159,6 +164,7 @@ class ConcurrentExecutor(ABC, Generic[CallableType, ResultType]):
|
|
|
159
164
|
self.sub_type_iteration = sub_type_iteration
|
|
160
165
|
self.name_prefix = name_prefix
|
|
161
166
|
self.summary_generator = summary_generator
|
|
167
|
+
self.nesting_type = nesting_type
|
|
162
168
|
|
|
163
169
|
# Event-driven state tracking for when the executor is done
|
|
164
170
|
self._completion_event = threading.Event()
|
|
@@ -188,6 +194,14 @@ class ConcurrentExecutor(ABC, Generic[CallableType, ResultType]):
|
|
|
188
194
|
"""Execute a single executable in a child context and return the result."""
|
|
189
195
|
raise NotImplementedError
|
|
190
196
|
|
|
197
|
+
def get_iteration_name(self, index: int) -> str:
|
|
198
|
+
"""Get the display name for an iteration/branch at the given index.
|
|
199
|
+
|
|
200
|
+
Subclasses can override this to provide custom naming (e.g., from item_namer
|
|
201
|
+
or branch names). The default returns "{name_prefix}{index}".
|
|
202
|
+
"""
|
|
203
|
+
return f"{self.name_prefix}{index}"
|
|
204
|
+
|
|
191
205
|
def execute(
|
|
192
206
|
self, execution_state: ExecutionState, executor_context: DurableContext
|
|
193
207
|
) -> BatchResult[ResultType]:
|
|
@@ -196,6 +210,11 @@ class ConcurrentExecutor(ABC, Generic[CallableType, ResultType]):
|
|
|
196
210
|
"▶️ Executing concurrent operation, items: %d", len(self.executables)
|
|
197
211
|
)
|
|
198
212
|
|
|
213
|
+
# Early return for empty executables
|
|
214
|
+
if not self.executables:
|
|
215
|
+
logger.debug("No items to execute, returning empty result")
|
|
216
|
+
return self._create_result()
|
|
217
|
+
|
|
199
218
|
max_workers = self.max_concurrency or len(self.executables)
|
|
200
219
|
|
|
201
220
|
self.executables_with_state = [
|
|
@@ -385,29 +404,36 @@ class ConcurrentExecutor(ABC, Generic[CallableType, ResultType]):
|
|
|
385
404
|
"""
|
|
386
405
|
Execute a single item in a derived child context.
|
|
387
406
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
directly. This avoids the hidden mutation of the context's
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
407
|
+
Instead of relying on `executor_context.run_in_child_context` we
|
|
408
|
+
generate an operation_id for the child, then call `child_handler`
|
|
409
|
+
directly. This avoids the hidden mutation of the context's
|
|
410
|
+
internal counter. We explicitly derive the child's operation_id
|
|
411
|
+
from `executable.index` so that the same input always produces
|
|
412
|
+
the same id regardless of the order branches actually run in.
|
|
394
413
|
|
|
395
|
-
|
|
396
|
-
|
|
414
|
+
Invariant: `operation_id` for a given executable is deterministic
|
|
415
|
+
and execution-order invariant.
|
|
397
416
|
"""
|
|
398
417
|
|
|
399
|
-
operation_id = executor_context._create_step_id_for_logical_step( # noqa: SLF001
|
|
418
|
+
operation_id: str = executor_context._create_step_id_for_logical_step( # noqa: SLF001
|
|
400
419
|
executable.index
|
|
401
420
|
)
|
|
402
|
-
name =
|
|
403
|
-
|
|
421
|
+
name: str = self.get_iteration_name(executable.index)
|
|
422
|
+
is_virtual: bool = self.nesting_type is NestingType.FLAT
|
|
423
|
+
|
|
424
|
+
child_context: DurableContext = executor_context.create_child_context(
|
|
425
|
+
operation_id, is_virtual=is_virtual
|
|
426
|
+
)
|
|
427
|
+
# For NESTED this is for branch's START/SUCCEED/FAIL checkpoints (not the children of the branch).
|
|
428
|
+
# For FLAT `child_handler` skips checkpoints, so not used.
|
|
429
|
+
# Construct it unconditionally to keep the call simple.
|
|
404
430
|
operation_identifier = OperationIdentifier(
|
|
405
431
|
operation_id,
|
|
406
432
|
executor_context._parent_id, # noqa: SLF001
|
|
407
433
|
name,
|
|
408
434
|
)
|
|
409
435
|
|
|
410
|
-
def run_in_child_handler():
|
|
436
|
+
def run_in_child_handler() -> ResultType:
|
|
411
437
|
return self.execute_item(child_context, executable)
|
|
412
438
|
|
|
413
439
|
result: ResultType = child_handler(
|
|
@@ -418,6 +444,7 @@ class ConcurrentExecutor(ABC, Generic[CallableType, ResultType]):
|
|
|
418
444
|
serdes=self.item_serdes or self.serdes,
|
|
419
445
|
sub_type=self.sub_type_iteration,
|
|
420
446
|
summary_generator=self.summary_generator,
|
|
447
|
+
is_virtual=is_virtual,
|
|
421
448
|
),
|
|
422
449
|
)
|
|
423
450
|
child_context.state.track_replay(operation_id=operation_id)
|
|
@@ -114,6 +114,85 @@ class BatchResult(Generic[R], BatchResultProtocol[R]): # noqa: PYI059
|
|
|
114
114
|
completion_reason = CompletionReason(completion_reason_value)
|
|
115
115
|
return cls(batch_items, completion_reason)
|
|
116
116
|
|
|
117
|
+
@staticmethod
|
|
118
|
+
def _get_completion_reason(
|
|
119
|
+
failure_count: int,
|
|
120
|
+
success_count: int,
|
|
121
|
+
completed_count: int,
|
|
122
|
+
total_count: int,
|
|
123
|
+
completion_config: CompletionConfig | None,
|
|
124
|
+
) -> CompletionReason:
|
|
125
|
+
"""
|
|
126
|
+
Determine completion reason based on completion counts.
|
|
127
|
+
|
|
128
|
+
Logic order:
|
|
129
|
+
1. Check failure tolerance FIRST (before checking if all completed)
|
|
130
|
+
2. Check if all completed
|
|
131
|
+
3. Check if minimum successful reached
|
|
132
|
+
4. Default to ALL_COMPLETED
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
failure_count: Number of failed items
|
|
136
|
+
success_count: Number of succeeded items
|
|
137
|
+
completed_count: Total completed (succeeded + failed)
|
|
138
|
+
total_count: Total number of items
|
|
139
|
+
completion_config: Optional completion configuration
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
CompletionReason enum value
|
|
143
|
+
"""
|
|
144
|
+
# STEP 1: Check tolerance first, before checking if all completed
|
|
145
|
+
|
|
146
|
+
# Handle fail-fast behavior (no completion config or empty completion config)
|
|
147
|
+
if completion_config is None:
|
|
148
|
+
if failure_count > 0:
|
|
149
|
+
return CompletionReason.FAILURE_TOLERANCE_EXCEEDED
|
|
150
|
+
else:
|
|
151
|
+
# Check if completion config has any criteria set
|
|
152
|
+
has_any_completion_criteria = (
|
|
153
|
+
completion_config.min_successful is not None
|
|
154
|
+
or completion_config.tolerated_failure_count is not None
|
|
155
|
+
or completion_config.tolerated_failure_percentage is not None
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
if not has_any_completion_criteria:
|
|
159
|
+
# Empty completion config - fail fast on any failure
|
|
160
|
+
if failure_count > 0:
|
|
161
|
+
return CompletionReason.FAILURE_TOLERANCE_EXCEEDED
|
|
162
|
+
else:
|
|
163
|
+
# Check specific tolerance thresholds
|
|
164
|
+
if (
|
|
165
|
+
completion_config.tolerated_failure_count is not None
|
|
166
|
+
and failure_count > completion_config.tolerated_failure_count
|
|
167
|
+
):
|
|
168
|
+
return CompletionReason.FAILURE_TOLERANCE_EXCEEDED
|
|
169
|
+
|
|
170
|
+
if (
|
|
171
|
+
completion_config.tolerated_failure_percentage is not None
|
|
172
|
+
and total_count > 0
|
|
173
|
+
):
|
|
174
|
+
failure_percentage = (failure_count / total_count) * 100
|
|
175
|
+
if (
|
|
176
|
+
failure_percentage
|
|
177
|
+
> completion_config.tolerated_failure_percentage
|
|
178
|
+
):
|
|
179
|
+
return CompletionReason.FAILURE_TOLERANCE_EXCEEDED
|
|
180
|
+
|
|
181
|
+
# STEP 2: Check if all completed
|
|
182
|
+
if completed_count == total_count:
|
|
183
|
+
return CompletionReason.ALL_COMPLETED
|
|
184
|
+
|
|
185
|
+
# STEP 3: Check if minimum successful reached
|
|
186
|
+
if (
|
|
187
|
+
completion_config is not None
|
|
188
|
+
and completion_config.min_successful is not None
|
|
189
|
+
and success_count >= completion_config.min_successful
|
|
190
|
+
):
|
|
191
|
+
return CompletionReason.MIN_SUCCESSFUL_REACHED
|
|
192
|
+
|
|
193
|
+
# STEP 4: Default
|
|
194
|
+
return CompletionReason.ALL_COMPLETED
|
|
195
|
+
|
|
117
196
|
@classmethod
|
|
118
197
|
def from_items(
|
|
119
198
|
cls,
|
|
@@ -123,12 +202,8 @@ class BatchResult(Generic[R], BatchResultProtocol[R]): # noqa: PYI059
|
|
|
123
202
|
"""
|
|
124
203
|
Infer completion reason based on batch item statuses and completion config.
|
|
125
204
|
|
|
126
|
-
This follows the same logic as the TypeScript implementation
|
|
127
|
-
- If all items completed: ALL_COMPLETED
|
|
128
|
-
- If minSuccessful threshold met and not all completed: MIN_SUCCESSFUL_REACHED
|
|
129
|
-
- Otherwise: FAILURE_TOLERANCE_EXCEEDED
|
|
205
|
+
This follows the same logic as the TypeScript implementation.
|
|
130
206
|
"""
|
|
131
|
-
|
|
132
207
|
statuses = (item.status for item in items)
|
|
133
208
|
counts = Counter(statuses)
|
|
134
209
|
succeeded_count = counts.get(BatchItemStatus.SUCCEEDED, 0)
|
|
@@ -138,18 +213,14 @@ class BatchResult(Generic[R], BatchResultProtocol[R]): # noqa: PYI059
|
|
|
138
213
|
completed_count = succeeded_count + failed_count
|
|
139
214
|
total_count = started_count + completed_count
|
|
140
215
|
|
|
141
|
-
#
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
)
|
|
149
|
-
completion_reason = CompletionReason.MIN_SUCCESSFUL_REACHED
|
|
150
|
-
else:
|
|
151
|
-
# Otherwise, assume failure tolerance was exceeded
|
|
152
|
-
completion_reason = CompletionReason.FAILURE_TOLERANCE_EXCEEDED
|
|
216
|
+
# Determine completion reason using the same logic as JavaScript SDK
|
|
217
|
+
completion_reason = cls._get_completion_reason(
|
|
218
|
+
failure_count=failed_count,
|
|
219
|
+
success_count=succeeded_count,
|
|
220
|
+
completed_count=completed_count,
|
|
221
|
+
total_count=total_count,
|
|
222
|
+
completion_config=completion_config,
|
|
223
|
+
)
|
|
153
224
|
|
|
154
225
|
return cls(items, completion_reason)
|
|
155
226
|
|
|
@@ -9,6 +9,7 @@ from typing import TYPE_CHECKING, Generic, TypeVar
|
|
|
9
9
|
|
|
10
10
|
from aws_durable_execution_sdk_python.exceptions import ValidationError
|
|
11
11
|
|
|
12
|
+
|
|
12
13
|
P = TypeVar("P") # Payload type
|
|
13
14
|
R = TypeVar("R") # Result type
|
|
14
15
|
T = TypeVar("T")
|
|
@@ -76,6 +77,42 @@ class TerminationMode(Enum):
|
|
|
76
77
|
ABANDON = "ABANDON"
|
|
77
78
|
|
|
78
79
|
|
|
80
|
+
class NestingType(Enum):
|
|
81
|
+
"""Control how child contexts are created for batch operations.
|
|
82
|
+
|
|
83
|
+
Applies to `map` and `parallel`. Each branch or iteration runs inside a
|
|
84
|
+
child context.
|
|
85
|
+
|
|
86
|
+
- NESTED: full checkpointed context
|
|
87
|
+
- FLAT: a virtual context that skips checkpoints for the branch/iteration.
|
|
88
|
+
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
NESTED = "NESTED"
|
|
92
|
+
"""Create CONTEXT operations for each branch/iteration with full checkpointing.
|
|
93
|
+
|
|
94
|
+
Operations within each branch/iteration are wrapped in their own context.
|
|
95
|
+
|
|
96
|
+
- Observability: high — each branch/iteration appears as a separate
|
|
97
|
+
operation in execution history.
|
|
98
|
+
- Cost: higher — consumes more operations due to CONTEXT creation
|
|
99
|
+
overhead.
|
|
100
|
+
- Scale: lower maximum iterations due to operation limits.
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
FLAT = "FLAT"
|
|
104
|
+
"""Skip CONTEXT operations for branches/iterations using virtual contexts.
|
|
105
|
+
|
|
106
|
+
Operations execute directly without individual context wrapping.
|
|
107
|
+
|
|
108
|
+
- Observability: lower — branches/iterations don't appear as separate
|
|
109
|
+
operations in execution history.
|
|
110
|
+
- Cost: ~30% lower — reduces operation consumption by skipping CONTEXT
|
|
111
|
+
overhead.
|
|
112
|
+
- Scale: higher maximum iterations possible within operation limits.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
|
|
79
116
|
@dataclass(frozen=True)
|
|
80
117
|
class CompletionConfig:
|
|
81
118
|
"""Configuration for determining when parallel/map operations complete.
|
|
@@ -187,6 +224,10 @@ class ParallelConfig:
|
|
|
187
224
|
Used internally by map/parallel operations to handle large BatchResult payloads.
|
|
188
225
|
Signature: (result: T) -> str
|
|
189
226
|
|
|
227
|
+
nesting_type: How child operations should inherit context from their parent.
|
|
228
|
+
- NESTED: Each branch runs in its own isolated context (default)
|
|
229
|
+
- FLAT: All branches share the same parent context
|
|
230
|
+
|
|
190
231
|
Example:
|
|
191
232
|
# Run at most 3 branches concurrently, succeed if any one succeeds
|
|
192
233
|
config = ParallelConfig(
|
|
@@ -202,6 +243,42 @@ class ParallelConfig:
|
|
|
202
243
|
serdes: SerDes | None = None
|
|
203
244
|
item_serdes: SerDes | None = None
|
|
204
245
|
summary_generator: SummaryGenerator | None = None
|
|
246
|
+
nesting_type: NestingType = NestingType.NESTED
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
@dataclass(frozen=True)
|
|
250
|
+
class ParallelBranch(Generic[T]):
|
|
251
|
+
"""A named branch for parallel execution.
|
|
252
|
+
|
|
253
|
+
Use this to provide custom names for parallel branches, improving
|
|
254
|
+
observability in execution history.
|
|
255
|
+
|
|
256
|
+
Type Parameters:
|
|
257
|
+
T: The return type of the branch function.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
func: The callable to execute in this branch. Receives a DurableContext.
|
|
261
|
+
name: Optional custom name for this branch. When provided, replaces
|
|
262
|
+
the default "parallel-branch-{index}" naming in execution history.
|
|
263
|
+
This affects observability but not replay determinism.
|
|
264
|
+
|
|
265
|
+
Example:
|
|
266
|
+
context.parallel(
|
|
267
|
+
functions=[
|
|
268
|
+
ParallelBranch(func=lambda ctx: fetch_user(ctx), name="fetch-user-data"),
|
|
269
|
+
ParallelBranch(func=lambda ctx: fetch_orders(ctx), name="fetch-order-history"),
|
|
270
|
+
],
|
|
271
|
+
name="load-data",
|
|
272
|
+
config=ParallelConfig(max_concurrency=2),
|
|
273
|
+
)
|
|
274
|
+
"""
|
|
275
|
+
|
|
276
|
+
func: Callable
|
|
277
|
+
name: str | None = None
|
|
278
|
+
|
|
279
|
+
def __call__(self, *args, **kwargs):
|
|
280
|
+
"""Delegate to the wrapped function, making ParallelBranch itself callable."""
|
|
281
|
+
return self.func(*args, **kwargs)
|
|
205
282
|
|
|
206
283
|
|
|
207
284
|
class StepSemantics(Enum):
|
|
@@ -218,12 +295,6 @@ class StepConfig:
|
|
|
218
295
|
serdes: SerDes | None = None
|
|
219
296
|
|
|
220
297
|
|
|
221
|
-
class CheckpointMode(Enum):
|
|
222
|
-
NO_CHECKPOINT = ("NO_CHECKPOINT",)
|
|
223
|
-
CHECKPOINT_AT_FINISH = ("CHECKPOINT_AT_FINISH",)
|
|
224
|
-
CHECKPOINT_AT_START_AND_FINISH = "CHECKPOINT_AT_START_AND_FINISH"
|
|
225
|
-
|
|
226
|
-
|
|
227
298
|
@dataclass(frozen=True)
|
|
228
299
|
class ChildConfig(Generic[T]):
|
|
229
300
|
"""Configuration options for child context operations.
|
|
@@ -259,21 +330,23 @@ class ChildConfig(Generic[T]):
|
|
|
259
330
|
|
|
260
331
|
Used internally by map/parallel operations to handle large BatchResult payloads.
|
|
261
332
|
Signature: (result: T) -> str
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
333
|
+
|
|
334
|
+
is_virtual: When True, skip all checkpoints (START, SUCCEED,
|
|
335
|
+
FAIL) for this child context and propagate the caller's reporting
|
|
336
|
+
parent id through to operations created inside the child. The
|
|
337
|
+
branch is a logical scope for step-id prefixing but does not
|
|
338
|
+
appear in the execution history. Used internally by
|
|
339
|
+
NestingType.FLAT branches. Use this to group operations without
|
|
340
|
+
adding a CONTEXT entry to the execution history.
|
|
268
341
|
|
|
269
342
|
See TypeScript reference: aws-durable-execution-sdk-js/src/types/index.ts
|
|
270
343
|
"""
|
|
271
344
|
|
|
272
|
-
# checkpoint_mode: CheckpointMode = CheckpointMode.CHECKPOINT_AT_START_AND_FINISH
|
|
273
345
|
serdes: SerDes | None = None
|
|
274
346
|
item_serdes: SerDes | None = None
|
|
275
347
|
sub_type: OperationSubType | None = None
|
|
276
348
|
summary_generator: SummaryGenerator | None = None
|
|
349
|
+
is_virtual: bool = False
|
|
277
350
|
|
|
278
351
|
|
|
279
352
|
class ItemsPerBatchUnit(Enum):
|
|
@@ -317,12 +390,15 @@ class ItemBatcher(Generic[T]):
|
|
|
317
390
|
|
|
318
391
|
|
|
319
392
|
@dataclass(frozen=True)
|
|
320
|
-
class MapConfig:
|
|
393
|
+
class MapConfig(Generic[T]):
|
|
321
394
|
"""Configuration options for map operations over collections.
|
|
322
395
|
|
|
323
396
|
This class configures how map operations process collections of items,
|
|
324
397
|
including concurrency, batching, completion criteria, and serialization.
|
|
325
398
|
|
|
399
|
+
Type Parameters:
|
|
400
|
+
T: The type of items being processed in the map operation.
|
|
401
|
+
|
|
326
402
|
Args:
|
|
327
403
|
max_concurrency: Maximum number of items to process concurrently.
|
|
328
404
|
If None, no limit is imposed and all items are processed concurrently.
|
|
@@ -361,6 +437,16 @@ class MapConfig:
|
|
|
361
437
|
Used internally by map/parallel operations to handle large BatchResult payloads.
|
|
362
438
|
Signature: (result: T) -> str
|
|
363
439
|
|
|
440
|
+
nesting_type: How child operations should inherit context from their parent.
|
|
441
|
+
- NESTED: Each item runs in its own isolated context (default)
|
|
442
|
+
- FLAT: All items share the same parent context
|
|
443
|
+
|
|
444
|
+
item_namer: Optional callable to generate custom names for each map iteration.
|
|
445
|
+
When provided, replaces the default "map-item-{index}" naming scheme.
|
|
446
|
+
Receives the item and its index, and returns a string name for that iteration.
|
|
447
|
+
This affects observability (execution history names) but not replay determinism.
|
|
448
|
+
If None, uses the default naming: "map-item-{index}".
|
|
449
|
+
|
|
364
450
|
Example:
|
|
365
451
|
# Process 5 items at a time, batch by count, require all to succeed
|
|
366
452
|
config = MapConfig(
|
|
@@ -368,6 +454,12 @@ class MapConfig:
|
|
|
368
454
|
item_batcher=ItemBatcher(max_items_per_batch=10),
|
|
369
455
|
completion_config=CompletionConfig.all_successful()
|
|
370
456
|
)
|
|
457
|
+
|
|
458
|
+
# With custom iteration names
|
|
459
|
+
config = MapConfig(
|
|
460
|
+
max_concurrency=5,
|
|
461
|
+
item_namer=lambda item, index: f"process-order-{item.id}"
|
|
462
|
+
)
|
|
371
463
|
"""
|
|
372
464
|
|
|
373
465
|
max_concurrency: int | None = None
|
|
@@ -376,6 +468,8 @@ class MapConfig:
|
|
|
376
468
|
serdes: SerDes | None = None
|
|
377
469
|
item_serdes: SerDes | None = None
|
|
378
470
|
summary_generator: SummaryGenerator | None = None
|
|
471
|
+
nesting_type: NestingType = NestingType.NESTED
|
|
472
|
+
item_namer: Callable[[T, int], str] | None = None
|
|
379
473
|
|
|
380
474
|
|
|
381
475
|
@dataclass(frozen=True)
|