Repository navigation
refactor(microvm-worker): log one suspend warning - #831
Conversation
A failed SuspendMicrovm call now logs one message per outcome. The fields state the listener state, read in the locked block that acts on the outcome. For an unknown outcome, that block runs after close(), so the fields match the refusal by construction. Fixes #820.
Address the D2 review of the failed-suspend warning. List all seven fields as item 9, and check them in the refusal-for-now test.
yaythomas
left a comment
There was a problem hiding this comment.
Both calls to _failed_call_log() now run while self._lock is held. _failed_call_log() calls describe(error), and describe() calls str(error). A client supplied through create_microvms_client controls that exception type. So its __str__() runs while the listener holds a non-reentrant lock. A slow implementation blocks every hook and job request. An implementation that calls back into the listener can deadlock.
I reproduced the deadlock with an exception whose __str__() calls listener.close(). The same probe completes on the base revision. safe_logger() cannot prevent this because describe() runs before the logger call.
Can this compute describe(error) before entering either locked block, then pass the resulting object to _failed_call_log()?
describe() calls str() on the SuspendMicrovm error, and a caller's client controls that error's __str__(). The listener now describes the error before it takes the lock. This addresses the review on #831.
## Summary After a failed SuspendMicrovm call, the worker logs one warning per outcome, with the state as fields. The 10 texts of `failedCallWarning` are removed. The behavior is unchanged. ## Why 1. `failedCallWarning` picked one of 10 texts from the outcome and four state flags. 2. The texts change no behavior. 3. #1049 and #1069 here, and Python #799 and #817, only fixed texts that stated something false. 4. Each text had to stay word for word identical in both SDKs. 5. So the message now names only the outcome, and fields carry the state. A field cannot state a false sentence. This is item D2 of a review of the MicroVM safeguards. The Python worker makes the same change in aws/aws-durable-execution-sdk-python#831. ## Changes - Messages: - rejected: "could not suspend the idle MicroVM, because the service rejected the call. A missing permission needs lambda:SuspendMicrovm on the MicroVM image in its execution role." - refused for now: "could not suspend the idle MicroVM, because the service refused the call for now." - unknown: "the suspend call for the idle MicroVM had an unknown outcome." - Fields, in this order: `microvmId`, `error`, `outcome`, `suspendHookArrived`, `terminating`, `closed`, `refusingJobs`. - The worker logs the warning after it has acted on the outcome. So `refusingJobs` is the state that follows from the outcome. It is `terminating || suspending`, the two states in which the job route answers 503. - The 30-second wait after a call refused for now is unchanged. - README and listener TSDoc item 9 describe the warning once, with all seven fields. The CHANGELOG has a `Changed` entry, because a log query that matched an old text must now match the new message or `outcome`. - One log order changes. After a rejection or a refusal for now, deferred `run` hook jobs start before the warning. So their "job started" line now comes before the warning. Python keeps the warning first. No test or documented behavior depends on this order. - For an unknown outcome, the grace timer now starts before the worker destroys its client. So a `destroy()` that throws no longer leaves the refusal in place until the `resume` hook. ## Testing - Every existing scenario is kept. Each asserts the message, the fields, and one warning per failed call. The 202/503 and timing assertions are unchanged. - Reading `refusingJobs` before the outcome is handled fails 28 tests. - Two tests were added for the default JSON logger and an unknown hook name, to keep the coverage. - Worker `npm test`: 294 tests pass. Coverage rose from 96.59 % to 96.78 % of statements. `npm run lint` and `npm run format:check` pass. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
|
Thanks, confirmed and fixed in 7704e9f. The except block now calls |
Fixes #820.
Summary
After a failed SuspendMicrovm call, the worker logs one warning per outcome, with the state as fields. The 10 texts of
_failed_call_warningare removed. The behavior is unchanged.Why
_failed_call_warningpicked one of 10 texts from the outcome and four state flags.This is item D2 of a review of the MicroVM safeguards. The JS worker makes the same change in aws/aws-durable-execution-sdk-js#1081.
Changes
microvmId,error,outcome,suspendHookArrived,terminating,closed,refusingJobs.refusingJobsisterminating or suspending, the two states in which the job route answers 503.describe()callsstr(), and a client fromcreate_microvms_clientcontrols the exception type.client.close(), because the block afterclose()decides the refusal.Why not the fix proposed in #820
#820 proposed to start the 30-second refusal before
close(). In the #820 sequence, that refuses a job that gets HTTP 202 today. This PR changes no behavior. It reads the fields where the decision is made.Testing
mainand passes here.__str__()callslistener.close(), or sends asuspendhook. Both fail on the first version of this PR (c2464b1), and pass now.hatch run dev-microvm-worker:cov: 457 tests pass.worker.pystays at 100 %. Typecheck andhatch fmt --checkpass. A stress run with-n 8passed 3 times.By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.