rspec/rules/S6422/csharp/rule.adoc

68 lines
2.8 KiB
Plaintext
Raw Normal View History

== Why is this an issue?
2023-06-02 16:50:50 +02:00
Making https://en.wikipedia.org/wiki/Blocking_(computing)[blocking calls] to `async` methods transforms the code into a synchronous operation. Doing so inside an Azure Function can lead to thread pool exhaustion.
2023-06-02 16:50:50 +02:00
Thread pool exhaustion refers to a situation where all available threads in a thread pool are occupied, and new tasks or work items cannot be scheduled for execution due to the lack of available threads. This can lead to delayed execution and degraded performance.
2023-06-02 16:50:50 +02:00
[source,csharp,diff-id=1,diff-type=noncompliant]
----
2023-06-02 16:50:50 +02:00
class RequestParser
{
2023-06-02 16:50:50 +02:00
[FunctionName(nameof(ParseRequest))]
public static async Task<IActionResult> ParseRequest([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req)
{
// This can lead to thread exhaustion
string requestBody = new StreamReader(req.Body).ReadToEndAsync().Result;
// do stuff...
}
}
----
2023-06-02 16:50:50 +02:00
Instead, https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/[asynchronous] mechanisms should be used:
2023-06-02 16:50:50 +02:00
[source,csharp, diff-id=1,diff-type=compliant]
----
2023-06-02 16:50:50 +02:00
class RequestParser
{
2023-06-02 16:50:50 +02:00
[FunctionName(nameof(ParseRequest))]
public static async Task<IActionResult> ParseRequest([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req)
{
2023-06-02 16:50:50 +02:00
// Non-blocking, asynchronous operation
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
// do stuff...
}
}
----
2023-06-02 16:50:50 +02:00
This applies to multiple methods that are available when working with tasks:
[frame=all]
[cols="^1,^1,^1"]
|===
|Goal | Blocking | Asynchronous
|Wait for the result of a task|`Task.Wait`, `Task.Result` or `Task.GetAwaiter.GetResult`|`await`
|Wait for any of many task to complete|`Task.WaitAny`|`await Task.WhenAny`
|Wait for all of many tasks to complete|`Task.WaitAll`|`await Task.WhenAll`
|Wait a period of time|`Thread.Sleep`|`await Task.Delay`
|===
== Resources
2023-06-02 16:50:50 +02:00
=== Documentation
* Microsoft Learn - https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming[Async/Await - Best Practices in Asynchronous Programming]
* Microsoft Learn - https://learn.microsoft.com/en-us/azure/azure-functions/performance-reliability#use-async-code-but-avoid-blocking-calls[Improve the performance and reliability of Azure Functions - Scalability best practices]
* Github - https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/master/AsyncGuidance.md[Async Guidance by David Fowler]
* S4462 - Calls to "async" methods should not be blocking (a more general version of this rule)
ifdef::env-github,rspecator-view[]
'''
== Implementation Specification
(visible only on this page)
The implementation should be common with S4462. When implementing, should make sure S4462 will ignore Azure Functions.
endif::env-github,rspecator-view[]