Set Timeout when making external calls in a plug-in
Category: Performance
Impact potential: High
Symptoms
If a plug-in makes external web requests that fail to respond quickly, the plug-in will wait for the full default timeout period before failing. This duration may cause a long transaction that can effect other operations. If the plug-in is registered:
Synchronously, users may experience:
- Unresponsive model-driven apps
- Slow client interactions
- The browser stops responding
Asynchronously, plug-in executions may take an extended period of time before failing.
Guidance
The default timeout value for .Net Http clients is 100 seconds, just 20 seconds short of the time available for the plug-in to complete. It is best to establish an expected baseline time that a calling service will respond. The longer it exceeds this normal response time, the higher the probability it will ultimately fail. As a performance best practice, it is best to fail quickly rather than allow the default timeout period to expire. You should control the period that your call to the external service will wait.
The timeout value you should set will depend on the service. For example, if you can monitor the performance of the service you may determine a duration where 99.999% of requests succeed and set your timeout period to that duration with a few seconds buffer. This will prevent the occasional outliers from having an inordinate impact on the performance of your plug-in.
If you are using System.Net.Http.HttpClient Class, you can set the Timeout
value explicitly, as shown in this example setting the timeout to 15 seconds.
using (HttpClient client = new HttpClient())
{
client.Timeout = TimeSpan.FromMilliseconds(15000); //15 seconds
client.DefaultRequestHeaders.ConnectionClose = true; //Set KeepAlive to false
HttpResponseMessage response = client.GetAsync(webAddress).GetAwaiter().GetResult(); //Make sure it is synchronous
response.EnsureSuccessStatusCode();
string responseText = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); //Make sure it is synchronous
tracingService.Trace(responseText);
//Log success in the Plugin Trace Log:
tracingService.Trace("HttpClientPlugin completed successfully.");
}
If you are using System.Net.WebClient Class, you need to create a derived class and override the base GetWebRequest Method to set the timeout:
/// <summary>
/// A class derived from WebClient with 15 second timeout and KeepAlive disabled
/// </summary>
public class CustomWebClient : WebClient
{
protected override WebRequest GetWebRequest(Uri address)
{
HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(address);
if (request != null)
{
request.Timeout = 15000; //15 Seconds
request.KeepAlive = false;
}
return request;
}
}
Then you can use this class in your plug-in code:
using (CustomWebClient client = new CustomWebClient())
{
byte[] responseBytes = client.DownloadData(webAddress);
string response = Encoding.UTF8.GetString(responseBytes);
tracingService.Trace(response);
//Log success in the Plugin Trace Log:
tracingService.Trace("WebClientPlugin completed successfully.");
}
See also
Sample: Web access from a sandboxed plug-in
Set KeepAlive to false when interacting with external hosts in a plug-in