File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -2318,22 +2318,17 @@ async def observe_ownership() -> None:
23182318 return result
23192319 finally :
23202320 abandoned .set ()
2321- shutdown .cancel ()
2322- observation .cancel ()
2323- with contextlib .suppress (asyncio .CancelledError , Exception ):
2324- await shutdown
2325- with contextlib .suppress (asyncio .CancelledError , Exception ):
2326- await observation
2327- if not invocation .done ():
2328- invocation .cancel ()
23292321
2330- def discard_late_result (future : asyncio .Task [Any ]) -> None :
2331- if not future .cancelled ():
2332- future .exception ()
2322+ def discard_late_result (future : asyncio .Task [Any ]) -> None :
2323+ if not future .cancelled ():
2324+ future .exception ()
23332325
2334- # A running thread or cancellation-resistant callable can outlive
2335- # the await. Its heartbeat and eventual publication stay fenced.
2336- invocation .add_done_callback (discard_late_result )
2326+ # The abandoned claim must finish without waiting for callback or
2327+ # observer cancellation. All late progress/publication stays fenced.
2328+ for background in (invocation , observation , shutdown ):
2329+ if not background .done ():
2330+ background .cancel ()
2331+ background .add_done_callback (discard_late_result )
23372332
23382333 async def _run_activity_task (self , task : dict [str , Any ]) -> str :
23392334 self ._track_worker_session_from_task (task )
Original file line number Diff line number Diff line change @@ -121,6 +121,32 @@ def callback() -> object:
121121 release .set ()
122122
123123
124+ async def test_shutdown_expiry_of_tracked_remote_attempt_fences_a_cancellation_resistant_result (owner ) -> None :
125+ worker , client = owner
126+ worker ._shutdown_timeout = 0.01
127+ entered , late_fenced = asyncio .Event (), asyncio .Event ()
128+
129+ async def callback () -> object :
130+ entered .set ()
131+ try :
132+ await asyncio .Event ().wait ()
133+ except asyncio .CancelledError :
134+ with pytest .raises (_RemoteActivityExecutionAborted ):
135+ await activity .context ().heartbeat ()
136+ late_fenced .set ()
137+ return object ()
138+
139+ worker .activities ["remote" ] = callback
140+ execution = worker ._track (worker ._run_activity_task (task ()))
141+ await asyncio .wait_for (entered .wait (), timeout = 2 )
142+ await asyncio .wait_for (worker .stop (), timeout = 2 )
143+ await asyncio .wait_for (late_fenced .wait (), timeout = 2 )
144+ assert execution .cancelled () or execution .result () == "claim_aborted"
145+ client .heartbeat_activity_task .assert_not_awaited ()
146+ client .complete_activity_task .assert_not_awaited ()
147+ client .fail_activity_task .assert_not_awaited ()
148+
149+
124150@pytest .mark .parametrize ("synchronous" , [False , True ])
125151async def test_authored_heartbeat_stays_on_owner_loop_and_preserves_typed_result (owner , synchronous : bool ) -> None :
126152 worker , client = owner
You can’t perform that action at this time.
0 commit comments