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.
- aws_durable_execution_sdk_python/.gitignore +0 -0
- aws_durable_execution_sdk_python/__about__.py +4 -0
- aws_durable_execution_sdk_python/__init__.py +34 -0
- aws_durable_execution_sdk_python/botocore/data/lambdainternal/2015-03-31/service-2.json +7864 -0
- aws_durable_execution_sdk_python/concurrency/__init__.py +0 -0
- aws_durable_execution_sdk_python/concurrency/executor.py +436 -0
- aws_durable_execution_sdk_python/concurrency/models.py +469 -0
- aws_durable_execution_sdk_python/config.py +499 -0
- aws_durable_execution_sdk_python/context.py +551 -0
- aws_durable_execution_sdk_python/exceptions.py +374 -0
- aws_durable_execution_sdk_python/execution.py +428 -0
- aws_durable_execution_sdk_python/identifier.py +14 -0
- aws_durable_execution_sdk_python/lambda_service.py +1034 -0
- aws_durable_execution_sdk_python/logger.py +131 -0
- aws_durable_execution_sdk_python/operation/__init__.py +1 -0
- aws_durable_execution_sdk_python/operation/callback.py +123 -0
- aws_durable_execution_sdk_python/operation/child.py +162 -0
- aws_durable_execution_sdk_python/operation/invoke.py +119 -0
- aws_durable_execution_sdk_python/operation/map.py +137 -0
- aws_durable_execution_sdk_python/operation/parallel.py +122 -0
- aws_durable_execution_sdk_python/operation/step.py +269 -0
- aws_durable_execution_sdk_python/operation/wait.py +53 -0
- aws_durable_execution_sdk_python/operation/wait_for_condition.py +235 -0
- aws_durable_execution_sdk_python/py.typed +1 -0
- aws_durable_execution_sdk_python/retries.py +174 -0
- aws_durable_execution_sdk_python/serdes.py +502 -0
- aws_durable_execution_sdk_python/state.py +790 -0
- aws_durable_execution_sdk_python/suspend.py +84 -0
- aws_durable_execution_sdk_python/threading.py +222 -0
- aws_durable_execution_sdk_python/types.py +180 -0
- aws_durable_execution_sdk_python/waits.py +130 -0
- aws_durable_execution_sdk_python-1.0.0.dist-info/METADATA +679 -0
- aws_durable_execution_sdk_python-1.0.0.dist-info/RECORD +36 -0
- aws_durable_execution_sdk_python-1.0.0.dist-info/WHEEL +4 -0
- aws_durable_execution_sdk_python-1.0.0.dist-info/licenses/LICENSE +175 -0
- aws_durable_execution_sdk_python-1.0.0.dist-info/licenses/NOTICE +1 -0
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
"""Configuration types."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import random
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from enum import Enum, StrEnum
|
|
8
|
+
from typing import TYPE_CHECKING, Generic, TypeVar
|
|
9
|
+
|
|
10
|
+
from aws_durable_execution_sdk_python.exceptions import ValidationError
|
|
11
|
+
|
|
12
|
+
P = TypeVar("P") # Payload type
|
|
13
|
+
R = TypeVar("R") # Result type
|
|
14
|
+
T = TypeVar("T")
|
|
15
|
+
U = TypeVar("U")
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from collections.abc import Callable
|
|
19
|
+
from concurrent.futures import Future
|
|
20
|
+
|
|
21
|
+
from aws_durable_execution_sdk_python.lambda_service import OperationSubType
|
|
22
|
+
from aws_durable_execution_sdk_python.retries import RetryDecision
|
|
23
|
+
from aws_durable_execution_sdk_python.serdes import SerDes
|
|
24
|
+
from aws_durable_execution_sdk_python.types import SummaryGenerator
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
Numeric = int | float # deliberately leaving off complex
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True)
|
|
31
|
+
class Duration:
|
|
32
|
+
"""Represents a duration stored as total seconds."""
|
|
33
|
+
|
|
34
|
+
seconds: int = 0
|
|
35
|
+
|
|
36
|
+
def __post_init__(self):
|
|
37
|
+
if self.seconds < 0:
|
|
38
|
+
msg = "Duration seconds must be positive"
|
|
39
|
+
raise ValidationError(msg)
|
|
40
|
+
|
|
41
|
+
def to_seconds(self) -> int:
|
|
42
|
+
"""Convert the duration to total seconds."""
|
|
43
|
+
return self.seconds
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
def from_seconds(cls, value: float) -> Duration:
|
|
47
|
+
"""Create a Duration from total seconds."""
|
|
48
|
+
return cls(seconds=int(value))
|
|
49
|
+
|
|
50
|
+
@classmethod
|
|
51
|
+
def from_minutes(cls, value: float) -> Duration:
|
|
52
|
+
"""Create a Duration from minutes."""
|
|
53
|
+
return cls(seconds=int(value * 60))
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def from_hours(cls, value: float) -> Duration:
|
|
57
|
+
"""Create a Duration from hours."""
|
|
58
|
+
return cls(seconds=int(value * 3600))
|
|
59
|
+
|
|
60
|
+
@classmethod
|
|
61
|
+
def from_days(cls, value: float) -> Duration:
|
|
62
|
+
"""Create a Duration from days."""
|
|
63
|
+
return cls(seconds=int(value * 86400))
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass(frozen=True)
|
|
67
|
+
class BatchedInput(Generic[T, U]):
|
|
68
|
+
batch_input: T
|
|
69
|
+
items: list[U]
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class TerminationMode(Enum):
|
|
73
|
+
TERMINATE = "TERMINATE"
|
|
74
|
+
CANCEL = "CANCEL"
|
|
75
|
+
WAIT = "WAIT"
|
|
76
|
+
ABANDON = "ABANDON"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@dataclass(frozen=True)
|
|
80
|
+
class CompletionConfig:
|
|
81
|
+
"""Configuration for determining when parallel/map operations complete.
|
|
82
|
+
|
|
83
|
+
This class defines the success/failure criteria for operations that process
|
|
84
|
+
multiple items or branches concurrently.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
min_successful: Minimum number of successful completions required.
|
|
88
|
+
If None, no minimum is enforced. Use this to implement "at least N
|
|
89
|
+
must succeed" semantics.
|
|
90
|
+
|
|
91
|
+
tolerated_failure_count: Maximum number of failures allowed before
|
|
92
|
+
the operation is considered failed. If None, no limit on failure count.
|
|
93
|
+
Use this to implement "fail fast after N failures" semantics.
|
|
94
|
+
|
|
95
|
+
tolerated_failure_percentage: Maximum percentage of failures allowed
|
|
96
|
+
(0.0 to 100.0). If None, no percentage limit is enforced.
|
|
97
|
+
Use this to implement "fail if more than X% fail" semantics.
|
|
98
|
+
|
|
99
|
+
Note:
|
|
100
|
+
The operation completes when any of the completion criteria are met:
|
|
101
|
+
- Enough successes (min_successful reached)
|
|
102
|
+
- Too many failures (tolerated limits exceeded)
|
|
103
|
+
- All items/branches completed
|
|
104
|
+
|
|
105
|
+
Example:
|
|
106
|
+
# Succeed if at least 3 succeed, fail if more than 2 fail
|
|
107
|
+
config = CompletionConfig(
|
|
108
|
+
min_successful=3,
|
|
109
|
+
tolerated_failure_count=2
|
|
110
|
+
)
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
min_successful: int | None = None
|
|
114
|
+
tolerated_failure_count: int | None = None
|
|
115
|
+
tolerated_failure_percentage: int | float | None = None
|
|
116
|
+
|
|
117
|
+
# TODO: reevaluate this
|
|
118
|
+
# @staticmethod
|
|
119
|
+
# def first_completed():
|
|
120
|
+
# return CompletionConfig(
|
|
121
|
+
# min_successful=None, tolerated_failure_count=None, tolerated_failure_percentage=None
|
|
122
|
+
# )
|
|
123
|
+
|
|
124
|
+
@staticmethod
|
|
125
|
+
def first_successful():
|
|
126
|
+
return CompletionConfig(
|
|
127
|
+
min_successful=1,
|
|
128
|
+
tolerated_failure_count=None,
|
|
129
|
+
tolerated_failure_percentage=None,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
@staticmethod
|
|
133
|
+
def all_completed():
|
|
134
|
+
return CompletionConfig(
|
|
135
|
+
min_successful=None,
|
|
136
|
+
tolerated_failure_count=None,
|
|
137
|
+
tolerated_failure_percentage=None,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
@staticmethod
|
|
141
|
+
def all_successful():
|
|
142
|
+
return CompletionConfig(
|
|
143
|
+
min_successful=None,
|
|
144
|
+
tolerated_failure_count=0,
|
|
145
|
+
tolerated_failure_percentage=0,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@dataclass(frozen=True)
|
|
150
|
+
class ParallelConfig:
|
|
151
|
+
"""Configuration options for parallel execution operations.
|
|
152
|
+
|
|
153
|
+
This class configures how parallel operations are executed, including
|
|
154
|
+
concurrency limits, completion criteria, and serialization behavior.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
max_concurrency: Maximum number of parallel branches to execute concurrently.
|
|
158
|
+
If None, no limit is imposed and all branches run concurrently.
|
|
159
|
+
Use this to control resource usage and prevent overwhelming the system.
|
|
160
|
+
|
|
161
|
+
completion_config: Defines when the parallel operation should complete.
|
|
162
|
+
Controls success/failure criteria for the overall parallel operation.
|
|
163
|
+
Default is CompletionConfig.all_successful() which requires all branches
|
|
164
|
+
to succeed. Other options include first_successful() and all_completed().
|
|
165
|
+
|
|
166
|
+
serdes: Custom serialization/deserialization configuration for BatchResult.
|
|
167
|
+
Applied at the handler level to serialize the entire BatchResult object.
|
|
168
|
+
If None, uses the default JSON serializer for BatchResult.
|
|
169
|
+
|
|
170
|
+
Backward Compatibility: If only 'serdes' is provided (no item_serdes),
|
|
171
|
+
it will be used for both individual functions AND BatchResult serialization
|
|
172
|
+
to maintain existing behavior.
|
|
173
|
+
|
|
174
|
+
item_serdes: Custom serialization/deserialization configuration for individual functions.
|
|
175
|
+
Applied to each function's result as tasks complete in child contexts.
|
|
176
|
+
If None, uses the default JSON serializer for individual function results.
|
|
177
|
+
|
|
178
|
+
When both 'serdes' and 'item_serdes' are provided:
|
|
179
|
+
- item_serdes: Used for individual function results in child contexts
|
|
180
|
+
- serdes: Used for the entire BatchResult at handler level
|
|
181
|
+
|
|
182
|
+
summary_generator: Function to generate compact summaries for large results (>256KB).
|
|
183
|
+
When the serialized result exceeds CHECKPOINT_SIZE_LIMIT, this generator
|
|
184
|
+
creates a JSON summary instead of checkpointing the full result. The operation
|
|
185
|
+
is marked with ReplayChildren=true to reconstruct the full result during replay.
|
|
186
|
+
|
|
187
|
+
Used internally by map/parallel operations to handle large BatchResult payloads.
|
|
188
|
+
Signature: (result: T) -> str
|
|
189
|
+
|
|
190
|
+
Example:
|
|
191
|
+
# Run at most 3 branches concurrently, succeed if any one succeeds
|
|
192
|
+
config = ParallelConfig(
|
|
193
|
+
max_concurrency=3,
|
|
194
|
+
completion_config=CompletionConfig.first_successful()
|
|
195
|
+
)
|
|
196
|
+
"""
|
|
197
|
+
|
|
198
|
+
max_concurrency: int | None = None
|
|
199
|
+
completion_config: CompletionConfig = field(
|
|
200
|
+
default_factory=CompletionConfig.all_successful
|
|
201
|
+
)
|
|
202
|
+
serdes: SerDes | None = None
|
|
203
|
+
item_serdes: SerDes | None = None
|
|
204
|
+
summary_generator: SummaryGenerator | None = None
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
class StepSemantics(Enum):
|
|
208
|
+
AT_MOST_ONCE_PER_RETRY = "AT_MOST_ONCE_PER_RETRY"
|
|
209
|
+
AT_LEAST_ONCE_PER_RETRY = "AT_LEAST_ONCE_PER_RETRY"
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
@dataclass(frozen=True)
|
|
213
|
+
class StepConfig:
|
|
214
|
+
"""Configuration for a step."""
|
|
215
|
+
|
|
216
|
+
retry_strategy: Callable[[Exception, int], RetryDecision] | None = None
|
|
217
|
+
step_semantics: StepSemantics = StepSemantics.AT_LEAST_ONCE_PER_RETRY
|
|
218
|
+
serdes: SerDes | None = None
|
|
219
|
+
|
|
220
|
+
|
|
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
|
+
@dataclass(frozen=True)
|
|
228
|
+
class ChildConfig(Generic[T]):
|
|
229
|
+
"""Configuration options for child context operations.
|
|
230
|
+
|
|
231
|
+
This class configures how child contexts are executed and checkpointed,
|
|
232
|
+
matching the TypeScript ChildConfig interface behavior.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
serdes: Custom serialization/deserialization configuration for BatchResult.
|
|
236
|
+
Applied at the handler level to serialize the entire BatchResult object.
|
|
237
|
+
If None, uses the default JSON serializer for BatchResult.
|
|
238
|
+
|
|
239
|
+
Backward Compatibility: If only 'serdes' is provided (no item_serdes),
|
|
240
|
+
it will be used for both individual items AND BatchResult serialization
|
|
241
|
+
to maintain existing behavior.
|
|
242
|
+
|
|
243
|
+
item_serdes: Custom serialization/deserialization configuration for individual items.
|
|
244
|
+
Applied to each item's result as tasks complete in child contexts.
|
|
245
|
+
If None, uses the default JSON serializer for individual items.
|
|
246
|
+
|
|
247
|
+
When both 'serdes' and 'item_serdes' are provided:
|
|
248
|
+
- item_serdes: Used for individual item results in child contexts
|
|
249
|
+
- serdes: Used for the entire BatchResult at handler level
|
|
250
|
+
|
|
251
|
+
sub_type: Operation subtype identifier used for tracking and debugging.
|
|
252
|
+
Examples: OperationSubType.MAP_ITERATION, OperationSubType.PARALLEL_BRANCH.
|
|
253
|
+
Used internally by the execution engine for operation classification.
|
|
254
|
+
|
|
255
|
+
summary_generator: Function to generate compact summaries for large results (>256KB).
|
|
256
|
+
When the serialized result exceeds CHECKPOINT_SIZE_LIMIT, this generator
|
|
257
|
+
creates a JSON summary instead of checkpointing the full result. The operation
|
|
258
|
+
is marked with ReplayChildren=true to reconstruct the full result during replay.
|
|
259
|
+
|
|
260
|
+
Used internally by map/parallel operations to handle large BatchResult payloads.
|
|
261
|
+
Signature: (result: T) -> str
|
|
262
|
+
Note:
|
|
263
|
+
checkpoint_mode field is commented out as it's not currently implemented.
|
|
264
|
+
When implemented, it will control when checkpoints are created:
|
|
265
|
+
- CHECKPOINT_AT_START_AND_FINISH: Checkpoint at both start and completion (default)
|
|
266
|
+
- CHECKPOINT_AT_FINISH: Only checkpoint when operation completes
|
|
267
|
+
- NO_CHECKPOINT: No automatic checkpointing
|
|
268
|
+
|
|
269
|
+
See TypeScript reference: aws-durable-execution-sdk-js/src/types/index.ts
|
|
270
|
+
"""
|
|
271
|
+
|
|
272
|
+
# checkpoint_mode: CheckpointMode = CheckpointMode.CHECKPOINT_AT_START_AND_FINISH
|
|
273
|
+
serdes: SerDes | None = None
|
|
274
|
+
item_serdes: SerDes | None = None
|
|
275
|
+
sub_type: OperationSubType | None = None
|
|
276
|
+
summary_generator: SummaryGenerator | None = None
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
class ItemsPerBatchUnit(Enum):
|
|
280
|
+
COUNT = ("COUNT",)
|
|
281
|
+
BYTES = "BYTES"
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
@dataclass(frozen=True)
|
|
285
|
+
class ItemBatcher(Generic[T]):
|
|
286
|
+
"""Configuration for batching items in map operations.
|
|
287
|
+
|
|
288
|
+
This class defines how individual items should be grouped together into batches
|
|
289
|
+
for more efficient processing in map operations.
|
|
290
|
+
|
|
291
|
+
Args:
|
|
292
|
+
max_items_per_batch: Maximum number of items to include in a single batch.
|
|
293
|
+
If 0 (default), no item count limit is applied. Use this to control
|
|
294
|
+
batch size when processing many small items.
|
|
295
|
+
|
|
296
|
+
max_item_bytes_per_batch: Maximum total size in bytes for items in a batch.
|
|
297
|
+
If 0 (default), no size limit is applied. Use this to control memory
|
|
298
|
+
usage when processing large items or when items vary significantly in size.
|
|
299
|
+
|
|
300
|
+
batch_input: Additional data to include with each batch.
|
|
301
|
+
This data is passed to the processing function along with the batched items.
|
|
302
|
+
Useful for providing context or configuration that applies to all items
|
|
303
|
+
in the batch.
|
|
304
|
+
|
|
305
|
+
Example:
|
|
306
|
+
# Batch up to 100 items or 1MB, whichever comes first
|
|
307
|
+
batcher = ItemBatcher(
|
|
308
|
+
max_items_per_batch=100,
|
|
309
|
+
max_item_bytes_per_batch=1024*1024,
|
|
310
|
+
batch_input={"processing_mode": "fast"}
|
|
311
|
+
)
|
|
312
|
+
"""
|
|
313
|
+
|
|
314
|
+
max_items_per_batch: int = 0
|
|
315
|
+
max_item_bytes_per_batch: int | float = 0
|
|
316
|
+
batch_input: T | None = None
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
@dataclass(frozen=True)
|
|
320
|
+
class MapConfig:
|
|
321
|
+
"""Configuration options for map operations over collections.
|
|
322
|
+
|
|
323
|
+
This class configures how map operations process collections of items,
|
|
324
|
+
including concurrency, batching, completion criteria, and serialization.
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
max_concurrency: Maximum number of items to process concurrently.
|
|
328
|
+
If None, no limit is imposed and all items are processed concurrently.
|
|
329
|
+
Use this to control resource usage when processing large collections.
|
|
330
|
+
|
|
331
|
+
item_batcher: Configuration for batching multiple items together for processing.
|
|
332
|
+
Allows grouping items by count or size to optimize processing efficiency.
|
|
333
|
+
Default is no batching (each item processed individually).
|
|
334
|
+
|
|
335
|
+
completion_config: Defines when the map operation should complete.
|
|
336
|
+
Controls success/failure criteria for the overall map operation.
|
|
337
|
+
Default allows any number of failures. Use CompletionConfig.all_successful()
|
|
338
|
+
to require all items to succeed.
|
|
339
|
+
|
|
340
|
+
serdes: Custom serialization/deserialization configuration for BatchResult.
|
|
341
|
+
Applied at the handler level to serialize the entire BatchResult object.
|
|
342
|
+
If None, uses the default JSON serializer for BatchResult.
|
|
343
|
+
|
|
344
|
+
Backward Compatibility: If only 'serdes' is provided (no item_serdes),
|
|
345
|
+
it will be used for both individual items AND BatchResult serialization
|
|
346
|
+
to maintain existing behavior.
|
|
347
|
+
|
|
348
|
+
item_serdes: Custom serialization/deserialization configuration for individual items.
|
|
349
|
+
Applied to each item's result as tasks complete in child contexts.
|
|
350
|
+
If None, uses the default JSON serializer for individual items.
|
|
351
|
+
|
|
352
|
+
When both 'serdes' and 'item_serdes' are provided:
|
|
353
|
+
- item_serdes: Used for individual item results in child contexts
|
|
354
|
+
- serdes: Used for the entire BatchResult at handler level
|
|
355
|
+
|
|
356
|
+
summary_generator: Function to generate compact summaries for large results (>256KB).
|
|
357
|
+
When the serialized result exceeds CHECKPOINT_SIZE_LIMIT, this generator
|
|
358
|
+
creates a JSON summary instead of checkpointing the full result. The operation
|
|
359
|
+
is marked with ReplayChildren=true to reconstruct the full result during replay.
|
|
360
|
+
|
|
361
|
+
Used internally by map/parallel operations to handle large BatchResult payloads.
|
|
362
|
+
Signature: (result: T) -> str
|
|
363
|
+
|
|
364
|
+
Example:
|
|
365
|
+
# Process 5 items at a time, batch by count, require all to succeed
|
|
366
|
+
config = MapConfig(
|
|
367
|
+
max_concurrency=5,
|
|
368
|
+
item_batcher=ItemBatcher(max_items_per_batch=10),
|
|
369
|
+
completion_config=CompletionConfig.all_successful()
|
|
370
|
+
)
|
|
371
|
+
"""
|
|
372
|
+
|
|
373
|
+
max_concurrency: int | None = None
|
|
374
|
+
item_batcher: ItemBatcher = field(default_factory=ItemBatcher)
|
|
375
|
+
completion_config: CompletionConfig = field(default_factory=CompletionConfig)
|
|
376
|
+
serdes: SerDes | None = None
|
|
377
|
+
item_serdes: SerDes | None = None
|
|
378
|
+
summary_generator: SummaryGenerator | None = None
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
@dataclass(frozen=True)
|
|
382
|
+
class InvokeConfig(Generic[P, R]):
|
|
383
|
+
"""
|
|
384
|
+
Configuration for invoke operations.
|
|
385
|
+
|
|
386
|
+
This class configures how function invocations are executed, including
|
|
387
|
+
timeout behavior, serialization, and tenant isolation.
|
|
388
|
+
|
|
389
|
+
Args:
|
|
390
|
+
timeout: Maximum duration to wait for the invoked function to complete.
|
|
391
|
+
Default is no timeout. Use this to prevent long-running invocations
|
|
392
|
+
from blocking execution indefinitely.
|
|
393
|
+
|
|
394
|
+
serdes_payload: Custom serialization/deserialization for the payload
|
|
395
|
+
sent to the invoked function. Defaults to DEFAULT_JSON_SERDES when
|
|
396
|
+
not set.
|
|
397
|
+
|
|
398
|
+
serdes_result: Custom serialization/deserialization for the result
|
|
399
|
+
returned from the invoked function. Defaults to DEFAULT_JSON_SERDES when
|
|
400
|
+
not set.
|
|
401
|
+
|
|
402
|
+
tenant_id: Optional tenant identifier for multi-tenant isolation.
|
|
403
|
+
If provided, the invocation will be scoped to this tenant.
|
|
404
|
+
"""
|
|
405
|
+
|
|
406
|
+
# retry_strategy: Callable[[Exception, int], RetryDecision] | None = None
|
|
407
|
+
timeout: Duration = field(default_factory=Duration)
|
|
408
|
+
serdes_payload: SerDes[P] | None = None
|
|
409
|
+
serdes_result: SerDes[R] | None = None
|
|
410
|
+
tenant_id: str | None = None
|
|
411
|
+
|
|
412
|
+
@property
|
|
413
|
+
def timeout_seconds(self) -> int:
|
|
414
|
+
"""Get timeout in seconds."""
|
|
415
|
+
return self.timeout.to_seconds()
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
@dataclass(frozen=True)
|
|
419
|
+
class CallbackConfig:
|
|
420
|
+
"""Configuration for callbacks."""
|
|
421
|
+
|
|
422
|
+
timeout: Duration = field(default_factory=Duration)
|
|
423
|
+
heartbeat_timeout: Duration = field(default_factory=Duration)
|
|
424
|
+
serdes: SerDes | None = None
|
|
425
|
+
|
|
426
|
+
@property
|
|
427
|
+
def timeout_seconds(self) -> int:
|
|
428
|
+
"""Get timeout in seconds."""
|
|
429
|
+
return self.timeout.to_seconds()
|
|
430
|
+
|
|
431
|
+
@property
|
|
432
|
+
def heartbeat_timeout_seconds(self) -> int:
|
|
433
|
+
"""Get heartbeat timeout in seconds."""
|
|
434
|
+
return self.heartbeat_timeout.to_seconds()
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
@dataclass(frozen=True)
|
|
438
|
+
class WaitForCallbackConfig(CallbackConfig):
|
|
439
|
+
"""Configuration for wait for callback."""
|
|
440
|
+
|
|
441
|
+
retry_strategy: Callable[[Exception, int], RetryDecision] | None = None
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
class StepFuture(Generic[T]):
|
|
445
|
+
"""A future that will block on result() until the step returns."""
|
|
446
|
+
|
|
447
|
+
def __init__(self, future: Future[T], name: str | None = None):
|
|
448
|
+
self.name = name
|
|
449
|
+
self.future = future
|
|
450
|
+
|
|
451
|
+
def result(self, timeout_seconds: int | None = None) -> T:
|
|
452
|
+
"""Return the result of the Future."""
|
|
453
|
+
return self.future.result(timeout=timeout_seconds)
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
# region Jitter
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
class JitterStrategy(StrEnum):
|
|
460
|
+
"""
|
|
461
|
+
Jitter strategies are used to introduce noise when attempting to retry
|
|
462
|
+
an invoke. We introduce noise to prevent a thundering-herd effect where
|
|
463
|
+
a group of accesses (e.g. invokes) happen at once.
|
|
464
|
+
|
|
465
|
+
Jitter is meant to be used to spread operations across time.
|
|
466
|
+
|
|
467
|
+
Based on AWS Architecture Blog: https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
|
|
468
|
+
|
|
469
|
+
members:
|
|
470
|
+
:NONE: No jitter; use the exact calculated delay
|
|
471
|
+
:FULL: Full jitter; random delay between 0 and calculated delay
|
|
472
|
+
:HALF: Equal jitter; random delay between 0.5x and 1.0x of the calculated delay
|
|
473
|
+
"""
|
|
474
|
+
|
|
475
|
+
NONE = "NONE"
|
|
476
|
+
FULL = "FULL"
|
|
477
|
+
HALF = "HALF"
|
|
478
|
+
|
|
479
|
+
def apply_jitter(self, delay: float) -> float:
|
|
480
|
+
"""Apply jitter to a delay value and return the final delay.
|
|
481
|
+
|
|
482
|
+
Args:
|
|
483
|
+
delay: The base delay value to apply jitter to
|
|
484
|
+
|
|
485
|
+
Returns:
|
|
486
|
+
The final delay after applying jitter strategy
|
|
487
|
+
"""
|
|
488
|
+
match self:
|
|
489
|
+
case JitterStrategy.NONE:
|
|
490
|
+
return delay
|
|
491
|
+
case JitterStrategy.HALF:
|
|
492
|
+
# Equal jitter: delay/2 + random(0, delay/2)
|
|
493
|
+
return delay / 2 + random.random() * (delay / 2) # noqa: S311
|
|
494
|
+
case _: # default is FULL
|
|
495
|
+
# Full jitter: random(0, delay)
|
|
496
|
+
return random.random() * delay # noqa: S311
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
# endregion Jitter
|