Repository navigation
fix(core): retry cancelled asynchronous actions after resume - #1032
linhongyu510 wants to merge 1 commit into
Conversation
enekoreto
left a comment
There was a problem hiding this comment.
The fix looks right to me. I checked out the branch (2a36017) on main (36e3ff3): the 4 new tests fail on main (resume goes to after) and pass here on Python 3.13, CI's test command passes, and pre-commit passes. I also tried it outside the tests with AsyncInMemoryPersister: a cancelled astep or arun is now saved as failed and resumes at work, and the local tracker now records the CancelledError on the step. A cancelled streaming action through astream_result was already saved as failed on main, and still is.
But the new tests fail on Python 3.10, which CI runs: all 4 stop at assert tracker.post_called[0][1]["exception"] is cancelled.value. On 3.10, awaiting a cancelled task raises a new CancelledError instead of the one raised inside the action, so the two aren't the same object (on 3.13 they are). isinstance(tracker.post_called[0][1]["exception"], asyncio.CancelledError) passes on both. The rest of the suite passes on 3.10.
Small thing, not blocking: Ctrl+C has the same problem, since KeyboardInterrupt isn't an Exception either. A KeyboardInterrupt inside an action, in step or astep, is still saved as completed and skipped on resume, on main and here. Catching it next to CancelledError in _astep, plus the same clause in _step, fixed it in my test, and the suite still passes. That could also be a follow-up.
I ran this on Python 3.13 on macOS, and the tests also on 3.10.
Cancelling an ordinary asynchronous action currently records its step as
completed, even though its state update never ran. Rebuilding the application withresume_at_next_action=Truethen skips that action and advances to its successor. For example, cancellingworkwhile it waits for a response resumes atafterwith the old state.Changes
asyncio.CancelledErrorfor_astep'spost_run_stephooks before re-raising it. Persisters consequently record the step asfailedand resume at the interrupted action.astepandarun, with both function-based single-step actions and class-based multi-step actions. The tests cancel at an event-controlled await, rebuild from persisted state, retry, and verify the final result.How I tested this
36e3ff3048c8cabd7aac6d5c373307934bf3c2df, all four new cases fail because the next action after recovery isafterrather thanwork.pytest tests/core/test_application.py tests/core/test_persistence.py tests/core/test_parallelism.py -q: 256 passed on Python 3.12.13.git diff --checkpass.pre-commit run --files burr/core/application.py tests/core/test_application.py: passed, including the frontend lint-staged and ASF header hooks.Notes
This concerns non-streaming step persistence. The streaming cleanup change in #1027 is already in the base. The top-level execute-hook reporting in #897 does not update
_astep's step hooks or persistence status.The existing cancellation still propagates to the caller. This does not shield persistence from repeated cancellation or change the handling of cancellation during a lifecycle hook.
This change was developed with an AI assistant. The reproduction and regression checks use no model or network service.
Checklist