Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
958f64f
readd cancel on disconnect decorator
bisgaard-itis Jun 30, 2025
176775f
add asyncio.Event for killing poller task
bisgaard-itis Jun 30, 2025
c376bab
add comment
bisgaard-itis Jun 30, 2025
8812a8a
add test for request handler `cancel_on_disconnect` @pcrespov
bisgaard-itis Jul 1, 2025
a80f4e1
use taskgroup for error handling
bisgaard-itis Jul 1, 2025
60e99d0
add old names to tasks
bisgaard-itis Jul 1, 2025
e6c42f7
use event to kill poller task to be sure it terminates
bisgaard-itis Jul 1, 2025
7311ef1
Merge branch 'master' into fix-hang-in-poller-task
bisgaard-itis Jul 1, 2025
e7c97de
Merge branch 'master' into fix-hang-in-poller-task
bisgaard-itis Jul 2, 2025
1229fa3
pylint
bisgaard-itis Jul 2, 2025
4cbc9dc
fix test
bisgaard-itis Jul 2, 2025
df8d9c7
Merge branch 'master' into fix-hang-in-poller-task
bisgaard-itis Jul 2, 2025
09a0f85
Merge branch 'master' into fix-hang-in-poller-task
bisgaard-itis Jul 2, 2025
321fa62
Merge branch 'master' into fix-hang-in-poller-task
bisgaard-itis Jul 4, 2025
e22808a
@pcrespov readd comments
bisgaard-itis Jul 4, 2025
5399294
readd old tests
bisgaard-itis Jul 4, 2025
97afe88
readd
bisgaard-itis Jul 4, 2025
373ad5c
factor out core funcionality into run_until_cancelled
bisgaard-itis Jul 4, 2025
ef3911e
fix tests
bisgaard-itis Jul 4, 2025
a626a29
ensure function decorator works on local deployment
bisgaard-itis Jul 4, 2025
edd826e
migrate middleware to new implementation
bisgaard-itis Jul 4, 2025
241a0e9
improve types
bisgaard-itis Jul 4, 2025
1c091a1
Merge branch 'master' into fix-hang-in-poller-task
bisgaard-itis Jul 4, 2025
431df54
fix request cancellation test
bisgaard-itis Jul 7, 2025
fe5b4ca
improve naming
bisgaard-itis Jul 7, 2025
62808a8
Revert "improve naming"
bisgaard-itis Jul 7, 2025
8c2e369
Merge branch 'master' into fix-hang-in-poller-task
bisgaard-itis Jul 7, 2025
6f6d755
Merge branch 'master' into fix-hang-in-poller-task
bisgaard-itis Jul 16, 2025
f0cfe6b
Merge branch 'master' into fix-hang-in-poller-task
bisgaard-itis Jul 17, 2025
b8e20d2
Merge branch 'master' into fix-hang-in-poller-task
bisgaard-itis Aug 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
class _HandlerWithRequestArg(Protocol):
__name__: str

async def __call__(self, request: Request, *args: Any, **kwargs: Any) -> Any:
...
async def __call__(self, request: Request, *args: Any, **kwargs: Any) -> Any: ...


def _validate_signature(handler: _HandlerWithRequestArg):
Expand All @@ -36,13 +35,15 @@ def _validate_signature(handler: _HandlerWithRequestArg):
_POLL_INTERVAL_S: float = 0.01


async def _disconnect_poller(request: Request, result: Any):
async def _disconnect_poller(close_event: asyncio.Event, request: Request, result: Any):
"""
Poll for a disconnect.
If the request disconnects, stop polling and return.
"""
while not await request.is_disconnected():
await asyncio.sleep(_POLL_INTERVAL_S)
if close_event.is_set():
break
return result


Expand All @@ -59,8 +60,12 @@ async def wrapper(request: Request, *args, **kwargs):

# Create two tasks:
# one to poll the request and check if the client disconnected
# sometimes canceling a task doesn't cancel the task immediately. If the poller task is not "killed" immediately, the client doesn't
# get a response, and the request "hangs". For this reason, we use an event to signal the poller task to stop.
# See: https://github.com/ITISFoundation/osparc-issues/issues/1922
kill_poller_event = asyncio.Event()
poller_task = asyncio.create_task(
_disconnect_poller(request, sentinel),
_disconnect_poller(kill_poller_event, request, sentinel),
name=f"cancel_on_disconnect/poller/{handler.__name__}/{id(sentinel)}",
)
# , and another which is the request handler
Expand All @@ -72,6 +77,7 @@ async def wrapper(request: Request, *args, **kwargs):
done, pending = await asyncio.wait(
[poller_task, handler_task], return_when=asyncio.FIRST_COMPLETED
)
kill_poller_event.set()

# One has completed, cancel the other
for t in pending:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ async def upload_files(files: list[UploadFile] = FileParam(...)):
response_model=ClientFileUploadData,
responses=_FILE_STATUS_CODES,
)
@cancel_on_disconnect
async def get_upload_links(
request: Request,
client_file: UserFileToProgramJob | UserFile,
Expand Down Expand Up @@ -421,6 +422,7 @@ async def abort_multipart_upload(
response_model=OutputFile,
responses=_FILE_STATUS_CODES,
)
@cancel_on_disconnect
async def complete_multipart_upload(
request: Request,
file_id: UUID,
Expand Down
Loading