Aysnc Await
The async/await operators have been around for some time (since C# 5.0, introduced in 2012) and have been of substantial use. A question about their use arises however: Why aren’t they used for every function? One of the reasons I believe is that they make debugging a bit tougher, and if you are not hurting for threads, why not just make your function synchrounous?
It seems as though I have gotten burned for not using these in a proper way. Most of the time, you can afford to make code synchronous, but recently we at AACN had a major increase in traffic due to the release of a free COVID-19 course.
In our code that receives these updates I put in the following line:
var response = _coursesService.ForwardCourseUpdatesToMicroservice(myDecodedString).Result;
Notice the “.Result” . This forces the code to block until the function completes, even though it is an asynchronous function.
Full code block:
public async Task<HttpResponseMessage> ForwardCourseUpdatesToMicroservice(string requestString)
{
rspRegistrationreport request;
using (var reader = new StringReader(requestString))
{
XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = "registrationreport";
xRoot.IsNullable = true;
var serializer = new System.Xml.Serialization.XmlSerializer(typeof(rspRegistrationreport), xRoot);
request = (rspRegistrationreport)serializer.Deserialize(reader);
}
var response = _coursesService.ForwardCourseUpdatesToMicroservice(myDecodedString).Result;
var rspJson = JsonConvert.SerializeObject(request);
var message = new HttpRequestMessage();
var httpContent = new StringContent(rspJson, Encoding.UTF8, "application/json");
message.Content = httpContent;
message.RequestUri = new Uri(_serviceUrl + "/api/courses/progressupdate");
var webClient = new HttpClient();
HttpResponseMessage response;
using (HttpClient client = new HttpClient())
{
response = await client.PostAsync(new Uri(_serviceUrl + "/api/courses/progressupdate"), httpContent);
}
return response;
}
}
What this code does is take a response, parsse it, and forward it on to another microservice.
In my debugging, I referenced the .Result construct to have the code run synchronously, mostly to save time debugging. The problem was, when load on this function increased to 40 calls/sec, there weren’t enough available threads to handle new requests. This is illustrated in the following diagram:
By removing the following .Result from the code above, you can cut the time down substantially, so the gateway web service can service additional requests, and a thread is not locked up waiting on an external micro service, which in turn is waiting for a database:
What a way to free up resources, all from changing a single line of code!