@@ -1826,6 +1826,7 @@ async def _iter_events(self, timeout: float = 1) -> AsyncGenerator[Event, None]:
18261826 Converts the queue to an async generator while the workflow is still running, and there are still events.
18271827 For better or worse, multiple consumers will compete for events
18281828 """
1829+ queue_get_task : asyncio .Task [Event ] | None = None
18291830
18301831 try :
18311832 while not self .queue .empty () or (
@@ -1836,9 +1837,7 @@ async def _iter_events(self, timeout: float = 1) -> AsyncGenerator[Event, None]:
18361837 available_events .append (self .queue .get_nowait ())
18371838 for event in available_events :
18381839 yield event
1839- queue_get_task : asyncio .Task [Event ] = asyncio .create_task (
1840- self .queue .get ()
1841- )
1840+ queue_get_task = asyncio .create_task (self .queue .get ())
18421841 task_waitable = self .task
18431842 done , pending = await asyncio .wait (
18441843 {queue_get_task , task_waitable }
@@ -1848,10 +1847,31 @@ async def _iter_events(self, timeout: float = 1) -> AsyncGenerator[Event, None]:
18481847 )
18491848 if queue_get_task in done :
18501849 yield await queue_get_task
1850+ queue_get_task = None
18511851 else : # otherwise task completed, so nothing else will be published to the queue
18521852 queue_get_task .cancel ()
1853+ queue_get_task = None
18531854 break
18541855 finally :
1856+ # Cancel any pending queue.get() task to prevent orphaned tasks from
1857+ # consuming events after the consumer disconnects. If the task already
1858+ # completed, put the event back in the queue so the next consumer sees it.
1859+ if queue_get_task is not None :
1860+ if queue_get_task .done ():
1861+ try :
1862+ event = queue_get_task .result ()
1863+ # Put the event back at the front of the queue
1864+ # We use a workaround since asyncio.Queue has no put_front
1865+ self .queue ._queue .appendleft (event ) # type: ignore[attr-defined]
1866+ except (asyncio .CancelledError , Exception ):
1867+ pass
1868+ else :
1869+ queue_get_task .cancel ()
1870+ try :
1871+ await queue_get_task
1872+ except asyncio .CancelledError :
1873+ pass
1874+
18551875 if self ._on_finish is not None and self .run_handler .done ():
18561876 # clean up the resources if the stream has been consumed
18571877 await self ._on_finish ()
0 commit comments