Why is the WCF Client timeout ignored - c#

I have a WCF client with the following piece of code written:
MyClient myClient = new MyClient();
string id = Guid.NewGuid();
string result = myClient.Foo(id);
Console.WriteLine(result);
This works, but I want to add a time limit for the service call, so an exception will be thrown if the operation takes too long. I tried adding the timeout in the config file at the binding element like so:
<basicHttpBinding>
<binding
receiveTimeout="00:00:05"
sendTimeout="00:00:05"
</binding>
</basicHttpBinding>
This doesn't seem to work sadly.
I also tried setting it manually in the code file like so:
MyClient myClient = new MyClient();
myClient.Endpoint.Binding = new BasicHttpBinding()
{
SendTimeout = new TimeSpan(0, 0, 5),
ReceiveTimeout = new TimeSpan(0, 0, 5)
};
string id = Guid.NewGuid();
string result = myClient.Foo(id);
Console.WriteLine(result);
But again, it doesn't seem to work.
I tested it with a really slow service, and after 20 minutes it finally returned with the (correct) answer, but a timeout exception was not thrown.
Is it possible that the WCF service I am trying to reach is somehow blocking timeouts?

I've been down this rabbit hole before. I think OperationTimeout might be what you're looking for.
(myClient as IClientChannel).OperationTimeout = TimeSpan.FromSeconds(5);

Answering my own question, almost a year later.
I originally left this bug unresolved since it used to rarely happen. It resurfaced recently so I had to dig into it once again.
As I found out, a TimeoutException wasn't thrown, simply because the SOAP request finished successfully. A timeout is only honoured from the moment the request goes out until a response returns. I verified that using fiddler. But still my code was hanging for hours.
It appears the part blocking my code was simply the parsing of the SOAP response. The default WCF parser was simply hanging forever when trying to parse specific responses as XElement.
I'm playing around with using different custom parsers and will post the results when I finish.

Related

.Net HttpClient.timeout means what?

I am working on HttpClient.timeout in .NET Core 2.2. I am wondering that - the timeout here counts the connection time (time when the request builds the connection with the remote host) and the socket time (after connection builds, the time for the host to wait for incoming data) as a whole or just connection time?
I've searched the official docs here but there seems no explanation. Could anyone help me? Thank you.
with WinHttpHandler You can set different timeouts as you can see bellow
var httpMessageHandler = new System.Net.Http.WinHttpHandler();
httpMessageHandler.SendTimeout = TimeSpan.FromSeconds(150);
httpMessageHandler.ReceiveDataTimeout = TimeSpan.FromSeconds(80);
httpMessageHandler.ReceiveHeadersTimeout = TimeSpan.FromSeconds(70);
var httpClient = new HttpClient(httpMessageHandler);
httpClient.Timeout = TimeSpan.FromSeconds(200);
for you question as far as i know httpClient.Timeout is the whole but you can test it with the above.

Calls to an API fails after some time when multiple users are logged in and using the application. Is there a way to solve this?

I am making some calls to an api that syncs and saves appointments to/from my application and mail agenda which is most commonly Outlook mails. The calls to the api are made from a web application and it is working fine for some time but then immediately after a few hours, the calls are failed. This continues for some time and starts to work again after some time.
The DefaultConnectionLimit was set to 100 in the beginning and during this scenario, the process stopped working after some time (Say 30 mins to 1 hour). Then DefaultConnectionLimit was set to 20 and in this case, it worked fine for 3-4 hours and stopped working after that. I don't know if this is the actual cause but just mentioning.
The code where the call to the api is made from the web application is mentioned below :
public bool SyncNewAppToExch(string encryptedAppSyncDetails)
{
try
{
string result = string.Empty;
JArray paramList = new JArray();
paramList.Add(encryptedAppSyncDetails);
var emailApiUrl = ConfigurationManager.AppSettings["emailApiUrl"].ToString();
Uri baseAddress = new Uri(emailApiUrl);
var url = "/api/EmailProcess/ManualSaveAppointments";
HttpClient client = new HttpClient();
client.BaseAddress = baseAddress;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
Task.Run(() => client.PostAsJsonAsync(url, paramList)).Wait(5000);
var responseMessage = true;
if (responseMessage)
return true;
else
return false;
}
catch (Exception ex)
{
return false;
}
}
The exception that follows the api call failure reads :
Exception : System.AggregateException: One or more errors occurred. System.Threading.Tasks.TaskCanceledException: A task was canceled. --- End of inner exception stack trace --- at System.Threading.Tasks.Task1.GetResultCore(Boolean waitCompletionNotification) at System.Threading.Tasks.Task1.get_Result()
If you are making a new HttpClient object each time you want to make a call, you are not using it appropriately. HttpClient may continue to use sockets even if it is no longer being used and this will likely cause socket exhaustion under load. You can see more on this topic here
You should create a single instance and reuse it. If you are using .Net core it would be a better idea to use the HttpClientFactory.
In addition, using Task.Run to call an async method is not wise. You are setting yourself up for a deadlock. It would be much better to mark your method as async and await, if possible. Since you are effectively doing fire and forget, the way you have done this if your App Domain shuts down this work will be lost.
If you need to do this, you should at least consider registering your task as such:
HostingEnvironment.QueueBackgroundWorkItem(ct => SendMailAsync(user.Email));
The short answer is that it is difficult to say what your issue is, it could be one of several problems.

ClientBase EndPoint Binding SendTimeout ReceiveTimeout: how to change while debugging

I'm developing a solution with a WCF service and a client that uses the service. Sometimes I'm debugging the service, sometime the client, and sometimes both.
During debugging I get a TimeoutException with additional information
Additional information: The request channel timed out while waiting for a reply after 00:00:59.9950000. Increase the timeout value passed to the call to Request or increase the SendTimeout value on the Binding. The time allotted to this operation may have been a portion of a longer timeout.
The reason if of course that my server is waiting at a breakpoint instead of answering the question.
During debugging I want longer timeouts, preferably without creating a new configuration for my service client, because if other values of this configuration would change, the changer would have to remember that a special configuration for debugging was created.
I think it is something like:
private IMyServiceInterface CreateServiceChannel()
{
var myServiceClient = new MyServiceClient(); // reads from configuration file
if (Debugger.IsAttached)
{
// Increase timeouts to enable slow debugging
...
}
return (IMyServiceInterface)myServiceClient;
}
According to MSDN Binding.SendTimeout Property is used for something else:
SendTimeout gets or sets the interval of time provided for a write operation to complete before the transport raises an exception.
Therefore I'd rather not change this value if not needed.
Is SendTimeout really the best timeout to increase, or is there something like a TransactionTimeout, the timeout between my question and the receipt of the answer?
How to change the timeout programmatically
The article All WCF timouts explained states that indeed there is something like a transaction timeout: IContextChannel.OperationTimeout
The operation timeout covers the whole service call (sending the request, processing it and receiving a reply). In other words, it defines the maximum time a service call is active from a client’s point of view. If not set, WCF initializes the operation timeout with the configured send timeout.
This explains why the TimeoutException that is thrown advises to change the send timeout.
However, it is possible to change the operation timeout without changing the send timeout:
var myServiceClient = new MyServiceClient(); // reads from configuration file
if (Debugger.IsAttached)
{ // Increase timeouts to enable slow debugging:
IContextChannel contextChannel = (IContextChannel)myServiceClient.InnerChannel;
// InnerChannel is of type IClientChannel, which implements IContextChannel
// set the operation timeout to a long value, for example 3 minutes:
contextChannel.OperationTimeout = TimeSpan.FromMinutes(3);
}
return (IMyInterface)myService;

Why would my C# app fail on this REST request but but works fine through a browser?

I have a C# app and I am accessing some data over REST so I pass in a URL to get a JSON payload back. I access a few different URLs programmatically and they all work fine using this code below except one call.
Here is my code:
var url = "http://theRESTURL.com/rest/API/myRequest";
var results = GetHTTPClient().GetStringAsync(url).Result;
var restResponse = new RestSharp.RestResponse();
restResponse.Content = results;
var _deserializer = new JsonDeserializer();
where GetHTTPClient() is using this code below:
private HttpClient GetHTTPClient()
{
var httpClient = new HttpClient(new HttpClientHandler()
{
Credentials = new System.Net.NetworkCredential("usr", "pwd"),
UseDefaultCredentials = false,
UseProxy = true,
Proxy = new WebProxy(new Uri("http://myproxy.com:8080")),
AllowAutoRedirect = false
});
httpClient.Timeout = new TimeSpan(0,0, 3500);
return httpClient;
}
so as i said, the above code works fine but a bunch of different request but for one particular request, I am getting an exception inside of the
.GetStringAsync(url).Result
call with the error:
Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host
I get that error after waiting for about 10 minutes. What is interesting is that if I put that same URL that isn't working into Internet Explorer directly I do get the JSON payload back (after about 10 minutes as well). So i am confused at why
It would work fine directly from the browser but fail when using the code above.
It fails on this one request but other requests using the same code work fine programmatically.
Any suggestions for things to try or things I should ask the owner of the server to check out on their end to help diagnose what is going on?
I think the timeout is not an issue here, as the error states that connection has been closed remotely and the set timeout is about 58 minutes, which is more than enough compared to your other figures.
Have you tried looking at the requests itself? Might want to edit your question with those results.
If you remove line httpClient.Timeout = new TimeSpan(0,0, 3500); the issue should be solved but if the request would last 20 minutes you should wait all the time.

Performance with Web API

I'm with some performance problems working with WEB Api. On my real/production code, I'll do a SOAP WS call, on this sample, I'll just sleep. I have 400+ clients sending request's to the Web API.
I guess it's a problem with web api, because if I open 5 process, I can handle more requests than when I'm only with one process.
My test async version of the controller looks like this
[HttpPost]
public Task<HttpResponseMessage> SampleRequest()
{
return Request.Content.ReadAsStringAsync()
.ContinueWith(content =>
{
Thread.Sleep(Timeout);
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(content.Result, Encoding.UTF8, "text/plain")
};
});
}
The sync version looks like this
[HttpPost]
public HttpResponseMessage SampleRequest()
{
var content = Request.Content.ReadAsStringAsync().Result;
Thread.Sleep(Timeout);
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(content, Encoding.UTF8, "text/plain")
};
}
My client code to this test, looks like this (it is configured to time out after 30 seconds)
for (int i = 0; i < numberOfRequests; i++)
{
tasks.Add(new Task(() =>
{
MakeHttpPostRequest();
}));
}
foreach (var task in tasks)
{
task.Start();
}
I was not able to put it here in a nice way, but the table with the results are available at github
The CPU, memory and disk IO is low. There's always at least 800 available threads (both worker and io threads)
public static void AvailableThreads()
{
int workerThreads;
int ioThreads;
ThreadPool.GetAvailableThreads(out workerThreads, out ioThreads);
Console.WriteLine("Available threads {0} ioThreads {1}", workerThreads, ioThreads);
}
I've configured the DefaultConnectionLimit
System.Net.ServicePointManager.DefaultConnectionLimit = Int32.MaxValue;
My question is why there's a queue to answer those request?
In every test, I began with a response time almost exactly like the server Thread.Sleep() time, but the responses get slower as new request arrive.
Any tip on how I can discover where's the bootleneck?
It is a .net 4.0 solution, using self host option.
Edit: I've also tested with .net 4.5 and Web API 2.0, and got the same behaviour.
First requests got the answer almost as soon as sleep expires, later it takes up to 4x the sleep time to get an answer.
Edit2: Gist of the web api1 implementation and gist of the web api2 implementation
Edit3: The MakeHttpPost method creates a new WebApiClient
Edit4:
If I change the
Thread.Sleep()
to
await Task.Delay(10000);
in the .net 4.5 version, it can handle all requests, as expected. So I don't think something related to any network issue.
Since Thread.Sleep() blocks the thread and Task.Delay don't, looks like there's an issue with webapi to consume more threads? But there's available threads in the threadpool...
Edit 5: If I open 5 servers and double the number of clients, the server can respond to all requests. So looks like it's not a problem with number of request to a server, because I can 'scale' this solution running a lot of process in different ports. It's more a problem with the number of request to the same process.
How to Check the TCP/IP stack for overloading
Run Netstat on the server having the issue, look for any Time-Waits, Fin-Wait-1, Fin-Wait-2 and RST-Wait, RST-Wait2. These are half-baked sessions whereby the stack is waiting for the other side to clean up.....OR.....the other side did send a packet in but the local machine could not process them yet depending on the Stacks' availability to do the job.
The kicker is that even sessions showing Established could be in trouble in that the time-out hasn't fired yet.
The symptoms described above are reminiscent of network or TCP/IP stack overload. Very similar behavior is seen when routers get overloaded.

Categories