So, its been a while since my last post on this blog. About 3 and half year ago, I posted an article about The dark side of the Cloud; network connectivity issues and service unavailability, and although the problem remains, the code behind this feature has changed. A lot.

retry-and-wait

So bringing forth today is a revised example of protecting a HttpHead operation. In this example, I was pointing to https://www.cuemon.net:111/, where I deliberately changed the port to 111 just to force a transient exception.

Any operation can be protected simply by calling one of the overloaded methods on the static TransientOperation class; WithAction, WithFunc or TryWithFunc.

Because this test was written for the .NET Framework, you will notice an exception of ThreadException. In the .NET Platform Standard edition, this would be an AggregateException being thrown.

Common for both editions are, that they will have one inner exception of TransientFaultException should the exception be transient related (options.DetectionStrategy delegate).

For this sample, I am currently marking two exception types; TimeoutException and WebException as transient related exceptions. Should any of these be present in the exception parameter of the DetectionStrategy delegate, the retry logic will be triggered.

With the forthcoming release, you will notice (if you pay close attention) that I have included details about the exception directly on the TransientFaultException class. Before, the information was appended to the Data property, but now a new property has been introduced; Evidence.

Here is the result of these new features:

Cuemon.TransientFaultException: The amount of retry attempts has been reached. NetHttpUtilityExtensions.HttpHead(Uri location) was invoked 5 time(s) over a period of 00:00:36.0418522. Last recovery wait time was 00:00:16, giving a total recovery wait time of 00:00:31. Latency was 00:00:05.0418522.

A quick breakdown shows, that the retry-logic was invoked 5 times over a period of 36 seconds. The total retry wait time was 31 seconds. That leaves 5 seconds latency. The latency in this case is a result of me tweaking the NetHttpUtility.DefaultHttpTimeout to 1 second. Otherwise, latency would have been around 150 seconds.

Any operation can be protected simply by calling one of the overloaded methods on the static TransientOperation class; WithAction, WithFunc or TryWithFunc.

As with most of the "instrumentation" in this framework, you can easily attach a callback method to TransientOperation.FaultCallback and hereby get valuable information/statistic for your liking.

That should be about it. I hope you can see a value in the code sample provided; happy coding!

using System;  
using System.Diagnostics;  
using System.Net;  
using Cuemon.Net.Http;  
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Cuemon.Examples  
{
    [TestClass]
    public class TransientOperationTest
    {
        [TestMethod]
        public void OpenWebsiteTest()
        {
            try
            {
                var website = new Uri("https://www.cuemon.net/");
                var response = TransientOperation.WithFunc(website.HttpHead, options =>
                {
                    options.DetectionStrategy = exception =>
                    {
                        var timeoutException = exception.ParseException<TimeoutException>();
                        var webException = exception.ParseException<WebException>();
                        return (webException != null|| (timeoutException != null);
                    };
                });

                Trace.Write(response.Headers.ToString());
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }

        }
    }
}
Notice the total of 6 exceptions; the first exception was before the retry-logic was triggered. The following 5 exceptions was because of the retry-logic.
Cuemon.Threading.ThreadException: One or more errors occurred.  
   at Cuemon.TransientOperation.WithFuncCore[TTuple,TResult](DoerFactory`2 factory, Act`1 setup) in ---
   at Cuemon.TransientOperation.WithFunc[TResult](Doer`1 faultSensitiveMethod, Act`1 setup) in ---
   at Cuemon.Examples.TransientOperationTest.OpenWebsiteTest() in ---
 --> (Inner exception 0) Cuemon.TransientFaultException: The amount of retry attempts has been reached. NetHttpUtilityExtensions.HttpHead(Uri location) was invoked 5 time(s) over a period of 00:00:36.0418522. Last recovery wait time was 00:00:16, giving a total recovery wait time of 00:00:31. Latency was 00:00:05.0418522.

 --> (Inner exception 1) System.ArgumentException: An error occurred while invoking the asynchronous operation.
Parameter name: request ---> System.TimeoutException: The HTTP request https://www.cuemon.net:111/ took longer than the specified timeout value of 00:00:01.  
   at Cuemon.Net.Http.NetHttpUtility.WebResponseFromAsync(Doer`3 beginMethod, Doer`2 endMethod, HttpWebRequest state) in ---
   --- End of inner exception stack trace ---
   at Cuemon.Net.Http.NetHttpUtility.HttpCore(HttpWebRequest request) in ---
   at Cuemon.Net.Http.NetHttpUtility.HttpCore(Uri location, String method, Stream content, Act`1 setup) in ---
   at Cuemon.Net.Http.NetHttpUtility.Http(String method, Uri location, Act`1 setup) in ---
   at Cuemon.Net.Http.NetHttpUtility.HttpHead(Uri location, Act`1 setup) in ---
   at Cuemon.Net.Http.NetHttpUtility.HttpHead(Uri location) in ---
   at Cuemon.Net.Http.NetHttpUtilityExtensions.HttpHead(Uri location)
   at Cuemon.DoerFactory.<>c__DisplayClass0_0`1.<Create>b__0(Template tuple) in ---
   at Cuemon.DoerFactory`2.ExecuteMethod() in ---
   at Cuemon.TransientOperation.WithFuncCore[TTuple,TResult](DoerFactory`2 factory, Act`1 setup) in ---

 --> (Inner exception 2) System.ArgumentException: An error occurred while invoking the asynchronous operation.
Parameter name: request ---> System.TimeoutException: The HTTP request https://www.cuemon.net:111/ took longer than the specified timeout value of 00:00:01.  
   at Cuemon.Net.Http.NetHttpUtility.WebResponseFromAsync(Doer`3 beginMethod, Doer`2 endMethod, HttpWebRequest state) in ---
   --- End of inner exception stack trace ---
   at Cuemon.Net.Http.NetHttpUtility.HttpCore(HttpWebRequest request) in ---
   at Cuemon.Net.Http.NetHttpUtility.HttpCore(Uri location, String method, Stream content, Act`1 setup) in ---
   at Cuemon.Net.Http.NetHttpUtility.Http(String method, Uri location, Act`1 setup) in ---
   at Cuemon.Net.Http.NetHttpUtility.HttpHead(Uri location, Act`1 setup) in ---
   at Cuemon.Net.Http.NetHttpUtility.HttpHead(Uri location) in ---
   at Cuemon.Net.Http.NetHttpUtilityExtensions.HttpHead(Uri location)
   at Cuemon.DoerFactory.<>c__DisplayClass0_0`1.<Create>b__0(Template tuple) in ---
   at Cuemon.DoerFactory`2.ExecuteMethod() in ---
   at Cuemon.TransientOperation.WithFuncCore[TTuple,TResult](DoerFactory`2 factory, Act`1 setup) in ---

 --> (Inner exception 3) System.ArgumentException: An error occurred while invoking the asynchronous operation.
Parameter name: request ---> System.TimeoutException: The HTTP request https://www.cuemon.net:111/ took longer than the specified timeout value of 00:00:01.  
   at Cuemon.Net.Http.NetHttpUtility.WebResponseFromAsync(Doer`3 beginMethod, Doer`2 endMethod, HttpWebRequest state) in ---
   --- End of inner exception stack trace ---
   at Cuemon.Net.Http.NetHttpUtility.HttpCore(HttpWebRequest request) in ---
   at Cuemon.Net.Http.NetHttpUtility.HttpCore(Uri location, String method, Stream content, Act`1 setup) in ---
   at Cuemon.Net.Http.NetHttpUtility.Http(String method, Uri location, Act`1 setup) in ---
   at Cuemon.Net.Http.NetHttpUtility.HttpHead(Uri location, Act`1 setup) in ---
   at Cuemon.Net.Http.NetHttpUtility.HttpHead(Uri location) in ---
   at Cuemon.Net.Http.NetHttpUtilityExtensions.HttpHead(Uri location)
   at Cuemon.DoerFactory.<>c__DisplayClass0_0`1.<Create>b__0(Template tuple) in ---
   at Cuemon.DoerFactory`2.ExecuteMethod() in ---
   at Cuemon.TransientOperation.WithFuncCore[TTuple,TResult](DoerFactory`2 factory, Act`1 setup) in ---

 --> (Inner exception 4) System.ArgumentException: An error occurred while invoking the asynchronous operation.
Parameter name: request ---> System.TimeoutException: The HTTP request https://www.cuemon.net:111/ took longer than the specified timeout value of 00:00:01.  
   at Cuemon.Net.Http.NetHttpUtility.WebResponseFromAsync(Doer`3 beginMethod, Doer`2 endMethod, HttpWebRequest state) in ---
   --- End of inner exception stack trace ---
   at Cuemon.Net.Http.NetHttpUtility.HttpCore(HttpWebRequest request) in ---
   at Cuemon.Net.Http.NetHttpUtility.HttpCore(Uri location, String method, Stream content, Act`1 setup) in ---
   at Cuemon.Net.Http.NetHttpUtility.Http(String method, Uri location, Act`1 setup) in ---
   at Cuemon.Net.Http.NetHttpUtility.HttpHead(Uri location, Act`1 setup) in ---
   at Cuemon.Net.Http.NetHttpUtility.HttpHead(Uri location) in ---
   at Cuemon.Net.Http.NetHttpUtilityExtensions.HttpHead(Uri location)
   at Cuemon.DoerFactory.<>c__DisplayClass0_0`1.<Create>b__0(Template tuple) in ---
   at Cuemon.DoerFactory`2.ExecuteMethod() in ---
   at Cuemon.TransientOperation.WithFuncCore[TTuple,TResult](DoerFactory`2 factory, Act`1 setup) in ---

 --> (Inner exception 5) System.ArgumentException: An error occurred while invoking the asynchronous operation.
Parameter name: request ---> System.TimeoutException: The HTTP request https://www.cuemon.net:111/ took longer than the specified timeout value of 00:00:01.  
   at Cuemon.Net.Http.NetHttpUtility.WebResponseFromAsync(Doer`3 beginMethod, Doer`2 endMethod, HttpWebRequest state) in ---
   --- End of inner exception stack trace ---
   at Cuemon.Net.Http.NetHttpUtility.HttpCore(HttpWebRequest request) in ---
   at Cuemon.Net.Http.NetHttpUtility.HttpCore(Uri location, String method, Stream content, Act`1 setup) in ---
   at Cuemon.Net.Http.NetHttpUtility.Http(String method, Uri location, Act`1 setup) in ---
   at Cuemon.Net.Http.NetHttpUtility.HttpHead(Uri location, Act`1 setup) in ---
   at Cuemon.Net.Http.NetHttpUtility.HttpHead(Uri location) in ---
   at Cuemon.Net.Http.NetHttpUtilityExtensions.HttpHead(Uri location)
   at Cuemon.DoerFactory.<>c__DisplayClass0_0`1.<Create>b__0(Template tuple) in ---
   at Cuemon.DoerFactory`2.ExecuteMethod() in ---   at Cuemon.TransientOperation.WithFuncCore[TTuple,TResult](DoerFactory`2 factory, Act`1 setup) in ---

 --> (Inner exception 6) System.ArgumentException: An error occurred while invoking the asynchronous operation.
Parameter name: request ---> System.TimeoutException: The HTTP request https://www.cuemon.net:111/ took longer than the specified timeout value of 00:00:01.  
   at Cuemon.Net.Http.NetHttpUtility.WebResponseFromAsync(Doer`3 beginMethod, Doer`2 endMethod, HttpWebRequest state) in ---
   --- End of inner exception stack trace ---
   at Cuemon.Net.Http.NetHttpUtility.HttpCore(HttpWebRequest request) in ---
   at Cuemon.Net.Http.NetHttpUtility.HttpCore(Uri location, String method, Stream content, Act`1 setup) in ---
   at Cuemon.Net.Http.NetHttpUtility.Http(String method, Uri location, Act`1 setup) in ---
   at Cuemon.Net.Http.NetHttpUtility.HttpHead(Uri location, Act`1 setup) in ---
   at Cuemon.Net.Http.NetHttpUtility.HttpHead(Uri location) in ---
   at Cuemon.Net.Http.NetHttpUtilityExtensions.HttpHead(Uri location)
   at Cuemon.DoerFactory.<>c__DisplayClass0_0`1.<Create>b__0(Template tuple) in ---
   at Cuemon.DoerFactory`2.ExecuteMethod() in ---
   at Cuemon.TransientOperation.WithFuncCore[TTuple,TResult](DoerFactory`2 factory, Act`1 setup) in ---