Cleaning DNS cache with Azure Search Client
My customer experienced a shortage of Azure Search. The problem is, the Azure Search successfully fail over by switching DNS. However, the SDK keep the DNS cache, so the shortage last for a while. According to the API of the Azure Search SDK, it seems no way to clear cache if the fail over happens.
In this blog post, I’ll explain how to clear the DNS cache using Azure Search Client.
Cleaning DNS cache with HttpClient
Inside of the Azure Search SDK, they use HttpClient. Generally speaking, we should have only one instance of HttpClient on your application. For the Azure Functions or AppServices, you need to cache the HttpClient to avoid to exhaust the outbound port. Which means, we can’t re-create HttpClient.
In this case, we can use SocketsHttpHandler to configure two properties. SocketsHttpHandler support connection ppling.
- PooledConnectionIdleTimeout
- PooledConnectionLifetime
If you set the PooledConnectionIdleTimeout, the HttpConnectionPoolManager create a timer which is check the status per PooledConnectionIdleTimeout / 4 (ScanvagesPerIdol).
If you start a new request, it check the CachedConnection.IsUsable() method to check the timeout and lifetime.
For the HttpClient, you can simply set the SocketsHttpHandler on the constructor.
var sockesHttpHandler = new SocketsHttpHandler();sockesHttpHandler.PooledConnectionIdleTimeout = TimeSpan.FromMinutes(3);sockesHttpHandler.PooledConnectionLifetime = TimeSpan.FromMinutes(1);var client = new HttpClient(sockesHttpHandler);
Cleaning DNS cache for Azure Search SDK
However, Azure Search Client only support HttpClientHandler. We can’t set the SocketHttpClient. However, HttpClientHandler has an instance of the SocketsHttpHandler inside of the instance. Let’s use reflection in this case. _socketsHttpHandler
is the one you need to use reflection. The code is implemented on HttpClientHandler.Windows However, you can also find HttpClientHandler.Unix
Create a class which inherit HttpClientHandler
public class KeepAliveAvailableHttpClientHandler : HttpClientHandler
{private readonly FieldInfo _fieldInfo = typeof(HttpClientHandler).GetField("_socketsHttpHandler", BindingFlags.NonPublic | BindingFlags.Instance);public KeepAliveAvailableHttpClientHandler()
{
var socketHttpHandler = (SocketsHttpHandler) _fieldInfo.GetValue(this);
socketHttpHandler.PooledConnectionIdleTimeout = TimeSpan.FromMinutes(3);
socketHttpHandler.PooledConnectionLifetime = TimeSpan.FromMinutes(1);
}
}
Accepting self-signed certificate
If you need to accept self-signed certificate, let’s configure it.
var handler = new KeepAliveAvailableHttpClientHandler();
handler.ServerCertificateCustomValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
{
return true;
};
Create a client
Set the class to the Azure Search Client.
string searchServiceName = Environment.GetEnvironmentVariable("SearchServiceName");string adminApiKey = Environment.GetEnvironmentVariable("SearchServiceAdminApiKey");var handler = new KeepAliveAvailableHttpClientHandler();handler.ServerCertificateCustomValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
{
return true;
};indexClient = new SearchIndexClient(searchServiceName, "azuresql-index", new SearchCredentials(adminApiKey), handler , null);
Experiment
If you include the EventListener, you can see the log on your output window of the VS.
class MyEventListener : EventListener
{
protected override void OnEventWritten(EventWrittenEventArgs eventData)
{
var memberNameIndex = eventData.PayloadNames.IndexOf("memberName");
var message = new StringBuilder(); for (var i = 0; i < eventData.Payload.Count; i++)
{
if (i == memberNameIndex) continue; if (i > 0)
{
message.Append(", ");
}message.Append(eventData.PayloadNames[i] + "=" + eventData.Payload[i]);
}var last = eventData.Payload.Last().ToString();if (string.IsNullOrWhiteSpace(last)) return;var content = message.ToString();if (content.Contains("devopshackfestfuji.simplearchitect.club"))
Debug.WriteLine(message);}
}:
var netEventSource = EventSource.GetSources().FirstOrDefault(es => es.Name == "Microsoft-System-Net-Http");if (netEventSource != null)
{
var myEventListener = new MyEventListener();
myEventListener.EnableEvents(netEventSource, EventLevel.LogAlways);
}
You can see the Cleaning pool is works and closing the Connection pool according to the timeout and lifetime settings.
Conclusion
Now we can enable DNS cache for Azure Search Client. It works both on Windows and Linux.
I hope in the future, Azure Search Client (or Azure SDK ServiceClient) support caching clear feature or simply accept SocketsHttpHandler.
You can see the whole sample code of mine.
I submit an issue. See what happens.