0

After fetching data from the database, I serialize it into XML.

I then write that XML into a Redis caching instance as a string.

I want the endpoint to check if data exists in the cache and based on the result either return the data from the cache, or hit the DB, cache the data and then return that.

My code works just fine when executed synchronously:

Working Synchronous Code

[HttpGet]
public IHttpActionResult Test(int userId)
{
  var response = new HttpResponseMessage(HttpStatusCode.OK);
  var data = Cache.StringGet("Cart:" + userId);

  if (string.IsNullOrEmpty(data))
  {
    // Grab data from DB and format as XML
    Cache.StringSet("Cart:" + userId, data, TimeSpan.FromHours(2));
  }

  response.Content = new StringContent(data);
  response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/xml");
  return new ResponseMessageResult(response);
}

Where everything goes bananas is when trying to make it Asynchronous.

Broken Async Code ( I included the smallest amount of code necessary to reproduce the issue )

[HttpGet]
public async Task<HttpResponseMessage> TestAsync(int userId)
{
  var data = await Cache.StringGetAsync("Cart:" + userId);
  var response = Request.CreateResponse(HttpStatusCode.OK);
  response.Content = new StringContent("<test>Test</test>");
  response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/xml");
  return response;
}

Note that in this example above I'm not even accessing the asynchronously loaded data. If I comment out the await line, things start working again. It only fails if a await is in the code.

The problem that occurs is that 50% of the time, requests to the endpoint just ... stall and never finish. Fiddler screenshot attached to highlight the issue.

Fiddler Grab

Finally, if there is a easier way to skip media formatting and content negotiation, i'd be more than happy to change my approach.

I should add that the service that will consume this endpoint only supports XML, and it made no sense to me to deserialize and reserialize on every request.

8
  • Have you tried doing await Cache.StringGetAsync("Cart:" + userId).ConfigureAwait(false) instead? It sounds like you could be experiencing a typical ASP.Net deadlock problem (the way it is organized, it loves to deadlock with async/await). Commented Nov 6, 2014 at 3:40
  • In your broken code why are you trying to access Cache.StringGetAsync, when in the working code you are calling Cache.StringGet, isn't the TestAsync call which is already Async and now it would await the result for call the Cache.StringGet. Would you just check if this resolve the issue Commented Nov 6, 2014 at 4:12
  • @MrinalKamboj I was under the impression that proper async / await design was to make every method in the chain asynchronous to avoid blocking error. E.g. async controller calls async service calls async db repository which uses a async method of retrieving the data. Removing the await does fix the problem, but also makes it synchronous as the controller runs on the HttpContext thread. Cache.StringGetAsync is a remote call to the redis instance and should be asynchronous if possible. Commented Nov 6, 2014 at 4:31
  • @LosFrijoles I just tried with adding ConfigureAwait(false), but it did not solve the issue. Everything remained the same. Commented Nov 6, 2014 at 4:36
  • what version of .net are you using? Commented Nov 6, 2014 at 5:48

1 Answer 1

1

Problem Solved!

It ended up being Azure Application Insights.

I guess it does not fully support async or has issues with async in combition with manually creating HttpResponseMessages.

Thank you all for the responses.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.