Skip to content

Commit

Permalink
List eLabFTW endpoint contents on a separate event loop within the sy…
Browse files Browse the repository at this point in the history
…nc route thread

FastAPI runs sync routes on a separate thread (from a threadpool), and async routes on the main thread's event loop. Originally, it was conceived to convert `_list` to an async method (see #19256). However, given that all other file source plugins are blocking, they could block the main thread for a significant amount of time (see fastapi/fastapi#3091). Thus, the implementation below creates a new event loop within the sync route's thread so that the eLabFTW plugin can still send concurrent requests without blocking the main thread.
  • Loading branch information
kysrpex committed Dec 16, 2024
1 parent 5e216ea commit 204b5fe
Showing 1 changed file with 45 additions and 1 deletion.
46 changes: 45 additions & 1 deletion lib/galaxy/files/sources/elabftw.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ def _serialization_props(self, user_context: OptionalUserContext = None) -> eLab

return cast(eLabFTWFilesSourceProperties, effective_props)

async def _list(
def _list(
self,
path="/",
recursive=False,
Expand All @@ -270,13 +270,57 @@ async def _list(
# `sort_by: Optional[Literal["name", "uri", "path", "class", "size", "ctime"]] = None,`
# from Python 3.9 on, the following would be possible, although barely readable
# `sort_by: Optional[Literal[*(get_type_hints(RemoteDirectory) | get_type_hints(RemoteFile)).keys()]] = None,`
) -> Tuple[List[AnyRemoteEntry], int]:
"""
List the contents of an eLabFTW endpoint.
FastAPI runs sync routes on a separate thread (from a threadpool), and async routes on the main thread's event
loop. Originally, it was conceived to convert `_list` to an async method (see
https://github.com/galaxyproject/galaxy/pull/19256). However, given that all other file source plugins are
blocking, they could block the main thread for a significant amount of time (see
https://github.com/fastapi/fastapi/issues/3091). Thus, the implementation below creates a new event loop within
the sync route's thread so that the eLabFTW plugin can still send concurrent requests without blocking the main
thread.
"""
event_loop = asyncio.new_event_loop()
try:
asyncio.set_event_loop(event_loop)
return event_loop.run_until_complete(
self._list_async(
path=path,
recursive=recursive,
user_context=user_context,
opts=opts,
limit=limit,
offset=offset,
query=query,
sort_by=sort_by,
),
)
finally:
event_loop.close()

async def _list_async(
self,
path="/",
recursive=False,
user_context: OptionalUserContext = None,
opts: Optional[FilesSourceOptions] = None,
limit: Optional[int] = None,
offset: Optional[int] = None,
query: Optional[str] = None,
sort_by: Optional[str] = None,
# in particular, expecting
# `sort_by: Optional[Literal["name", "uri", "path", "class", "size", "ctime"]] = None,`
) -> Tuple[List[AnyRemoteEntry], int]:
"""
List remote entries in a remote directory.
List entity types ("experiment" and "resource"), entity ids of a specific type, or the ids of files attached to
an entity.
Meant to be called only by `_list()`.
:param path: Path referring to the root, an entity type, or an entity id.
:type path: str
:param recursive: List recursively, including all entity types for the root, all entities for each entity type
Expand Down

0 comments on commit 204b5fe

Please sign in to comment.