fix: cancel sibling tasks when concurrent retrieval fails#11967
Conversation
|
@hxaxd is attempting to deploy a commit to the deepset Team on Vercel. A member of the Team first needs to authorize it. |
There was a problem hiding this comment.
Pull request overview
- Aligns async fan-out retrieval components with
Pipelinefailure semantics by cancelling + draining sibling tasks when one concurrent retrieval fails (fixes #11965), preventing orphaned in-flight tasks.
Changes:
- Replace direct
asyncio.gather([...])usage with explicitasyncio.create_task(...)plus cancel-and-drain behavior in the three affectedrun_asyncimplementations. - Add regression tests for
MultiRetriever,MultiQueryTextRetriever, andMultiQueryEmbeddingRetrieverto verify sibling cancellation on failure. - Add a release note describing the behavior change.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
haystack/components/retrievers/multi_retriever.py |
Track spawned retrieval tasks and cancel/drain siblings on failure before re-raising. |
haystack/components/retrievers/multi_query_text_retriever.py |
Apply the same cancel/drain pattern to per-query concurrent tasks. |
haystack/components/retrievers/multi_query_embedding_retriever.py |
Apply the same cancel/drain pattern to per-query concurrent tasks. |
test/components/retrievers/test_multi_retriever.py |
Add regression test asserting sibling retriever task receives cancellation. |
test/components/retrievers/test_multi_query_text_retriever_async.py |
Add regression test asserting sibling query task receives cancellation. |
test/components/retrievers/test_multi_query_embedding_retriever_async.py |
Add regression test asserting sibling query task receives cancellation. |
releasenotes/notes/cancel-retriever-sibling-tasks-2d76cfc76a9b3ced.yaml |
Document the fix in release notes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Coverage reportClick to see where and how coverage changed
This report was generated by python-coverage-comment-action |
||||||||||||||||||||||||||||||||||||||||||||||||
| tasks = [asyncio.create_task(self._run_one_async(query, retriever_kwargs)) for query in queries] | ||
| try: | ||
| results = await asyncio.gather(*tasks) | ||
| except Exception: | ||
| for task in tasks: | ||
| task.cancel() | ||
| await asyncio.gather(*tasks, return_exceptions=True) | ||
| raise |
There was a problem hiding this comment.
This is duplicate in all components, maybe we can export this to a utility function in haystack/utils/async_utils.py to avoid duplicate code?
There was a problem hiding this comment.
Thanks for pointing this out! I extracted the shared cancel-and-drain logic into _gather_tasks_with_cancel in haystack/utils/async_utils.py and updated all three components to use it. The three existing cancellation regression tests still pass.
Related Issues
Proposed Changes:
MultiRetriever.run_async,MultiQueryTextRetriever.run_async, andMultiQueryEmbeddingRetriever.run_asyncusedasyncio.gather()to run retrieval calls concurrently. When one call failed, the exception was propagated immediately, but the remaining sibling tasks continued running.These components share an intentional implementation lineage:
MultiRetrieverwas introduced in feat: Add new componentsTextEmbeddingRetrieverandMultiRetriever#10872 with parallel sync and async retrieval paths.MultiQueryTextRetriever.run_asyncandMultiQueryEmbeddingRetriever.run_asyncwere introduced in feat: add run_async to TextEmbeddingRetriever, MultiQueryEmbeddingRetriever, and MultiQueryTextRetriever #11367, which explicitly followed the pattern established byMultiRetriever.run_async._execute_component_asyncutility asPipeline.Since
Pipelinealready cancels and drains its remaining in-flight tasks when one component fails, these components should apply the same failure semantics to the nested concurrent tasks they manage internally.This change aligns them with the existing
Pipelinebehavior:return_exceptions=Truebefore re-raising the original exception.The existing exception behavior and successful result handling remain unchanged.
How did you test it?
Affected unit tests
Ran the unit test suites for all three affected retriever components. The new regression tests verify that an in-flight sibling task is cancelled when another concurrent retrieval task fails.
The 10 deselected tests are marked as integration tests and are excluded by the standard unit test configuration. They were not failures or skips caused by this change.
Additional checks
Ran the project type checks, Ruff linting and formatting, and all applicable pre-commit hooks.
Notes for the reviewer
The implementation intentionally follows the existing
Pipelinecancel-and-drain pattern, preserving the current first-exception behavior.This PR was developed with the assistance of an AI assistant. I have reviewed and understand the changes, and I take responsibility for this contribution.
Checklist
fix:,feat:,build:,chore:,ci:,docs:,style:,refactor:,perf:,test:and added!in case the PR includes breaking changes.