diff --git a/invokeai/app/api/routers/videos.py b/invokeai/app/api/routers/videos.py index c535aa3b931..29e8b99cdf7 100644 --- a/invokeai/app/api/routers/videos.py +++ b/invokeai/app/api/routers/videos.py @@ -346,8 +346,12 @@ async def upload_video( pass +# Declared sync (`def`, not `async def`) so FastAPI runs it in the threadpool: every call +# below is blocking SQLite/filesystem work, which would stall the event loop — and with it +# every other request and socket event — for the duration of the delete. The batch +# siblings below are sync for the same reason. @videos_router.delete("/i/{video_name}", operation_id="delete_video", response_model=DeleteVideosResult) -async def delete_video( +def delete_video( current_user: CurrentUserOrDefault, video_name: str = PathParam(description="The name of the video to delete"), ) -> DeleteVideosResult: @@ -451,8 +455,9 @@ def delete_uncategorized_videos( ) +# Sync for the same reason as delete_video: the update is a blocking SQLite write. @videos_router.patch("/i/{video_name}", operation_id="update_video", response_model=VideoDTO) -async def update_video( +def update_video( current_user: CurrentUserOrDefault, video_name: str = PathParam(description="The name of the video to update"), video_changes: VideoRecordChanges = Body(description="The changes to apply to the video"), diff --git a/tests/app/routers/test_videos_multiuser.py b/tests/app/routers/test_videos_multiuser.py index 8282346b114..346173039f5 100644 --- a/tests/app/routers/test_videos_multiuser.py +++ b/tests/app/routers/test_videos_multiuser.py @@ -28,10 +28,12 @@ VideoNamesBatch, _is_mp4_file, delete_uncategorized_videos, + delete_video, delete_videos_from_list, get_video_thumbnail, star_videos_in_list, unstar_videos_in_list, + update_video, ) from invokeai.app.api_app import app from invokeai.app.services.invoker import Invoker @@ -932,3 +934,19 @@ def test_delete_uncategorized_videos_is_offloaded_by_fastapi() -> None: ) def test_video_batch_mutations_are_offloaded_by_fastapi(handler: Any) -> None: assert not inspect.iscoroutinefunction(handler) + + +@pytest.mark.parametrize( + "handler", + [ + delete_video, + update_video, + ], +) +def test_single_video_mutations_are_offloaded_by_fastapi(handler: Any) -> None: + """Single-item mutations do blocking SQLite/disk work, same as their batch siblings. + + Declared ``async def``, they would run that work directly on the event loop and stall + every other request and socket event until the delete/update finished. + """ + assert not inspect.iscoroutinefunction(handler)