-
Notifications
You must be signed in to change notification settings - Fork 17
feat: with retry helper #390
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
wangyb-A
wants to merge
3
commits into
main
Choose a base branch
from
feat/withRetry
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
68 changes: 68 additions & 0 deletions
68
packages/aws-durable-execution-sdk-python-examples/src/with_retry/with_retry_callback.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| """Demonstrates with_retry wrapping a wait_for_callback operation. | ||
|
|
||
| The callback may fail multiple times before succeeding. The with_retry helper | ||
| retries the entire callback flow (including creating a new callback each attempt) | ||
| with exponential backoff between attempts. | ||
| """ | ||
|
|
||
| from typing import Any | ||
|
|
||
| from aws_durable_execution_sdk_python.config import Duration, WaitForCallbackConfig | ||
| from aws_durable_execution_sdk_python.context import DurableContext | ||
| from aws_durable_execution_sdk_python.execution import durable_execution | ||
| from aws_durable_execution_sdk_python.retries import ( | ||
| RetryStrategyConfig, | ||
| WithRetryConfig, | ||
| create_retry_strategy, | ||
| with_retry, | ||
| ) | ||
|
|
||
|
|
||
| @durable_execution | ||
| def handler(_event: Any, context: DurableContext) -> dict[str, Any]: | ||
| """Handler demonstrating with_retry around a wait_for_callback. | ||
|
|
||
| The external system may fail to process the callback multiple times. | ||
| with_retry will re-create the callback and wait again on each retry, | ||
| with exponential backoff between attempts. | ||
| """ | ||
|
|
||
| def retryable_callback_flow(ctx: DurableContext, attempt: int) -> str: | ||
| """The retryable block: create a callback and wait for the result.""" | ||
|
|
||
| def submitter(callback_id: str, _callback_ctx) -> None: | ||
| """Submit the callback ID to an external system.""" | ||
| # In real usage, this would send the callback_id to an external | ||
| # system (e.g., via API call, SQS message, etc.) | ||
| pass | ||
|
|
||
| config = WaitForCallbackConfig( | ||
| timeout=Duration.from_seconds(30), | ||
| heartbeat_timeout=Duration.from_seconds(60), | ||
| ) | ||
|
|
||
| return ctx.wait_for_callback( | ||
| submitter, name=f"external-call-attempt-{attempt}", config=config | ||
| ) | ||
|
|
||
| retry_config = WithRetryConfig( | ||
| retry_strategy=create_retry_strategy( | ||
| RetryStrategyConfig( | ||
| max_attempts=5, | ||
| initial_delay=Duration.from_seconds(2), | ||
| backoff_rate=1.0, | ||
| ) | ||
| ), | ||
| ) | ||
|
|
||
| result = with_retry( | ||
| context, | ||
| func=retryable_callback_flow, | ||
| config=retry_config, | ||
| name="callback-with-retry", | ||
| ) | ||
|
|
||
| return { | ||
| "success": True, | ||
| "result": result, | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
69 changes: 69 additions & 0 deletions
69
...ges/aws-durable-execution-sdk-python-examples/test/with_retry/test_with_retry_callback.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| """Tests for with_retry_callback example. | ||
|
|
||
| Demonstrates that with_retry retries the entire wait_for_callback flow | ||
| when the callback fails. The external system fails 2 times before | ||
| succeeding on the 3rd attempt. | ||
| """ | ||
|
|
||
| import pytest | ||
| from src.with_retry import with_retry_callback | ||
| from test.conftest import deserialize_operation_payload | ||
|
|
||
| from aws_durable_execution_sdk_python.execution import InvocationStatus | ||
| from aws_durable_execution_sdk_python.lambda_service import ErrorObject | ||
|
|
||
|
|
||
| @pytest.mark.example | ||
| @pytest.mark.durable_execution( | ||
| handler=with_retry_callback.handler, | ||
| lambda_function_name="With Retry Callback", | ||
| ) | ||
| def test_with_retry_callback_fails_twice_then_succeeds(durable_runner): | ||
| """Test that with_retry retries the callback flow after failures. | ||
|
|
||
| The external system sends callback failure 2 times, then succeeds | ||
| on the 3rd attempt. with_retry handles the failures and retries | ||
| the entire wait_for_callback block. | ||
| """ | ||
| with durable_runner: | ||
| execution_arn = durable_runner.run_async(input=None, timeout=60) | ||
|
|
||
| # Attempt 1: external system fails | ||
| callback_id_1 = durable_runner.wait_for_callback( | ||
| execution_arn=execution_arn, | ||
| name="external-call-attempt-1 create callback id", | ||
| ) | ||
| durable_runner.send_callback_failure( | ||
| callback_id=callback_id_1, | ||
| error=ErrorObject.from_message("External system unavailable"), | ||
| ) | ||
|
|
||
| # Attempt 2: external system fails again | ||
| callback_id_2 = durable_runner.wait_for_callback( | ||
| execution_arn=execution_arn, | ||
| name="external-call-attempt-2 create callback id", | ||
| ) | ||
| durable_runner.send_callback_failure( | ||
| callback_id=callback_id_2, | ||
| error=ErrorObject.from_message("External system timeout"), | ||
| ) | ||
|
|
||
| # Attempt 3: external system succeeds | ||
| callback_id_3 = durable_runner.wait_for_callback( | ||
| execution_arn=execution_arn, | ||
| name="external-call-attempt-3 create callback id", | ||
| ) | ||
| durable_runner.send_callback_success( | ||
| callback_id=callback_id_3, | ||
| result="approval granted".encode(), | ||
| ) | ||
|
|
||
| result = durable_runner.wait_for_result(execution_arn=execution_arn) | ||
|
|
||
| assert result.status is InvocationStatus.SUCCEEDED | ||
|
|
||
| result_data = deserialize_operation_payload(result.result) | ||
| assert result_data == { | ||
| "success": True, | ||
| "result": "approval granted", | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.