Repository navigation
feat(extras): rely on MicroVM auto-resume - #913
Open
ParidelPooya wants to merge 5 commits into
Open
ParidelPooya wants to merge 5 commits into
ParidelPooya wants to merge 5 commits into
Conversation
The service resumes a suspended MicroVM on the next request when its idle policy sets autoResumeEnabled. So the SDK no longer resumes a MicroVM itself. - Remove ensure_running, its poll loop and EnsureRunningOptions. - Rename resume_if_suspended to check_not_ended. It makes one GetMicrovm call after a 502 MICROVM_CONNECT_FAILED or a request without an answer. TERMINATING, TERMINATED and not found fail at once. A denied call fails at once. Any other state keeps the job retrying. Any other error logs one warning per step attempt. - Remove MicrovmResumeDeniedError and the ResumeMicrovm call. - Stop writing resumeBeforeDelivery in the launch checkpoint. from_dict ignores the field, so an old checkpoint still replays. - Remove the in-memory suspend mark of a session handle and the recheck path of send_job. The test fake now fails any test that calls ResumeMicrovm.
A session that suspends its MicroVM must set an idle policy with autoResumeEnabled. The README now says so, with the idle timer rule for maxIdleDurationSeconds, the failure without the policy, the extra second of the first job after a suspend, and the replay of a launch checkpoint from the earlier release. - Remove lambda:ResumeMicrovm from the permissions. - Add a section on the state check, and remove the in-memory suspend record and the resume steps. - Update the worker README and one worker docstring: the service resumes the MicroVM, not the durable function.
The SDK no longer resumes a suspended MicroVM. So the two examples that suspend their MicroVM set an idle policy with autoResumeEnabled, a maxIdleDurationSeconds above the MicroVM's maximum duration, and a suspendedDurationSeconds of the maximum duration. The cloud tests keep their SUSPENDED-between-jobs and file-kept assertions. They now expect a launch checkpoint without resumeBeforeDelivery. The examples test README drops lambda:ResumeMicrovm from the role needs.
The rebase onto the dead-MicroVM fix keeps its order: after a sign, the state check runs first, and only the invocation reserve limits it. The check no longer waits for a resume. So the test of the resume-wait cap now proves that an attempt without a deadline ends, and the denial test covers only GetMicrovm. The worker README now says that the service resumes the MicroVM after a 503.
- Say that a job sent right after a suspend can take up to about 10 seconds. A retry_window under about 9 seconds relies on the step retry strategy for the rest of the 5-second window. - Add a suspend_after session without an idle_policy to the in-flight note. - Place the 30-second worker refusal after the suspend. - Use the Python names idle_policy and True, and the session timeout. - Add the suspendedDurationSeconds guideline. Its end is not verified. - Say that the service resumes the MicroVM in the worker docs. - Rewrap a long docstring line in an example.
ParidelPooya
marked this pull request as ready for review
November 7, 2026 13:19
Contributor
Codex AI reviewNo actionable findings, including against CONTRIBUTING rules 1–9. Tests were not executed during this read-only review. Residual risk: the PR explicitly marks auto-resume after worker-initiated suspension as unverified. Reviewed commit |
This branch was successfully deployed
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
The service resumes a suspended MicroVM on the next request when its idle policy sets
autoResumeEnabled. So the MicroVM operations no longer resume a MicroVM themselves. The SDK never callsResumeMicrovm, and the function's role no longer needslambda:ResumeMicrovm.This change builds on #910, now on
main. It keeps the order of the state check from #910: after a sign that the MicroVM does not run, the state check runs first, and only the invocation reserve limits it. It keeps the one internal denial error,MicrovmPermissionDeniedError. Only theGetMicrovmcase remains, so its message nameslambda:GetMicrovm.Twin PR: aws/aws-durable-execution-sdk-js#1182
Design note (CONTRIBUTING rule 3)
1. Problem
The MicroVM operations resume a suspended MicroVM themselves:
GetMicrovm,ResumeMicrovm, polling untilRUNNING, a recorded resume mode, and a "may be suspended" mark aftersuspend_after. This is a large share of the code and of the review rounds, and it needslambda:ResumeMicrovmin the function role. The service can do the same job.2. Cause
The SDK was built before
IdlePolicy.autoResumeEnabledwas known and tested. Without it, a suspended MicroVM's endpoint answers502 MICROVM_CONNECT_FAILEDforever, so the SDK had to resume.3. What a live probe showed
A probe ran 16 MicroVMs in us-east-1. Every MicroVM was terminated afterwards.
autoResumeEnabled: trueresumes it502 MICROVM_CONNECT_FAILEDin about 200 ms and does not resume the MicroVM. GetMicrovm already says SUSPENDED.502 INTERNAL_ERROR,502 MICROVM_CONNECT_FAILED, or no answer. A request with no answer reaches the worker after the next resume; the worker dedupes by callback ID.autoResumeEnabled: false, or no idle policy502 MICROVM_CONNECT_FAILEDforever; stays SUSPENDEDmaxIdleDurationSeconds>= 60, max 2,147,483,647.resumehook fires on every auto-resume, before the request reaches the worker. The worker's 503 refusal never reached a client after an auto-resume.The current request retries (250 ms doubling to 4 s, 60 s window) reach 7.75 s after 5 retries, so they already cover the 5 s window. With a
retry_windowunder about 9 seconds, the step retry strategy covers the rest of that window, at the cost of one or two more step attempts. A job sent right after a suspend can take up to about 10 seconds.4. Options
autoResumeEnabled, configured by the developer. Chosen.lambda:ResumeMicrovm. A developer who suspends must set the policy.maxIdleDurationSeconds; a wrong value suspends long jobs. The developer knows their job length.idlePolicyfrom GetMicrovm and fails fast when auto-resume is off5. Chosen option
502 MICROVM_CONNECT_FAILEDor a request without an answer, the request step makes one GetMicrovm call, at most once per 2 s. The call runs first, before the retry window and the backoff, and only the invocation reserve limits it. With less than 1 s left before the reserve, no call starts, and the attempt ends with the retryableMicrovmEndpointUnavailableError.MicrovmNotRunningError.MicrovmPermissionDeniedError(fix(extras): fail microvm() fast on a dead MicroVM #910 behavior). Its message nameslambda:GetMicrovm.suspend_after(the function calls SuspendMicrovm) and the worker'sauto_suspend_on_idlestay. Both require the developer's idle policy withautoResumeEnabled: true.MicrovmDeliveryError.suspend_after,auto_suspend_on_idle,idle_policy):idle_policywith"autoResumeEnabled": True. The SDK does not resume a MicroVM; the service resumes it on the next request."maxIdleDurationSecondsabovemaximumDurationInSeconds(the sessiontimeoutplus 5 minutes). The idle timer counts only inbound requests, so a shorter value suspends a running job."suspendedDurationSecondsto the sessiontimeoutor more." What happens to a job when that time ends is not verified.autoResumeEnabled, a job after a suspend fails after its retries withMicrovmDeliveryError."retry_windowunder about 9 seconds, the step retry strategy covers the rest of that window, at the cost of one or two more step attempts. A job sent right after a suspend can take up to about 10 seconds."lambda:ResumeMicrovm. It needslambda:GetMicrovmfor HTTP delivery, andlambda:SuspendMicrovmforsuspend_after.6. What it removes
ensure_running, its loop_wait_until_running,EnsureRunningOptions(lifecycle.py).resume_if_suspended. It is renamedcheck_not_ended, and it takes the client, the MicroVM ID, and the remaining-time function.ResumeMicrovmcase ofMicrovmPermissionDeniedError.min(max_wait_seconds, retry_window_seconds)). The check no longer waits, so no wait remains to cap.resume_before_deliveryinLaunchResultandresume_from_config(operation.py,session.py).LaunchResult.from_dictignores aresumeBeforeDeliverykey, so an old checkpoint still replays._may_be_suspendedmark and_resume_modeofMicrovmSession.recheckpath ofsend_job(request.py), and theresumeargument ofdeliver.SendJobOptions.suspend_checkis renamedstate_check, and it takes no argument.SendJobOptions.window_started_at. With no check before delivery, it equaled the start ofsend_job.ResumeMicrovmfromMicrovmApiOperationand frommicrovm_not_found.lambda:ResumeMicrovmfrom the README permissions and the example docs.Stays: the request retries, the dead-MicroVM check and its order,
MicrovmPermissionDeniedError,suspend_after, workerauto_suspend_on_idle, the worker'ssuspend/resumehooks, its 503 refusal and 30 s grace (still needed for the worker's own SuspendMicrovm), and its callback-ID dedupe (now also needed for late delivery after a frozen request).7. Replay and compatibility
resumeBeforeDelivery: true. The new code ignores the field. Its next job no longer resumes the MicroVM. It works only if that MicroVM was launched withautoResumeEnabled: true. Asuspend_aftersession without an idle policy is affected too: its next job after the suspend no longer resumes the MicroVM and fails after its retries withMicrovmDeliveryError. The README says both. The package is unpublished.Source lines (
aws-durable-execution-sdk-python-extras/srconly)Coverage counts 967 statements and 188 branches, down from 1,094 and 228 on
main.Tests
main: 490 passed, 100%)hatch fmt --check.pyfileNew and changed tests:
conftest.pyfakeresume_microvmraises, and the autouse fixtureno_resume_microvmfails any test whose fake client recorded the calllifecycle_test.py::test_check_returns_the_endpoint_of_a_microvm_that_has_not_ended[RUNNING, PENDING, SUSPENDING, SUSPENDED]lifecycle_test.py::test_check_raises_the_state_error_for_a_microvm_that_ends[TERMINATING, TERMINATED],..._for_a_removed_microvmlifecycle_test.py::test_check_fails_at_once_when_get_microvm_is_deniedMicrovmPermissionDeniedError, message nameslambda:GetMicrovmoperation_test.py::test_checks_the_state_after_a_sign_that_uses_up_the_retry_window[connect-failed, no-answer](from #910)operation_test.py::test_ends_an_attempt_of_a_suspended_microvm_without_a_deadlineoperation_test.py::test_a_custom_strategy_sees_the_denial_error_typeMicrovmPermissionDeniedErrorfor a denied GetMicrovmrequest_test.py::test_runs_the_state_check_when_the_sign_used_up_the_windowrequest_test.py::test_starts_no_state_check_within_1_second_of_the_reserveoperation_test.py::test_keeps_sending_the_job_to_a_microvm_suspended_from_outside[SUSPENDING, SUSPENDED]microvmkeeps retryingsession_test.py::test_keeps_sending_to_a_microvm_that_the_worker_suspended_and_uses_its_endpoint[...],test_keeps_sending_the_job_after_a_suspend_after_job[...],test_a_job_after_a_replayed_suspend_after_job_keeps_sending,test_runs_both_items_when_both_find_the_microvm_suspendedsession_test.py::test_replays_a_launch_checkpoint_with_the_resume_mode_of_an_older_releaseresumeBeforeDelivery: truereplays, and the next job worksoperation_test.py::test_launch_result_ignores_the_resume_mode_of_an_older_releasefrom_dictignores the field,to_dictdoes not write itsession_test.py::test_writes_no_resume_mode_in_the_launch_checkpoint[...]worker_composed_test.py(3 tests)resumehook. Jobs succeed without ResumeMicrovm, with and without theresumehook, and when a resend meets a suspend.Removed tests. The behaviors they covered no longer exist:
ensure_runningtest, and the resume, resume-conflict and resume-wait tests of the state check (lifecycle_test.py).lifecycle_test.py::test_check_fails_at_once_when_resume_microvm_is_denied,lifecycle_test.py::test_check_names_no_request_id_for_an_error_of_another_call(the ResumeMicrovm case; the GetMicrovm case stays),operation_test.py::test_the_wait_for_a_resume_gets_a_new_retry_window_without_a_deadline(replaced, see above), and theresumecase ofoperation_test.py::test_a_custom_strategy_sees_one_error_type_for_a_denial.request_test.py::test_counts_the_window_from_the_given_start, withwindow_started_at.rechecktests ofsend_job, the "flag" tests ofsuspend_after, and the ResumeMicrovm scenarios ofrequest_ids_test.py.Edited expectations (rule 5.4):
502 MICROVM_CONNECT_FAILEDfirst, because the check runs only after that sign.request_ids_test.py: "GetMicrovm denies access" expectsMicrovmPermissionDeniedErrorwith its message, because the strict pre-check that raised the rawAccessDeniedExceptionis gone. The message-based retry strategy case for GetMicrovm is removed, because a throttled state check logs a warning and never reaches the strategy.request_test.py::test_checks_the_state_after_a_connect_failure_or_no_answer: the check takes no argument, so the test records when it runs (0 and 3.75 s, before the delay) instead of the time it received.SuspendMicrovmwarning no longer says "The next job checks its state."Mutation checks (rule 4)
Each row changes one branch in
src, runs the extras tests, and restores the file.MICROVM_CONNECT_FAILEDfrom_dictrejects aresumeBeforeDeliverykeyto_dictwritesresumeBeforeDeliveryagainAssumptions (rule 6)
MicrovmDeliveryErrorwithoutautoResumeEnabled: computed from the default retry strategy, not measured.Decisions not covered
maxIdleDurationSecondsis above the maximum duration.suspendedDurationSeconds: 0.auto_suspend_on_idleat all. The service's idle timer with a long job is unsafe, and the worker's own suspend needs the same policy. It stays for now.lambda:ResumeMicrovmgrant in the role that the cloud tests use is defined outside this repository. Remove it in a later change, after both SDKs ship.worker.py, the docstring ofSUSPEND_GRACE_SECONDSinself_suspend.py, two test comments, and the worker README now say that the durable function delivers the job again and the service resumes the MicroVM. The worker code is unchanged.SendJobOptions.suspend_checkis renamedstate_check, andresume_if_suspendedis renamedcheck_not_endedwith a new signature (client, MicroVM ID, remaining-time function).GetMicrovm for MicroVM <id> did not end before the invocation reserve, after <n> ms.The JS twin uses the same message.