Skip to content

Commit 619b09e

Browse files
committed
fix: remove timeout from Invoke Config (v2 breaking change)
1 parent 9277fff commit 619b09e

7 files changed

Lines changed: 26 additions & 175 deletions

File tree

docs/architecture.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,6 @@ classDiagram
176176
}
177177
178178
class InvokeConfig~P,R~ {
179-
+int timeout_seconds
180179
+SerDes~P~ serdes_payload
181180
+SerDes~R~ serdes_result
182181
}

docs/core/invoke.md

Lines changed: 8 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@
2828

2929
**Payload** - The input data sent to the invoked function. Can be any JSON-serializable value or use custom serialization.
3030

31-
**Timeout** - The maximum time to wait for an invoked function to complete. If exceeded, the invoke operation fails with a timeout error.
32-
3331
[↑ Back to top](#table-of-contents)
3432

3533
## What are invoke operations?
@@ -59,7 +57,6 @@ When you invoke a function, the SDK:
5957
- **Asynchronous execution** - Invoked functions run independently without blocking resources
6058
- **Result handling** - Results are automatically deserialized and returned
6159
- **Error propagation** - Errors from invoked functions propagate to the caller
62-
- **Timeout support** - Configure maximum wait time for invoked functions
6360
- **Custom serialization** - Control how payloads and results are serialized
6461
- **Named operations** - Identify invoke operations by name for debugging
6562

@@ -131,7 +128,7 @@ def invoke(
131128
- `function_name` - The name of the Lambda function to invoke. This should be the function name, not the ARN.
132129
- `payload` - The input data to send to the invoked function. Can be any JSON-serializable value.
133130
- `name` (optional) - A name for the invoke operation, useful for debugging and testing.
134-
- `config` (optional) - An `InvokeConfig` object to configure timeout and serialization.
131+
- `config` (optional) - An `InvokeConfig` object to configure serialization and tenant isolation.
135132

136133
**Returns:** The result returned by the invoked function.
137134

@@ -291,52 +288,33 @@ from aws_durable_execution_sdk_python import (
291288
DurableContext,
292289
durable_execution,
293290
)
294-
from aws_durable_execution_sdk_python.config import Duration, InvokeConfig
291+
from aws_durable_execution_sdk_python.config import InvokeConfig
295292

296293
@durable_execution
297294
def handler(event: dict, context: DurableContext) -> dict:
298-
# Configure invoke with timeout
299295
invoke_config = InvokeConfig(
300-
timeout=Duration.from_minutes(5),
296+
serdes_payload=my_payload_serdes,
297+
serdes_result=my_result_serdes,
301298
)
302-
299+
303300
result = context.invoke(
304-
function_name="long-running-function",
301+
function_name="my-function",
305302
payload=event,
306-
name="long_running",
303+
name="my_invoke",
307304
config=invoke_config,
308305
)
309-
306+
310307
return result
311308
```
312309

313310
### InvokeConfig parameters
314311

315-
**timeout** - Maximum duration to wait for the invoked function to complete. Default is no timeout. Use this to prevent long-running invocations from blocking execution indefinitely.
316-
317312
**serdes_payload** - Custom serialization/deserialization for the payload sent to the invoked function. If None, uses default JSON serialization.
318313

319314
**serdes_result** - Custom serialization/deserialization for the result returned from the invoked function. If None, uses default JSON serialization.
320315

321316
**tenant_id** - Optional tenant identifier for multi-tenant isolation. If provided, the invocation will be scoped to this tenant.
322317

323-
### Setting timeouts
324-
325-
Use the `Duration` class to set timeouts:
326-
327-
```python
328-
from aws_durable_execution_sdk_python.config import Duration, InvokeConfig
329-
330-
# Timeout after 30 seconds
331-
config = InvokeConfig(timeout=Duration.from_seconds(30))
332-
333-
# Timeout after 5 minutes
334-
config = InvokeConfig(timeout=Duration.from_minutes(5))
335-
336-
# Timeout after 2 hours
337-
config = InvokeConfig(timeout=Duration.from_hours(2))
338-
```
339-
340318
[↑ Back to top](#table-of-contents)
341319

342320
## Error handling
@@ -372,33 +350,6 @@ def handler(event: dict, context: DurableContext) -> dict:
372350
}
373351
```
374352

375-
### Timeout handling
376-
377-
Handle timeout errors specifically:
378-
379-
```python
380-
from aws_durable_execution_sdk_python.config import Duration, InvokeConfig
381-
382-
@durable_execution
383-
def handler(event: dict, context: DurableContext) -> dict:
384-
"""Handle timeout errors."""
385-
config = InvokeConfig(timeout=Duration.from_seconds(30))
386-
387-
try:
388-
result = context.invoke(
389-
function_name="slow-function",
390-
payload=event,
391-
config=config,
392-
)
393-
return {"status": "success", "result": result}
394-
395-
except CallableRuntimeError as e:
396-
if "timed out" in str(e).lower():
397-
context.logger.warning("Function timed out, using fallback")
398-
return {"status": "timeout", "fallback": True}
399-
raise
400-
```
401-
402353
### Retry patterns
403354

404355
Implement retry logic for failed invocations:
@@ -551,8 +502,6 @@ def handler(event: dict, context: DurableContext) -> dict:
551502

552503
**Name invoke operations** - Use the `name` parameter to identify invoke operations in logs and tests.
553504

554-
**Set appropriate timeouts** - Configure timeouts based on expected execution time. Don't set them too short or too long.
555-
556505
**Handle errors explicitly** - Catch and handle errors from invoked functions. Don't let them propagate unexpectedly.
557506

558507
**Keep payloads small** - Large payloads increase serialization overhead. Consider passing references instead of large data.
@@ -603,10 +552,6 @@ A: Yes, you can invoke the same function multiple times with different payloads
603552

604553
A: The `function_name` parameter accepts function names in the same account. For cross-account invocations, you need appropriate IAM permissions and may need to use function ARNs (check AWS documentation for cross-account Lambda invocations).
605554

606-
**Q: What's the maximum timeout I can set?**
607-
608-
A: The timeout is limited by Lambda's maximum execution time (15 minutes). However, durable functions can run longer by suspending and resuming.
609-
610555
**Q: Can I invoke functions in parallel?**
611556

612557
A: Not directly with `context.invoke()`. For parallel execution, consider using `context.parallel()` with steps that perform invocations, or invoke multiple functions sequentially.
@@ -615,10 +560,6 @@ A: Not directly with `context.invoke()`. For parallel execution, consider using
615560

616561
A: Use the `name` parameter to identify operations in logs. Check CloudWatch logs for both the calling and invoked functions.
617562

618-
**Q: What happens if I don't set a timeout?**
619-
620-
A: The invoke operation waits indefinitely for the invoked function to complete. It's recommended to set timeouts for better error handling.
621-
622563
**Q: What's the difference between context.invoke() and using boto3's Lambda client to invoke functions?**
623564

624565
A: When you use `context.invoke()`, the SDK suspends your durable function's execution while waiting for the result. This means you don't pay for Lambda compute time while waiting. With boto3's Lambda client, your function stays active and consumes billable compute time while waiting for the response. Additionally, `context.invoke()` automatically checkpoints the operation, handles errors durably, and integrates with the durable execution lifecycle.
@@ -703,27 +644,6 @@ def test_invoke_error_handling(durable_runner):
703644
assert "error" in result.result
704645
```
705646

706-
### Testing timeouts
707-
708-
Test that timeouts are handled correctly:
709-
710-
```python
711-
from aws_durable_execution_sdk_python.config import Duration, InvokeConfig
712-
713-
@pytest.mark.durable_execution(
714-
handler=handler_with_timeout,
715-
lambda_function_name="timeout_function",
716-
)
717-
def test_invoke_timeout(durable_runner):
718-
"""Test invoke timeout handling."""
719-
with durable_runner:
720-
result = durable_runner.run(input={}, timeout=60)
721-
722-
# Check that timeout was handled
723-
assert result.status is InvocationStatus.SUCCEEDED
724-
assert result.result["status"] == "timeout"
725-
```
726-
727647
### Mocking invoked functions
728648

729649
When testing, you can mock the invoked functions to control their behavior:

src/aws_durable_execution_sdk_python/config.py

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -384,13 +384,9 @@ class InvokeConfig(Generic[P, R]):
384384
Configuration for invoke operations.
385385
386386
This class configures how function invocations are executed, including
387-
timeout behavior, serialization, and tenant isolation.
387+
serialization and tenant isolation.
388388
389389
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-
394390
serdes_payload: Custom serialization/deserialization for the payload
395391
sent to the invoked function. Defaults to DEFAULT_JSON_SERDES when
396392
not set.
@@ -404,16 +400,10 @@ class InvokeConfig(Generic[P, R]):
404400
"""
405401

406402
# retry_strategy: Callable[[Exception, int], RetryDecision] | None = None
407-
timeout: Duration = field(default_factory=Duration)
408403
serdes_payload: SerDes[P] | None = None
409404
serdes_result: SerDes[R] | None = None
410405
tenant_id: str | None = None
411406

412-
@property
413-
def timeout_seconds(self) -> int:
414-
"""Get timeout in seconds."""
415-
return self.timeout.to_seconds()
416-
417407

418408
@dataclass(frozen=True)
419409
class CallbackConfig:

src/aws_durable_execution_sdk_python/operation/invoke.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ def execute(self, _checkpointed_result: CheckpointedResult) -> R:
166166
ExecutionError: If suspend doesn't raise (should never happen)
167167
"""
168168
msg: str = f"Invoke {self.operation_identifier.operation_id} started, suspending for completion"
169-
suspend_with_optional_resume_delay(msg, self.config.timeout_seconds)
169+
suspend_with_optional_resume_delay(msg)
170170
# This line should never be reached since suspend_with_optional_resume_delay always raises
171171
error_msg: str = "suspend_with_optional_resume_delay should have raised an exception, but did not."
172172
raise ExecutionError(error_msg) from None

tests/config_test.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,6 @@ def test_invoke_config_defaults():
282282
"""Test InvokeConfig defaults."""
283283
config = InvokeConfig()
284284
assert config.tenant_id is None
285-
assert config.timeout_seconds == 0
286285

287286

288287
def test_invoke_config_with_tenant_id():

tests/context_test.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,7 @@ def test_invoke_with_name_and_config(mock_executor_class):
616616
mock_state.durable_execution_arn = (
617617
"arn:aws:durable:us-east-1:123456789012:execution/test"
618618
)
619-
config = InvokeConfig[str, str](timeout=Duration.from_seconds(30))
619+
config = InvokeConfig[str, str]()
620620

621621
context = create_test_context(state=mock_state)
622622
[context._create_step_id() for _ in range(5)] # Set counter to 5 # noqa: SLF001
@@ -756,7 +756,6 @@ def test_invoke_with_custom_serdes(mock_executor_class):
756756
config = InvokeConfig[dict, dict](
757757
serdes_payload=payload_serdes,
758758
serdes_result=result_serdes,
759-
timeout=Duration.from_minutes(1),
760759
)
761760

762761
context = create_test_context(state=mock_state)

0 commit comments

Comments
 (0)