diff --git a/packages/aws-durable-execution-sdk-python/src/aws_durable_execution_sdk_python/config.py b/packages/aws-durable-execution-sdk-python/src/aws_durable_execution_sdk_python/config.py index 6763a53e..71eb01ea 100644 --- a/packages/aws-durable-execution-sdk-python/src/aws_durable_execution_sdk_python/config.py +++ b/packages/aws-durable-execution-sdk-python/src/aws_durable_execution_sdk_python/config.py @@ -479,13 +479,9 @@ class InvokeConfig(Generic[P, R]): Configuration for invoke operations. This class configures how function invocations are executed, including - timeout behavior, serialization, and tenant isolation. + serialization and tenant isolation. Args: - 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. - serdes_payload: Custom serialization/deserialization for the payload sent to the invoked function. Defaults to DEFAULT_JSON_SERDES when not set. @@ -499,16 +495,10 @@ class InvokeConfig(Generic[P, R]): """ # retry_strategy: Callable[[Exception, int], RetryDecision] | None = None - timeout: Duration = field(default_factory=Duration) serdes_payload: SerDes[P] | None = None serdes_result: SerDes[R] | None = None tenant_id: str | None = None - @property - def timeout_seconds(self) -> int: - """Get timeout in seconds.""" - return self.timeout.to_seconds() - @dataclass(frozen=True) class CallbackConfig: diff --git a/packages/aws-durable-execution-sdk-python/src/aws_durable_execution_sdk_python/operation/invoke.py b/packages/aws-durable-execution-sdk-python/src/aws_durable_execution_sdk_python/operation/invoke.py index 9288c983..6d181341 100644 --- a/packages/aws-durable-execution-sdk-python/src/aws_durable_execution_sdk_python/operation/invoke.py +++ b/packages/aws-durable-execution-sdk-python/src/aws_durable_execution_sdk_python/operation/invoke.py @@ -166,7 +166,7 @@ def execute(self, _checkpointed_result: CheckpointedResult) -> R: ExecutionError: If suspend doesn't raise (should never happen) """ msg: str = f"Invoke {self.operation_identifier.operation_id} started, suspending for completion" - suspend_with_optional_resume_delay(msg, self.config.timeout_seconds) + suspend_with_optional_resume_delay(msg) # This line should never be reached since suspend_with_optional_resume_delay always raises error_msg: str = "suspend_with_optional_resume_delay should have raised an exception, but did not." raise ExecutionError(error_msg) from None diff --git a/packages/aws-durable-execution-sdk-python/tests/config_test.py b/packages/aws-durable-execution-sdk-python/tests/config_test.py index a8af487d..17069355 100644 --- a/packages/aws-durable-execution-sdk-python/tests/config_test.py +++ b/packages/aws-durable-execution-sdk-python/tests/config_test.py @@ -271,7 +271,6 @@ def test_invoke_config_defaults(): """Test InvokeConfig defaults.""" config = InvokeConfig() assert config.tenant_id is None - assert config.timeout_seconds == 0 def test_invoke_config_with_tenant_id(): diff --git a/packages/aws-durable-execution-sdk-python/tests/context_test.py b/packages/aws-durable-execution-sdk-python/tests/context_test.py index d0061aa9..d91c0951 100644 --- a/packages/aws-durable-execution-sdk-python/tests/context_test.py +++ b/packages/aws-durable-execution-sdk-python/tests/context_test.py @@ -644,7 +644,7 @@ def test_invoke_with_name_and_config(mock_executor_class): mock_state.durable_execution_arn = ( "arn:aws:durable:us-east-1:123456789012:execution/test" ) - config = InvokeConfig[str, str](timeout=Duration.from_seconds(30)) + config = InvokeConfig[str, str]() context = create_test_context(state=mock_state) [context._create_step_id() for _ in range(5)] # Set counter to 5 # noqa: SLF001 @@ -790,7 +790,6 @@ def test_invoke_with_custom_serdes(mock_executor_class): config = InvokeConfig[dict, dict]( serdes_payload=payload_serdes, serdes_result=result_serdes, - timeout=Duration.from_minutes(1), ) context = create_test_context(state=mock_state) diff --git a/packages/aws-durable-execution-sdk-python/tests/operation/invoke_test.py b/packages/aws-durable-execution-sdk-python/tests/operation/invoke_test.py index c6a247f1..dda39b21 100644 --- a/packages/aws-durable-execution-sdk-python/tests/operation/invoke_test.py +++ b/packages/aws-durable-execution-sdk-python/tests/operation/invoke_test.py @@ -7,7 +7,7 @@ import pytest -from aws_durable_execution_sdk_python.config import Duration, InvokeConfig +from aws_durable_execution_sdk_python.config import InvokeConfig from aws_durable_execution_sdk_python.exceptions import ( CallableRuntimeError, ExecutionError, @@ -218,8 +218,8 @@ def test_invoke_handler_already_started(status): @pytest.mark.parametrize("status", [OperationStatus.STARTED, OperationStatus.PENDING]) -def test_invoke_handler_already_started_with_timeout(status): - """Test invoke_handler when operation is already started with timeout config.""" +def test_invoke_handler_already_started_suspends(status): + """Test invoke_handler when operation is already started suspends indefinitely.""" mock_state = Mock(spec=ExecutionState) mock_state.durable_execution_arn = "test_arn" @@ -232,9 +232,9 @@ def test_invoke_handler_already_started_with_timeout(status): mock_result = CheckpointedResult.create_from_operation(operation) mock_state.get_checkpoint_result.return_value = mock_result - config = InvokeConfig[str, str](timeout=Duration.from_seconds(30)) + config = InvokeConfig[str, str]() - with pytest.raises(TimedSuspendExecution): + with pytest.raises(SuspendExecution) as exc_info: invoke_handler( function_name="test_function", payload="test_input", @@ -244,6 +244,7 @@ def test_invoke_handler_already_started_with_timeout(status): ), config=config, ) + assert type(exc_info.value) is SuspendExecution def test_invoke_handler_new_operation(): @@ -261,7 +262,7 @@ def test_invoke_handler_new_operation(): started = CheckpointedResult.create_from_operation(started_op) mock_state.get_checkpoint_result.side_effect = [not_found, started] - config = InvokeConfig[str, str](timeout=Duration.from_minutes(1)) + config = InvokeConfig[str, str]() with pytest.raises( SuspendExecution, match="Invoke invoke8 started, suspending for completion" @@ -288,62 +289,6 @@ def test_invoke_handler_new_operation(): assert operation_update.chained_invoke_options.function_name == "test_function" -def test_invoke_handler_new_operation_with_timeout(): - """Test invoke_handler when starting a new operation with timeout.""" - mock_state = Mock(spec=ExecutionState) - mock_state.durable_execution_arn = "test_arn" - - not_found = CheckpointedResult.create_not_found() - started_op = Operation( - operation_id="invoke_test", - operation_type=OperationType.CHAINED_INVOKE, - status=OperationStatus.STARTED, - ) - started = CheckpointedResult.create_from_operation(started_op) - mock_state.get_checkpoint_result.side_effect = [not_found, started] - - config = InvokeConfig[str, str](timeout=Duration.from_seconds(30)) - - with pytest.raises(TimedSuspendExecution): - invoke_handler( - function_name="test_function", - payload="test_input", - state=mock_state, - operation_identifier=OperationIdentifier( - "invoke9", OperationSubType.CHAINED_INVOKE, None, "test_invoke" - ), - config=config, - ) - - -def test_invoke_handler_new_operation_no_timeout(): - """Test invoke_handler when starting a new operation without timeout.""" - mock_state = Mock(spec=ExecutionState) - mock_state.durable_execution_arn = "test_arn" - - not_found = CheckpointedResult.create_not_found() - started_op = Operation( - operation_id="invoke_test", - operation_type=OperationType.CHAINED_INVOKE, - status=OperationStatus.STARTED, - ) - started = CheckpointedResult.create_from_operation(started_op) - mock_state.get_checkpoint_result.side_effect = [not_found, started] - - config = InvokeConfig[str, str](timeout=Duration.from_seconds(0)) - - with pytest.raises(SuspendExecution): - invoke_handler( - function_name="test_function", - payload="test_input", - state=mock_state, - operation_identifier=OperationIdentifier( - "invoke10", OperationSubType.CHAINED_INVOKE, None, "test_invoke" - ), - config=config, - ) - - def test_invoke_handler_no_config(): """Test invoke_handler when no config is provided.""" mock_state = Mock(spec=ExecutionState) @@ -1067,79 +1012,6 @@ def test_invoke_immediate_response_already_completed(): assert mock_state.get_checkpoint_result.call_count == 1 -def test_invoke_immediate_response_with_timeout_immediate_success(): - """Test immediate success with timeout configuration.""" - mock_state = Mock(spec=ExecutionState) - mock_state.durable_execution_arn = "test_arn" - - # First call: not found, second call: succeeded - not_found = CheckpointedResult.create_not_found() - succeeded_op = Operation( - operation_id="invoke_immediate_8", - operation_type=OperationType.CHAINED_INVOKE, - status=OperationStatus.SUCCEEDED, - chained_invoke_details=ChainedInvokeDetails( - result=json.dumps("timeout_result") - ), - ) - succeeded = CheckpointedResult.create_from_operation(succeeded_op) - mock_state.get_checkpoint_result.side_effect = [not_found, succeeded] - - config = InvokeConfig[str, str](timeout=Duration.from_seconds(30)) - - result = invoke_handler( - function_name="test_function", - payload="test_input", - state=mock_state, - operation_identifier=OperationIdentifier( - "invoke_immediate_8", OperationSubType.CHAINED_INVOKE, None, "test_invoke" - ), - config=config, - ) - - # Verify result was returned without suspend - assert result == "timeout_result" - assert mock_state.get_checkpoint_result.call_count == 2 - - -def test_invoke_immediate_response_with_timeout_no_immediate_response(): - """Test no immediate response with timeout configuration. - - When no immediate response, operation should suspend with timeout. - """ - mock_state = Mock(spec=ExecutionState) - mock_state.durable_execution_arn = "test_arn" - - # First call: not found, second call: started - not_found = CheckpointedResult.create_not_found() - started_op = Operation( - operation_id="invoke_immediate_9", - operation_type=OperationType.CHAINED_INVOKE, - status=OperationStatus.STARTED, - ) - started = CheckpointedResult.create_from_operation(started_op) - mock_state.get_checkpoint_result.side_effect = [not_found, started] - - config = InvokeConfig[str, str](timeout=Duration.from_seconds(30)) - - # Verify operation suspends with timeout - with pytest.raises(TimedSuspendExecution): - invoke_handler( - function_name="test_function", - payload="test_input", - state=mock_state, - operation_identifier=OperationIdentifier( - "invoke_immediate_9", - OperationSubType.CHAINED_INVOKE, - None, - "test_invoke", - ), - config=config, - ) - - assert mock_state.get_checkpoint_result.call_count == 2 - - def test_invoke_immediate_response_with_custom_serdes(): """Test immediate success with custom serialization.""" mock_state = Mock(spec=ExecutionState)