If you use the await keyword does it have any limits or does it wait indefinitely for the task to complete?
EDIT: The full context I have is...
A frontend application issues a non async HTTP request to an async Web API endpoint. Ultimately the endpoint will await a call to a stored proc on a db. The frontend application hits a HTTP timeout after probably 100 seconds. If the proc takes 35 minutes to complete,
- Will the await method wait 35 minutes for the proc to complete or are there limits?
- What happens the await call when the HTTP timeout completes after 100 seconds?
- if it continues to run, what happens when the proc returns a response after 35 minutes?
CodePudding user response:
The await
keyword waits indefinitely. There is no limit in the awaiting. For example the await
below will never proceed to the next line of code. The execution flow will get stuck forever in the await
.
await Task.Delay(Timeout.Infinite);
throw new UnreachableException(); // This line is unreachable.
Starting from .NET 6, the Task
, Task<T>
, ValueTask
and ValueTask<T>
types have a WaitAsync
method, that returns a new task which completes after a specified period of time (represented by a TimeSpan
). In case the timeout
elapses before the completion of the base task, the WaitAsync
task propagates a TimeoutException
.
CodePudding user response:
await
will wait indefinitely unless the operation itself cancels. While you can cancel that using a CancellationTokenSource or Task.WaitAsync
that won't stop the asynchronous operation itself.
In the question's case :
await
will keep waiting but- The client's HTTP request will timeout, so
await
will end with a timeout exception - The endpoints won't notice anything and the stored procedure will keep running for 35 minutes.
- Any results will be lost since the client is no longer waiting for them
If you want to actually cancel the stored procedure you'll have to modify the endpoint to handle a cancellation request and somehow cancel the stored procedure
One way to do this would be to signal the worker code that executes the stored procedure to cancel. Cancelling an ExecuteNonQuery
call wont' cancel the stored procedure itself, but if the code runs with an explicit database transaction, the cancellation exception will abort the transaction and the stored procedure.
Another option is to kill the database session that executes the stored procedure. This will require some bookkeeping, to store the session ID of the executing connection before the call to ExecuteNonQuery
.
Stephen Toub describes these implications in How do I cancel non-cancelable async operations?. The conclusion is
So, can you cancel non-cancelable operations? No. Can you cancel waits on non-cancelable operations? Sure… just be very careful when you do.
What about asynchronous Web APIs?
To paraphrase Simon Wardley What does this mean
?
ASP.NET Web APIs with async
actions?
Those are still RPC-style APIs that just allow the action to use the async
keyword. If the client times out, it won't be able to receive results
Request-Reply style
In this case the client, eg an Angular or Reactjs SPA sends a request to the server and after half an hour the server sends a reply to the client using WebSockets or SignalR.
That's truly asynchronous and the response won't be lost. This requires a different client design, separating the request from the response code. Quite often that can simplify the client though, as the request code no longer has to handle timeout failure cases.
With a bit of care, responses can be sent through SignalR to specific Client/User IDs, not just the requesting client, so that even if the browser closes in the mean time, a new client could receive the results.
Other technologies that can be used for this are Server-Sent events and Push API notifications.
Polling for status
Not very scalable for either client or server. In this case the client starts a request, eg a job and gets back a token. The client polls the server for the job's status. That's very expensive on server resources.