Skip to content

Commit e6ed713

Browse files
committed
fix flakes in tests
1 parent fb0a8e2 commit e6ed713

2 files changed

Lines changed: 27 additions & 4 deletions

File tree

packages/llama-index-workflows/src/workflows/server/server.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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()

packages/llama-index-workflows/tests/plugins/test_runtime_matrix.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,10 +386,13 @@ async def original_step(
386386
self, ctx: Context, ev: StartEvent
387387
) -> Union[OneTestEvent, LastEvent]:
388388
await ctx.store.set("num_to_collect", 3)
389+
# Send test4 first to ensure it's pulled from receive_queue
390+
# before test_step workers complete. Events are pulled one per
391+
# iteration, so ordering in receive_queue determines delivery order.
392+
ctx.send_event(AnotherTestEvent(another_test_param="test4"))
389393
ctx.send_event(OneTestEvent(test_param="test1"))
390394
ctx.send_event(OneTestEvent(test_param="test2"))
391395
ctx.send_event(OneTestEvent(test_param="test3"))
392-
ctx.send_event(AnotherTestEvent(another_test_param="test4"))
393396
return LastEvent()
394397

395398
@step(num_workers=3)

0 commit comments

Comments
 (0)