Repository navigation
fix(local): show durable execution ARN as soon as the execution starts - #9205
roger-zhangg merged 6 commits into
Conversation
sam local invoke printed the durable execution ARN only after the execution reached a terminal state, and otherwise logged it at DEBUG. That ARN is the only identifier sam local execution get, history and stop accept, so an execution that was still running -- suspended on a callback, waiting out a context.wait, or interrupted with Ctrl-C -- could not be inspected without re-running the invoke under --debug and reading it out of the log. --durable-execution-name is not a way around it. The emulator builds the ARN from a pair of generated UUIDs and keeps the name only as metadata, so the execution commands answer "Execution <name> not found" when given a name. Nothing else about a running execution is missing: GetDurableExecution and GetDurableExecutionHistory both return data mid-flight, so the ARN was the only gap. The ARN is now printed as soon as start_durable_execution returns, before the wait begins, together with the get/history/stop commands that work from another terminal while the execution runs. That mirrors the service, which returns the ARN in the X-Amz-Durable-Execution-Arn response header of Invoke -- the same header this code already builds for start-lambda and start-api callers, which is why the message is gated to the CLI path by the existing _is_cli_context() check. The completion summary is unchanged, so a short execution now names its ARN twice: at the start, where it is needed in order to poll, and at the end alongside the result. The suggested commands are left unquoted, matching the existing completion output. Once the emulator emits service-shaped ARNs containing $LATEST, both call sites will need quoting, and that belongs with the ARN format change rather than here. assert_invoke_output in the integration base used to find the summary's ARN by taking the first "ARN:" match in stdout; the banner now precedes it. The value is the same either way, so nothing failed, but the helper had stopped proving that the summary carries the ARN. The lookup is now anchored to the summary section, and the banner is asserted separately, so both halves of the user-visible output are covered. This covers the first of the three options aws#9200 offers. Listing local executions and accepting --durable-execution-name in the execution commands are not addressed here. Testing: 86 unit tests pass across tests/unit/lib/utils/test_durable_formatters.py and tests/unit/local/docker/test_durable_lambda_container.py, five of them new -- the formatter, _show_execution_started in CLI context, in HTTP context and with no ARN, and an ordering test that asserts through a get_durable_execution side effect that the ARN was already printed by the time the execution was first polled. The existing sync wait_for_result test now expects both secho calls, in order. The durable integration tests need Docker and were not run; the reworked assertions in assert_invoke_output were checked against a reconstruction of a full invoke stdout, confirming the anchored lookup returns the summary ARN and that the banner assertions hold. black, ruff and mypy clean.
d194dd6 to
2babdb7
Compare
Address review feedback on the durable execution started banner. The command list is now rendered by the existing generate_next_command_recommendation helper instead of being formatted inline, so the banner matches the next-command sections the rest of the CLI prints. The custom lead-in line becomes a standalone sentence above the shared "Commands you can use next" heading. The banner is diagnostic output, so it goes to stderr, following the convention that stdout carries only the function result. The completion summary is unchanged and stays on stdout. assert_invoke_output now takes an optional stderr argument and looks for the banner there; the three call sites that capture the two streams separately pass it. The streaming callback test merges stderr into stdout, so it keeps using the lookup anchored to the summary section.
bchampp
left a comment
There was a problem hiding this comment.
This is a regression. We shouldn't be displaying the "next" commands that can run before the invoke happens.
The started banner listed the get/history/stop commands under a "Commands you can use next" heading. At the point the execution starts those commands are not next: the invoke still owns the terminal, and the commands are for use from a second one, so the heading described the wrong thing. Show just the ARN. Command suggestions stay in the completion output, where "next" is accurate, which also means the started banner no longer duplicates the command list and the shared generate_next_command_recommendation helper is left untouched. The _execution_commands helper added earlier in this branch existed only to share the tuples between the two sections and now has a single caller, so format_next_commands_after_invoke goes back to building them inline. The net change to durable_formatters.py is one added function.
The docstring still claimed the ARN is displayed together with the commands that can be used to inspect a running execution. Only the ARN is printed; the command suggestions stay in the completion output.
|
Updated for all the feedback, and the PR description now has a UX preview section with real transcripts ( @roger-zhangg the output now matches the shape you sketched: $ sam local invoke HelloWorld --no-event
Invoking hello_world.hello_world.handler (python3.13)
Using local image: public.ecr.aws/lambda/python:3.13-rapid-x86_64.
Durable execution started
=========================
ARN: 26af376b-5300-40ae-8e79-fe1032020f4f/77e70d5f-78b8-4c3f-a00f-6e2fcc8143f7
START RequestId: 0cb95694-e145-4a9a-aa40-2d4662ea68b1 Version: $LATEST
END RequestId: 0cb95694-e145-4a9a-aa40-2d4662ea68b1
REPORT RequestId: 0cb95694-e145-4a9a-aa40-2d4662ea68b1 Duration: 8101.15 ms Billed Duration: 8102 ms
Execution Summary:
=========================
ARN: 26af376b-5300-40ae-8e79-fe1032020f4f/77e70d5f-78b8-4c3f-a00f-6e2fcc8143f7
Status: SUCCEEDED β
...
Commands you can use next
=========================
[*] Get execution details: sam local execution get 26af376b-...
[*] View execution history: sam local execution history 26af376b-...One thing I left out deliberately: your sketch had The most useful part of the preview is the case the issue is actually about: invoke @bchampp @zhongkechen the new commits reset your approvals, sorry about the churn β would appreciate another look. |
Merge commits are not allowed on this repository
sam local invokeprinted the durable execution ARN only after the execution reached a terminal state, and otherwise logged it at DEBUG. That ARN is the only identifiersam local execution get,historyandstopaccept, so an execution that was still running -- suspended on a callback, waiting out acontext.wait, or interrupted with Ctrl-C -- could not be inspected without re-running the invoke under--debugand reading it out of the log.--durable-execution-nameis not a way around it. The emulator builds the ARN from a pair of generated UUIDs and keeps the name only as metadata, so the execution commands answer "Execution not found" when given a name. Nothing else about a running execution is missing:GetDurableExecutionandGetDurableExecutionHistoryboth return data mid-flight, so the ARN was the only gap.The ARN is now printed as soon as
start_durable_executionreturns, before the wait begins. That mirrors the service, which returns the ARN in theX-Amz-Durable-Execution-Arnresponse header ofInvoke-- the same header this code already builds forstart-lambdaandstart-apicallers, which is why the message is gated to the CLI path by the existing_is_cli_context()check.Only the ARN is announced. Command suggestions are left where they already were, in the completion output, because at the point the execution starts they are not the next thing to run -- the invoke still owns the terminal, and the commands are for use from a second one. The completion summary is otherwise unchanged, so a short execution names its ARN twice: at the start, where it is needed in order to poll, and at the end alongside the result.
The banner goes to stderr, following the convention that stdout carries only the function result.
UX preview
All output below is from real runs against
tests/integration/testdata/durable.Before
Nothing identifies the execution until it is over. The ARN first appears in the completion summary, by which point there is nothing left to inspect:
After
The ARN is announced before
START, i.e. before the handler has begun running. Everything else is unchanged:What the ARN unlocks
This is the case the issue is about. Terminal 1 invokes
WaitForCallback, which suspends and does not reach a terminal state on its own:Terminal 2, using the ARN from the banner while terminal 1 is still suspended:
Terminal 1 then finishes on its own:
On
developneither of the two commands above can be issued at all, because the ARN is not known until the execution has already ended.Stream split
The banner is diagnostic output, so it goes to stderr and stdout still carries only the summary and result:
Which issue(s) does this change fix?
Part of #9200
Why is this change necessary?
The execution ARN is the only handle the
sam local executioncommands accept, and it was not available until the execution had already reached a terminal state -- so a running, suspended or interrupted execution could not be inspected.How does it address the issue?
It prints the ARN as soon as the emulator returns it, before the invoke starts waiting.
What side effects does this change have?
sam local invokeon a durable function writes one additional three-line banner to stderr. stdout is unchanged.Mandatory Checklist
PRs will only be reviewed after checklist is complete
make prpassesmake update-reproducible-reqsif dependencies were changedTesting: unit tests pass across
tests/unit/lib/utils/test_durable_formatters.pyandtests/unit/local/docker/test_durable_lambda_container.py, five of them new -- the formatter,_show_execution_startedin CLI context, in HTTP context and with no ARN, and an ordering test that asserts through aget_durable_executionside effect that the ARN was already printed by the time the execution was first polled.assert_invoke_outputin the integration base used to find the summary's ARN by taking the firstARN:match in stdout; the lookup is now anchored to the summary section and the banner is asserted separately against stderr. The durable invoke and execution integration tests were run locally to produce the transcripts above.black,ruffandmypyclean.By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.