Repository navigation
fix(extras): fail microvm() fast on a dead MicroVM - #910
Conversation
A microvm() job over HTTP whose MicroVM had ended kept sending the job to the same dead endpoint. The endpoint of a terminated MicroVM answers HTTP 502 with x-aws-proxy-error: MICROVM_CONNECT_FAILED, measured in us-east-1. The request step retried that 502 as a MicroVM that is still starting. So the job failed with MicrovmDeliveryError only after both retry tiers, several minutes later. A session job already checked the state after that answer. deliver() turned the check on only through its suspend_check flag, and only a session set it. This change removes the flag. So every job over HTTP without the resume mode runs the same check. A terminating, terminated, or removed MicroVM now fails the job at once with MicrovmNotRunningError. microvm() now uses lambda:GetMicrovm, and lambda:ResumeMicrovm for a MicroVM that something outside the operation suspended. Without GetMicrovm, the check logs one warning and the job keeps its retries. Port of aws/aws-durable-execution-sdk-js#1178.
The service's stateReason after TerminateMicrovm is "Success.". The ended-state message adds its own period. So the message ended with "Success..". The message now removes one trailing period of stateReason, as the JS SDK does. This changes the session message too, because sessions share the same function. The docs said the check calls GetMicrovm once and logs one warning per job. The check runs after each sign, at most once per 2 seconds. Each step retry runs in a new invocation with a new warning flag. So the docs now say "at most once per 2 seconds" and "one warning per step attempt, so up to 5 with the default retry strategy". Two new tests cover the resume path of microvm(): a MicroVM suspended from outside is resumed and the job succeeds, and a denied ResumeMicrovm fails the job at once with MicrovmDeliveryError.
The state check after a 502 with MICROVM_CONNECT_FAILED, or after a request without an answer, caught AccessDeniedException from GetMicrovm, logged a warning, and kept sending the job. A dead MicroVM then stayed on the six-minute failure path. A denied GetMicrovm now raises MicrovmGetDeniedError. The default retry strategy does not retry it, so the job fails at once with MicrovmDeliveryError. The message names lambda:GetMicrovm and ends with the GetMicrovm request ID. A throttled, 5xx, timed-out, or failed-to-connect GetMicrovm still logs one warning per step attempt and keeps sending the job. Sessions share the check, so the same applies to session jobs. The denied note on the final error is removed, with the unavailable_note option of send_job that carried it.
Address the second review of the denied GetMicrovm change. - Use the same denied GetMicrovm message as the JavaScript SDK. - README: say that a role without lambda:GetMicrovm now fails a job whose request gets no answer, even when the MicroVM runs. Use "that answer, or no answer" in Permissions and Session permissions. - Name the denied GetMicrovm and ResumeMicrovm in the MicrovmDeliveryError docs of the Errors table, vm.invoke, and microvm(). - Add a test row: UnrecognizedClientException from GetMicrovm stays on warn-and-retry.
This comment has been minimized.
This comment has been minimized.
A delayed 502 from a dead endpoint can use up a short retry window. The window exit ran before the state check, so the attempt ended with MicrovmEndpointUnavailableError after zero GetMicrovm calls. - After a sign, the state check runs first, before the window exit and the backoff, at most once per 2 seconds. - Only the invocation reserve limits the check. With less than 1 second before the reserve, no call starts, and the tier raises MicrovmEndpointUnavailableError as before. - Remove the window budget of the check and its window exit branch. - Tests: a delayed 502 or no answer with a 1-second window ends in MicrovmNotRunningError after one GetMicrovm call; a reached reserve makes no call. Three tests move their expected check times to the failure times, because the check now runs before the delay.
A denied GetMicrovm and a denied ResumeMicrovm have the same retry behavior and the same public error. So one internal type covers both. - Replace MicrovmGetDeniedError and MicrovmResumeDeniedError with MicrovmPermissionDeniedError. The default retry strategy does not retry it, and the delivery raises MicrovmDeliveryError. - One helper builds the message. It names the denied action and the permission, and ends with the request ID of the call. - Tests: both denials raise the new type with their own permission, and a custom retry strategy sees the one type name.
This comment has been minimized.
This comment has been minimized.
Without a Lambda context, the state check passed an infinite wait to resume_if_suspended. A MicroVM that stayed SUSPENDED then kept the wait for RUNNING polling forever. The resume and the wait for RUNNING now get a new retry_window, and the invocation reserve still ends them earlier. The first GetMicrovm call stays limited only by the reserve. This matches the JavaScript operation, which passes retryWindowMs to resumeIfSuspended.
| request ID, as the service's output does. The service's stateReason | ||
| after TerminateMicrovm is ``Success.``. The message removes its | ||
| trailing period, so the message has no double period.""" | ||
| client = fake_client(endpoint=endpoint, states=[state]) |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
This comment has been minimized.
This comment has been minimized.
| request ID, as the service's output does. The service's stateReason | ||
| after TerminateMicrovm is ``Success.``. The message removes its | ||
| trailing period, so the message has no double period.""" | ||
| client = fake_client(endpoint=endpoint, states=[state]) |
There was a problem hiding this comment.
Codex AI review · Finding arf_v1_6ft4kjt3rh3nf5oyujf4tnmon7
[P2] Follow the package's required testing approach
The extras README's Custom clients and tests, rule 3 requires tests to wrap a real lambda-microvms client in botocore's Stubber. This factory and the added denial/resume tests instead synthesize responses and errors through scripted clients, bypassing validation against the API model. CONTRIBUTING rule 9 requires enforcing package rules even when the implementation is correct. Please return to the design phase under rule 8, step 6, and agree a testing approach that provides the required API-model validation.
To suppress repeats, reply /ai verdict deferred, /ai verdict false-positive, or /ai verdict out-of-scope on the first nonblank line. Add rationale on later lines. A new /ai verdict accepted allows future repeats again.
Codex AI reviewOne unresolved approach finding at Reviewed commit |
Redesign of the state check (rule 8)
Two review findings hit the same check, so this section replaces the earlier design of the state check.
Problem
After a sign that the MicroVM may not run (an HTTP 502 with
x-aws-proxy-error: MICROVM_CONNECT_FAILED, or a request without an answer), the job must learn whether the MicroVM has ended. Two findings showed cases where it does not:AccessDeniedExceptionas a transient failure and the job kept sending. A dead MicroVM then made about 55 denied calls and stayed on the roughly six-minute failure path. (Fixed in the previous round.)retryWindow: { seconds: 1 }(retry_windowin Python), one delayed 502 from a dead endpoint (measured 3 to 8 s) uses up the window. The window exit runs before the check, so the attempt ends withMicrovmEndpointUnavailableErrorafter zero GetMicrovm calls, and the default strategy repeats that.Cause (shared)
The state check is an optional hook inside the delivery retry loop, not a step with its own rule:
So the check's outcome depends on the retry window, and its failures were folded into the loop's own retry rule.
Options
Chosen order
On every sign:
MicrovmNotRunningError.MicrovmEndpointUnavailableError).The order does not depend on the retry window, so the terminal state is always checked after a sign while invocation time remains.
Error model
Both internal denial errors (denied GetMicrovm, denied ResumeMicrovm) have the same retry behavior (not retried by the default strategy) and the same public error (
MicrovmDeliveryError). So they become one internal type,MicrovmPermissionDeniedError. Its message names the denied action and the permission, for example... GetMicrovm was denied. The function's execution role needs lambda:GetMicrovm. GetMicrovm request ID: <id>.MicrovmGetDeniedErrorandMicrovmResumeDeniedErrorare removed. A custom retry strategy that matches by name uses the one name.Tests
MicrovmNotRunningErrorafter one GetMicrovm call (fails on the current head).MicrovmPermissionDeniedErrorwith their own permission in the message.What it removes
The window budget for the check, the check's branch inside the window logic, and one internal error class.
Rule 8 commits
The fifth and sixth commits implement the redesign above. They apply as a fast-forward onto the fourth commit.
Fifth commit:
fix(extras): check MicroVM state before the window.send_job, the state check (suspend_check) now runs right after a sign, before the window exit and before the backoff. It still runs at most once per 2 seconds._call_budgethelper with the reserve as the deadline. With less than 1 second before the reserve, no call starts, and the tier raisesMicrovmEndpointUnavailableError, as at the reserve today. The check receives the time until the reserve, or infinity when the compute reports no deadline.left = deadline() - now) and its window exit branch (if left <= 0: raise unavailable()).recheckpath (resume mode) does not change.SendJobOptions.suspend_check,send_job,deliver, and README "Suspending after a job" item 2 describe the order.Sixth commit:
refactor(extras): use one MicroVM denial error.MicrovmGetDeniedErrorandMicrovmResumeDeniedErrorare replaced by one internalMicrovmPermissionDeniedError. The default retry strategy does not retry it, and the delivery raisesMicrovmDeliveryError, as before._denied, builds the message for both calls. The message text does not change:MicroVM <id> gave a sign that it does not run. GetMicrovm was denied, so the operation cannot check its state. The function's execution role needs lambda:GetMicrovm. GetMicrovm request ID: <id>.andMicroVM <id> is SUSPENDED. ResumeMicrovm was denied, so the operation cannot resume it. The function's execution role needs lambda:ResumeMicrovm. ResumeMicrovm request ID: <id>.default_microvm_retry_strategylists neither old name, so it does not change. The README names neither class. The package has no CHANGELOG.Tests of the rule 8 commits.
operation_test.py::test_checks_the_state_after_a_sign_that_uses_up_the_retry_window[connect-failed]MICROVM_CONNECT_FAILEDthat takes 1.5 s, with a 1-secondretry_window:MicrovmNotRunningErrorafter 1 request and 1GetMicrovmcall.operation_test.py::test_checks_the_state_after_a_sign_that_uses_up_the_retry_window[no-answer]request_test.py::test_starts_no_suspend_check_within_1_second_of_the_reserveMicrovmEndpointUnavailableErrorafter 1 attempt.request_test.py::test_runs_the_suspend_check_when_the_sign_used_up_the_windowoperation_test.py::test_a_custom_strategy_sees_one_error_type_for_a_denial[get, resume]MicrovmPermissionDeniedErrorfor both denials, with its own permission in the message.The tests use a 1.5-second delay instead of the measured 3 to 8 seconds. Any delay longer than the 1-second window shows the bug, and the shorter delay keeps the suite fast.
Edited tests (rule 5.4). The check now runs before the delay, so it runs at the failure time instead of after the delay:
request_test.py::test_checks_for_a_suspend_after_a_connect_failure_or_no_answer: the check budgets are the time until the reserve ([90, 86.25]) instead of the rest of the window ([59.75, 52.25]). The test now sets a reserve.request_test.py::test_checks_for_a_suspend_at_most_once_per_2_seconds: the check times are[0, 3.75]instead of[0.25, 3.75, 7.75].operation_test.py::test_keeps_sending_the_job_with_one_warning_when_get_microvm_failsandsession_test.py::test_keeps_sending_after_a_failed_state_check_with_one_warning: 5 failed requests instead of 4, so that two checks still run (at 0 and 3.75 s). The job succeeds on request 6.request_test.py::test_ends_the_tier_when_the_suspend_check_has_no_time_leftis removed: it asserted that the window skips the check, which is finding 2.test_starts_no_suspend_check_within_1_second_of_the_reservereplaces it.lifecycle_test.py,operation_test.py, andsession_test.py. No expected message changes.The window test fails on the fourth commit (
bee5570a). Both cases end withMicrovmDeliveryError, after 1 request and noGetMicrovmcall:Mutation check of the fifth commit, against the 22 state check tests of
request_test.pyandoperation_test.py:operation_test.py,test_runs_the_suspend_check_when_the_sign_used_up_the_window)deadline()instead of the reserve)test_checks_for_a_suspend_after_a_connect_failure_or_no_answer)test_starts_no_suspend_check_within_1_second_of_the_reserve,test_checks_for_a_suspend_after_a_connect_failure_or_no_answer)Mutation check of the sixth commit, against the 9 denial tests of
lifecycle_test.py,operation_test.py, andsession_test.py:MicrovmPermissionDeniedErrorretryable (a subclass ofMicrovmEndpointUnavailableError)microvm()2, session 2)lambda:GetMicrovmfor both calls)test_a_custom_strategy_sees_one_error_type_for_a_denial[resume])GetMicrovmtreated as transient (warn and keep sending)microvm()2, session 1)Extras package after the sixth commit: 489 passed, coverage 100% (1093 statements, 228 branches). mypy: no issues in 24 files.
hatch fmt --check: no new findings (the one remaining finding, intests/microvm/aws_errors_test.py, is on the fourth commit too).lintcommit.py --range origin/main..HEAD: all commits pass.Decisions not covered by the design note.
d73fe6c9.) The resume and the wait forRUNNINGafter the check get a newretry_window, and end earlier at the reserve when one exists. So without a Lambda context the wait ends after about one window with the retryableMicrovmEndpointUnavailableError, as in the JavaScript operation. The first GetMicrovm call stays limited only by the reserve._call_budget, the guard of every other call in the tier. So the check starts only with at least 1 second before the reserve, not at any time before it. The error is the tier's ownMicrovmEndpointUnavailableErrormessage, as at the reserve today.recheckpath keeps its order. It runs before every job, and the findings did not involve it.Issue #, if available
None. Port of aws-durable-execution-sdk-js#1178.
Description of changes
microvm()over HTTP now fails at once withMicrovmNotRunningErrorwhen its MicroVM has ended before the job arrives. Before, the request step kept sending the job to the dead endpoint until both retry tiers ended, and the job failed with a plainMicrovmDeliveryErrorabout 6 minutes later.The change removes the
suspend_checkflag of the internaldeliver(). So every job over HTTP without the resume mode runs the state check that a session job already runs. Session behavior does not change: a session set the flag for every job.The second commit ports the follow-up of the JS PR. The ended-state message now removes one trailing period of
stateReason, so it readsTERMINATED: Success.instead ofTERMINATED: Success... Sessions share_raise_if_ended, so the session message changes the same way. The docs now say that the check callsGetMicrovmafter each sign, at most once per 2 seconds, and logs one warning per step attempt, so up to 5 with the default retry strategy. Two new tests cover the resume path ofmicrovm().New permissions for
microvm()with HTTP delivery:lambda:GetMicrovmandlambda:ResumeMicrovm.lambda:GetMicrovmis required.The third commit answers the review: a denied
GetMicrovmin the state check now fails the request step at once. It raises the new internalMicrovmGetDeniedError, in the style ofMicrovmResumeDeniedError. The default retry strategy does not retry it, so the job fails withMicrovmDeliveryError, and the message readsMicroVM <id> gave a sign that it does not run. GetMicrovm was denied, so the operation cannot check its state. The function's execution role needs lambda:GetMicrovm. GetMicrovm request ID: <id>.A throttled, 5xx, timed-out, or failed-to-connectGetMicrovmstill logs one warning per step attempt and keeps sending the job. Sessions share this code, so a session job withoutlambda:GetMicrovmalso fails at once when its request gets the dead or suspended sign. The commit removes the denied note on the finalMicrovmDeliveryErrormessage, theunavailable_noteoption ofsend_jobthat carried it (it has no other caller), and thedeniedattribute ofMicrovmStateCheckError.Effect for a role without
lambda:GetMicrovm: a job whose request gets a 502 withMICROVM_CONNECT_FAILED, or no answer, fails at once withMicrovmDeliveryError, and the message names the permission. A job whose MicroVM answers makes noGetMicrovmcall and is not affected. A role withoutlambda:GetMicrovmnow fails a job whose request gets no answer, even when the MicroVM runs, for example a worker that takes more than 10 seconds to listen. Before, such a job logged a warning and succeeded.Docs: the
MicrovmNotRunningError,MicrovmStateCheckError,resume_if_suspended,SendJobOptions.suspend_check,send_job,SendJobOptions.window_started_at,deliver,_STATE_CHECK_WARNINGandmicrovm()docstrings, and the README sections Errors, Permissions, Suspending an idle MicroVM, Suspending after a job and Session permissions. Theerrors.pynote "Whether that 502 carriesMICROVM_CONNECT_FAILEDis not verified" now states the measured answer. The package has no CHANGELOG.The fourth commit answers the second review. The denied
GetMicrovmmessage is now the same text as in the JS SDK. The README states the effect for a healthy job withoutlambda:GetMicrovm(above), and says "that answer, or no answer" in Permissions and Session permissions. TheMicrovmDeliveryErrordocs of the README Errors table,MicrovmSession.invokeandmicrovm()name the deniedGetMicrovmandResumeMicrovm. One new test row pins the narrow match: a GetMicrovmUnrecognizedClientExceptionstays on warn-and-retry.Design
See Rules for every change.
Design note (rule 3).
Problem. A
microvm()job over HTTP whose MicroVM has ended keeps sending the job to the same dead endpoint until both retry tiers end. The job then fails with a plainMicrovmDeliveryError, minutes later, and each attempt bills Lambda compute.Cause.
clientToken = sha256(callback_id). So every retry reaches the same MicroVM.HTTP 502withx-aws-proxy-error: MICROVM_CONNECT_FAILEDand an empty body.CreateMicrovmAuthTokenstill issues a token for it. Both were measured live in us-east-1 (see the JS PR and the table below).GetMicrovmafter each such answer, at most once per 2 seconds (resume_if_suspended).deliver()turned the check on only through itssuspend_checkflag, and only a session set it. Somicrovm()made no state check.Options.
suspend_checkflag. The trigger stays the same: a 502 withMICROVM_CONNECT_FAILED, or no answer.microvm()that only raises for an ended MicroVM, and never resumes. It adds a code path next to an existing one that does the same job.GetMicrovmcall would add nothing.Chosen. Option 1, the same as the JS SDK. It fixes the bug by removing a condition. The measured dead answer is exactly the answer that the existing check reacts to.
Removes (first commit). The
suspend_checkparameter ofdeliver()and its condition (elif suspend_check:becomeselse:), and thesuspend_check=Trueargument insession.py.deliver()ran the state check only for sessions, somicrovm()treated the dead endpoint's 502 as a booting MicroVM.elif suspend_check:toelse:); the rest is docstrings. Public API: no change (deliver()is internal).Before and after, live in us-east-1
Scenario:
microvm()withrequest=MicrovmRequestConfig(path="/job"). The boto3 client terminates the MicroVM in anafter-callhandler ofRunMicrovm, and waits forTERMINATED(about 2 s). The job then goes to the dead endpoint. A urllib handler on the opener logs each request and its answer. The function's role haslambda:GetMicrovm.x-aws-proxy-error: MICROVM_CONNECT_FAILED, every requestMicrovmDeliveryErrorMicrovmNotRunningErrorMicroVM "job": the job could not be delivered: The MicroVM did not accept the job at /job after 11 attempts in 61130 ms. Last failure: HTTP 502.MicroVM "job": the job could not be delivered: MicroVM <id> is TERMINATED: Success. GetMicrovm request ID: <id>.(the live run at the first commit readSuccess..; the second commit removes one trailing period ofstateReason)TERMINATED)This change does not cover
runhook delivery. A job in therunhook has no request step. So a MicroVM that ends before it runs the job still fails it withMicrovmTimeoutError, at itsheartbeat_timeoutortimeout. The repo has no Python cloud harness for this scenario, so the live run used a throwaway script outside the repo.Tests
New tests in
operation_test.py, 14 cases formicrovm()over HTTP:MICROVM_CONNECT_FAILEDor no answer,GetMicrovmreportsTERMINATEDorTERMINATING:MicrovmNotRunningErrorat once, with thestateReason(Success., one period) and the request ID, 1 request, 1GetMicrovmcall, and the MicroVM is terminated (4 cases).GetMicrovmanswersResourceNotFoundException:MicrovmNotRunningErrorwith the request ID (1).GetMicrovmreportsRUNNINGorPENDING: the job is sent again and succeeds, with no resume (2).GetMicrovmis denied:MicrovmDeliveryErrorat once, naminglambda:GetMicrovmand the request ID, after 1 request and 1GetMicrovmcall; the step error type isMicrovmGetDeniedError(1). It replaces the old denied test.GetMicrovmis throttled, answers 5xx, or fails to connect: the job keeps its retries and succeeds, with one warning for two failed checks (3).GetMicrovmcall (2).MicrovmDeliveryErrorat once, with noGetMicrovmcall (2).GetMicrovmreportsSUSPENDED: the MicroVM is resumed (get:SUSPENDED, resume, get:RUNNING, terminate), and the job succeeds after 2 requests (1).GetMicrovmreportsSUSPENDEDandResumeMicrovmis denied:MicrovmDeliveryErrorat once, naminglambda:ResumeMicrovm, after 1 request (1).On main, 8 of the first 12 fail (all
GetMicrovmcases). The 4 others pass (plain 5xx and 4xx). With the old behavior restored by mutation (no state check formicrovm()), 10 of the 14 fail: the 8, plus the 2 resume tests. With the fix, all pass.Third commit, in other test modules:
session_test.py:test_fails_at_once_when_the_state_check_is_denied(new; one request, no retry, nameslambda:GetMicrovm, step error typeMicrovmGetDeniedError) andtest_keeps_sending_after_a_failed_state_check_with_one_warning(new; throttled and 5xx, 2 cases) replacetest_keeps_sending_after_a_denied_state_check_and_names_it_at_the_end.lifecycle_test.py:test_check_fails_at_once_when_get_microvm_is_deniedandtest_check_names_no_get_request_id_for_an_error_of_another_call(new).test_check_reports_a_failed_get_microvm_without_failing_the_jobnow runs forThrottlingExceptionand a 500 instead ofAccessDeniedExceptionandThrottlingException, and no longer assertsdenied. The fourth commit adds anUnrecognizedClientException(403) row to it.test_check_reports_a_get_microvm_connection_failureno longer assertsdenied.request_test.py:test_adds_the_note_to_the_final_errorandtest_adds_no_note_when_the_note_is_noneare removed withunavailable_note.Extras package after the fourth commit: 484 passed, coverage 100% (statements and branches). mypy: no issues.
hatch fmt --check: no new findings.Mutation check (rule 4). The first 8 rows ran against the first 12 new tests, the last 3 against all 14:
microvm()(the old behavior)TERMINATING/TERMINATEDdoes not raiseResourceNotFoundExceptionfromGetMicrovmtreated as a failed checkGetMicrovmstate forced toTERMINATED(soRUNNING/PENDINGare treated as ended)TERMINATINGmessages)GetMicrovmfails the jobmicrovm()(the old behavior), all 14 testsstateReasonnot removedresume_if_suspendeddoes not resume a suspended MicroVMMutation check of the third commit, against the 17 denied and failed-check tests of
lifecycle_test.py,operation_test.py,session_test.py, andrequest_test.py:GetMicrovmtreated as a transient failure (warn and retry)microvm()1, session 1)MicrovmGetDeniedErrorretried by the default strategymicrovm()1, session 1)GetMicrovmClientErrortreated as deniedmicrovm(), and session)Mutation check of the fourth commit: a GetMicrovm
UnrecognizedClientExceptiontreated as denied fails 1 test (the newUnrecognizedClientExceptionrow oftest_check_reports_a_failed_get_microvm_without_failing_the_job), of 202 tests inlifecycle_test.py,operation_test.py, andsession_test.py.Edited tests (rule 5.4): the third commit removes or changes 5 tests from main, because the denied behavior they asserted is removed (a denied
GetMicrovmno longer warns, keeps sending, and adds a note to the final error):request_test.py:test_adds_the_note_to_the_final_errorandtest_adds_no_note_when_the_note_is_noneare removed withunavailable_note.session_test.py:test_keeps_sending_after_a_denied_state_check_and_names_it_at_the_endis replaced bytest_fails_at_once_when_the_state_check_is_deniedandtest_keeps_sending_after_a_failed_state_check_with_one_warning.lifecycle_test.py:test_check_reports_a_failed_get_microvm_without_failing_the_jobchanges its parameters (AccessDeniedExceptionandThrottlingExceptionbecomeThrottlingExceptionand a 500) and drops thedeniedassert.test_check_reports_a_get_microvm_connection_failuredrops thedeniedassert.The fourth commit adds one row to
test_check_reports_a_failed_get_microvm_without_failing_the_joband changes the quoted denied message in 3 tests that the third commit added. No existing test asserted thatmicrovm()makes noGetMicrovmcall. The second commit changes one expectation of a test that the first commit added:test_fails_at_once_when_the_microvm_has_endedexpectedSuccess..and now expectsSuccess., because of the period fix. Without the fix, its 4 cases fail.Assumptions
x-aws-proxy-error: MICROVM_CONNECT_FAILED: measured live in us-east-1 (table above, and the probe in the JS PR).CreateMicrovmAuthTokenissues a token for a terminated MicroVM whose record still exists: measured live in us-east-1 (JS PR probe; the Python run above got past the token call too).stateReasonafterTerminateMicrovmisSuccess.: measured live (message above).TerminateMicrovmwas measured.CreateMicrovmAuthTokenanswer for a removed MicroVM record (not only terminated): unverified, as before.Decisions not covered by the issue or the design note
MICROVM_CONNECT_FAILEDor no answer, not any 5xx. A plain 5xx comes from a running worker. A test pins it.microvm()MicroVM that something outside the operation suspended is now resumed, as in a session. Before, the job retried until both tiers ended. Withoutlambda:ResumeMicrovm, it now fails at once withMicrovmDeliveryError.microvm()sets no idle policy, so its MicroVM suspends only through an outside call.microvm()and sessions now needlambda:GetMicrovmfor HTTP delivery, andmicrovm()should havelambda:ResumeMicrovm. WithoutGetMicrovm, a job whose request gets the dead or suspended sign fails at once with an error that names the permission. README Permissions says so.MicrovmGetDeniedErrornext toMicrovmResumeDeniedError. The rule 8 error model replaces both withMicrovmPermissionDeniedError, because both have the same retry behavior and the same public error.GetMicrovm failed, so the operation could not check whether the MicroVM is suspended. ...formicrovm()too, so that log filters keep matching. Same as the JS SDK._raise_if_endedchanges the session message too, because sessions share the function. The JS SDK made the same change.operation_test.py, because the changed branch is inoperation.py(CONTRIBUTING: one test module per source module).Checklist
microvm(). The third commit adds theAccessDeniedExceptionbranch inresume_if_suspended; the mutation table of the third commit covers it.By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.