1

I m a bit puzzled with this. I get an occasional task cancelled exception on the following piece of code. This is not because it times out - it happens too soon.

Assume the MakeRequest in the code below formulates the URL and simply returns with

return await _client.SendAsync(request, cancellationToken).ConfigureAwait(false);

The rest of the code gets the response and makes sure its successful otherwise throws.

    protected async Task<T> MakeRequestAndResponseAsync<T>(
        HttpRequestMessage request,
        CancellationToken cancellationToken = default(CancellationToken))
    {
        var response = await MakeRequest(request, cancellationToken).ConfigureAwait(false);
        return await GetResponse<T>(response, throwsOnNotFound).ConfigureAwait(false);
    }

    protected async Task<T> GetResponse<T>(HttpResponseMessage response, Boolean throwsOnNotFound = true)
    {
        String content = await EnsureSuccessfulResponse(response, throwsOnNotFound).ConfigureAwait(false);
        if (content == null && !throwsOnNotFound) return default(T);
        return JsonConvert.DeserializeObject<T>(content);
    }

    protected async Task<String> EnsureSuccessfulResponse(HttpResponseMessage response, Boolean throwsOnNotFound = true)
    {
        String content = string.Empty;
        if (response.Content != null)
        {
            content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
        }

        if (response.IsSuccessStatusCode) return content;

        var error = JsonConvert.DeserializeObject<ApiErrorResponse>(content);
        throw new ApiErrorException(/*some internal stuff*/);
    }

The whole code is invoked from a Console app like this. This is a multithreaded application and so this code may be invoked multiple times simultaneously.

var result = MakeRequestAndResponseAsync(...).Result;
// do something with result

What I have crossed out so far:

  • The HttpClient is correctly initialised and re-used. It doesnt get disposed mid-way.
  • This error happens sometimes. Those requests happen in parallel so I thought the HttpClient SendAsync is not thread-safe but I discovered here that in fact it is thread-safe
  • It's not a timeout exception. I have a unique id per request and i can see in the logs when it starts and when it errors (approx 1-2 seconds)

What I don't know: - If the other service returns a non-200 I intentionally throw a custom exception (ApiErrorException). I suspect it may be related to that, i.e. that the task is cancelled when it throws. If thats the case I would expect to get back the ApiErrorException rather than a task cancelled exception. I unwrap all inner exceptions to the point there isn't anything left. Whats left is the TaskCancelled exception.

Any help would be greatly appreciated.

Thanks

Community
  • 1
  • 1
Yannis
  • 6,047
  • 5
  • 43
  • 62
  • Have you inspected the properties of the exception you're getting? – Paulo Morgado Dec 18 '16 at 20:54
  • @Yannis - How many threads are doing that concurrently? Do they share an `HttpClient`? They _can_ - as you point out `HttpClient` is thread-safe - but there is a default limit of 2 concurrent connections per host that `HttpWebRequest` - which `HttpClient` uses, imposes. Perhaps many threads are having to wait. I'm not sure whether that waiting counts towards any timeouts, but might be worth investigating. – sellotape Dec 18 '16 at 21:23
  • It's 5 threads. – Yannis Dec 18 '16 at 21:37
  • @PauloMorgado Unfortunately I cannot reproduce it locally. It only happens on production where the volume of requests is much higher. – Yannis Dec 18 '16 at 21:48
  • @Yannis - are you able to see the value of `TaskCanceledException.CancellationToken.IsCancellationRequested` in the logs, and if so, what's its value? – sellotape Dec 18 '16 at 21:59
  • @sellotape I am not but I can shuffle get this. – Yannis Dec 21 '16 at 06:38
  • @Yannis, I believe I am having a similar issue. Was there anything else you found out with this? – Chucky Feb 21 '19 at 11:47

0 Answers0