Code Sample: PartitionCollectionTest


About 3 years ago, I posted an article about Too much to digest? Slice it up in chunks!, which worked, but was a rather complex way of slicing items into smaller bites.

Fruit

For a while now, there has been an alternative; the PartitionCollection<T> class in the Cuemon.Collections.Generic namespace. Depending on your .NET installation, there are also extension methods ToPartitionCollection that might come in handy.

The usage is simple, as illustrated here (i have kept the example as close to the original as possible):

using System.Diagnostics;  
using System.Linq;  
using Cuemon.Collections.Generic;  
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Cuemon.Examples  
{
    [TestClass]
    public class PartitionCollectionTest
    {
        [TestMethod]
        public void ChunkTest()
        {
            var workload = 4096;
            var partitionSize = 255;
            var simulatedWorkload = Enumerable.Range(0, workload).ToPartitionCollection(partitionSize);

            Trace.WriteLine("Work to process: {0} items.".FormatWith(workload));
            Trace.WriteLine("Partitions for work: {0} runs.".FormatWith(simulatedWorkload.PartitionCount()));

            while (simulatedWorkload.HasPartitions)
            {
                var chunkedWorkload = simulatedWorkload.ToList();
                foreach (var work in chunkedWorkload)
                {
                    // process your work here
                }
                
                Trace.WriteLine("Remaining work to process: {0} items.".FormatWith(simulatedWorkload.Remaining));
            }
        }
    }
}
The result is well familiar:
Work to process: 4096 items.  
Partitions for work: 17 runs.  
Remaining work to process: 3841 items.  
Remaining work to process: 3586 items.  
Remaining work to process: 3331 items.  
Remaining work to process: 3076 items.  
Remaining work to process: 2821 items.  
Remaining work to process: 2566 items.  
Remaining work to process: 2311 items.  
Remaining work to process: 2056 items.  
Remaining work to process: 1801 items.  
Remaining work to process: 1546 items.  
Remaining work to process: 1291 items.  
Remaining work to process: 1036 items.  
Remaining work to process: 781 items.  
Remaining work to process: 526 items.  
Remaining work to process: 271 items.  
Remaining work to process: 16 items.  
Remaining work to process: 0 items.  

As always; I hope you found these small articles inspiring - and happy coding!

Code Sample: TimeMeasureTest


Every now and then you have to time measure different parts of your system. One approach could be a simple StopWatch or even different DateTime objects subtracted one another. My approach, as with TransientOperation, is to provide a lamda friendly way of performing this task.

Instrumentation

Any measurement can be performed simply by calling one of the overloaded methods on the static TimeMeasure class; WithAction or WithFunc. The latter will contain a property holding the Result of the operation.

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

I hope this little introduction will help simplify your time measurement related operations. Happy coding!

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

namespace Cuemon.Examples  
{
    [TestClass]
    public class TimeMeasureTest
    {
        [TestMethod]
        public void AggregateTimeMeasurementTest()
        {
            var websiteProfiler = TimeMeasure.WithFunc(GetWebpageBodySize, new Uri("https://www.cuemon.net/";));
            Trace.WriteLine("Body size: {0} KB.".FormatWith(websiteProfiler.Result.BytesToKilobytes()));
            Trace.WriteLine("");
            Trace.WriteLine(websiteProfiler);
            var profiler = TimeMeasure.WithAction(MethodThatTakesBetweenOneToFiveSeconds);
            Trace.WriteLine(profiler);
            profiler = TimeMeasure.WithAction(MethodThatTakesBetweenFiveToTwentySeconds);
            Trace.WriteLine(profiler);
            profiler = TimeMeasure.WithAction(MethodWithRandomParameters, false42"flexible"Guid.Empty, 666.666m);
            Trace.WriteLine(profiler);
        }

        public long GetWebpageBodySize(Uri location)
        {
            return location.HttpGet(o => o.AutomaticDecompression = DecompressionMethods.None).ContentLength;
        }

        public void MethodThatTakesBetweenOneToFiveSeconds()
        {
            Thread.Sleep(TimeSpan.FromSeconds(NumberUtility.GetRandomNumber(16)));
        }

        public void MethodThatTakesBetweenFiveToTwentySeconds()
        {
            Thread.Sleep(TimeSpan.FromSeconds(NumberUtility.GetRandomNumber(521)));
        }

        public void MethodWithRandomParameters(bool a, int b, string c, Guid d, decimal e)
        {
            // do nothing
        }
    }
}
Notice the ClassName.MemberName(Parameters) followed by the actual elapsed time and the values of the Paramters (if any).
Body size: 31.3037109375 KB.

TimeMeasureTest.GetWebpageBodySize(Uri location) took 00:00:00.482019300 to execute. Parameters: { location=https://www.cuemon.net/ }  
TimeMeasureTest.MethodThatTakesBetweenOneToFiveSeconds() took 00:00:04.019125500 to execute.  
TimeMeasureTest.MethodThatTakesBetweenFiveToTwentySeconds() took 00:00:08.001355400 to execute.  
TimeMeasureTest.MethodWithRandomParameters(Boolean a, Int32 b, String c, Guid d, Decimal e) took 00:00:00.001324900 to execute. Parameters: { a=False, b=42, c=flexible, d=00000000-0000-0000-0000-000000000000, e=666,666 }  

Code Sample: TransientOperationTest


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 ---

Too much to digest? Slice it up in chunks!


From time to time you will find yourself in the need of slicing up large chunks of bites into smaller chunks of consumable bites.

Since I have been unable to find such a method in the .NET framework, I decided to include one on the EnumerableUtility class found in the Cuemon.Collections.Generic namespace. The name for the method was carefully chosen to be Chunk, and consist of two overloads; both accepting an IEnumerable<T> where the chunk size is defaulted to 128 elements and the other with a custom size.

To see the Chunk method in action, have a look at Figure 1. For simplicity i set a chunk size to 255 and a workload count to 4096. This means that our workload consists of 4096 elements which we will process 255 elements at time. This leaves us with a workload of 17 runs in total.


[TestClass]
public class ChunkExample
{
    [TestMethod]
    public void TestChunk()
    {
        int chunkSize = 255;
        int workload = 4096;
        int expectedWorkloadIterations = (workload + chunkSize - 1 ) / chunkSize;
        IEnumerable<int> simulatedWorkload = new List<int>(EnumerableUtility.Range(0, workload));

        Debug.WriteLine("Work to process: {0} items.", workload);
        Debug.WriteLine("Expected iterations for work: {0} runs.", expectedWorkloadIterations);

        int actualWorkloadIterations = 0;
        while (((List<int>)simulatedWorkload).Count > 0)
        {
            IEnumerable<int> chunkedWorkload = EnumerableUtility.Chunk(ref simulatedWorkload, chunkSize);
            IEnumerator<int> chunkedWorkloadEnumerator = chunkedWorkload.GetEnumerator();
            int countOfChunkedElements = EnumerableUtility.Count(chunkedWorkload);

            Assert.IsTrue(countOfChunkedElements <= chunkSize);

            int enumeratorCount = 0;
            while (chunkedWorkloadEnumerator.MoveNext())
            {
                enumeratorCount++;
            }
            actualWorkloadIterations++;

            Assert.IsTrue(enumeratorCount <= chunkSize);

            Debug.WriteLine("Remaining work to process: {0} items.", ((List<int>)simulatedWorkload).Count);
        }

        Assert.IsTrue(expectedWorkloadIterations == actualWorkloadIterations);
        Debug.WriteLine("Actual iterations for work: {0} runs.", actualWorkloadIterations);
    }
}

Figure 1: A simple test method that utilizes the Chunk method found on the EnumerableUtility class.




When executed, the test method should produce this debug output:




Debug Trace:
Work to process: 4096 items.
Expected iterations for work: 17 runs.
Remaining work to process: 3841 items.
Remaining work to process: 3586 items.
Remaining work to process: 3331 items.
Remaining work to process: 3076 items.
Remaining work to process: 2821 items.
Remaining work to process: 2566 items.
Remaining work to process: 2311 items.
Remaining work to process: 2056 items.
Remaining work to process: 1801 items.
Remaining work to process: 1546 items.
Remaining work to process: 1291 items.
Remaining work to process: 1036 items.
Remaining work to process: 781 items.
Remaining work to process: 526 items.
Remaining work to process: 271 items.
Remaining work to process: 16 items.
Remaining work to process: 0 items.
Actual iterations for work: 17 runs.

Figure 2: The output of the test in Figure 1.



I hope you liked this little peek into one of the many day-to-day helper methods found through out the Cuemon assembly family. Happy coding!


The dark side of the Cloud; network connectivity issues and service unavailability


In these days where everything is moving to "the cloud", new challenges arises that most of us where spared from before; temporary condition such as network connectivity issues or simply a service unavailability. The technical term for this is called Transient Fault Handling and one you must be conversant with.

On MSDN there is a very informative and good background information for Transient Fault Handling which i strongly encourage you to read. While you are there, you could consider if the comprehensive work of the Enterprise Library from version 5.0 suits your needs in what has been named the Transient Fault Handling Application Block.

If it seems a big over the top for your solution, you could consider the static TransientFaultUtility class found in the Cuemon namespace; it is fully compatible with cloud provides such as Windows Azure. It has several overloads for invoking a fault sensitive method, and will continue until the operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to a transient fault condition.

The minimum required parameters for invoking a transient fault protected method is an integer specifying retryAttempts, a function delegate that will determine if isTransientFault and last but not least; a function delegate/action delegate pointing to the faultSensitiveMethod.

To see this in action, have a look at Figure 1. What we do here is simply throwing an HttpException should we encounter a HTTP 502. This can easily be extended to the ones listed in the IsTransientFault callback method. Otherwise we just write some debug information. Figure 2 shows how to consume the TransientFaultExample class and is intentionally set to fail in the first run. Figure 3 shows the Debug Trace.


public class TransientFaultExample
{
    public TransientFaultExample()
    {
        NetHttpUtility.DefaultHttpTimeout = TimeSpan.FromSeconds(15);
    }

    public void OpenWebsite(Uri location)
    {
        using (HttpWebResponse response = NetHttpUtility.HttpGet(location))
        {
            if (response.StatusCode == HttpStatusCode.BadGateway) { throw new HttpException(502, response.StatusDescription); }
            Debug.WriteLine("Status code in response was {0} - {1}.", (int)response.StatusCode, response.StatusDescription);
            Debug.WriteLine("The headers of the response was {0}.", ConvertUtility.ToDelimitedString(response.Headers.AllKeys, ", ", HeaderConverter, response.Headers) as object);
        }
    }

    private string HeaderConverter(string header, WebHeaderCollection headers)
    {
        return string.Format("{0}: {1}", header, headers[header] ?? "null");
    }

    public bool IsTransientFault(Exception exception)
    {
        HttpException httpException = exception as HttpException;
        if (httpException != null)
        {
            switch (httpException.GetHttpCode())
            {
                case 404:
                case 408:
                case 410:
                case 500:
                case 502:
                case 503:
                case 504:
                    return true;
                default:
                    return false;
            }
        }
        return (exception.Message.IndexOf("timed out", StringComparison.OrdinalIgnoreCase) >= 0);
    }
}

Figure 1: A simple TransientFaultExample class that can easily be rewritten to more real-life scenarios


[TestClass]
public class TransientFaultExampleTest
{
    [TestMethod]
    public void TransientFault()
    {
        TransientFaultExample transient = new TransientFaultExample();

        try
        {
            TransientFaultUtility.ExecuteAction(5, transient.IsTransientFault, transient.OpenWebsite, new Uri("http://www.google.com:88/"));
        }
        catch (TransientFaultException ex)
        {
            Debug.WriteLine("TransientFaultException was thrown (which is good): {0}", ConvertUtility.ToString(ex, Encoding.Default, true) as object);
        }

        try
        {
            TransientFaultUtility.ExecuteAction(5, transient.IsTransientFault, transient.OpenWebsite, new Uri("http://www.google.com/"));
        }
        catch (TransientFaultException ex)
        {
            Debug.WriteLine("TransientFaultException was thrown (which is not so good - for Google at least): {0}", ConvertUtility.ToString(ex, Encoding.Default, true) as object);
            Assert.Fail();
        }
    }
}

Figure 2: Consumes the class defined in Figure 1



Debug Trace:

TransientFaultException was thrown (which is good): TransientFaultException (Cuemon)
Source:
    Cuemon
Message:
    The amount of retry attempts has been reached.
Data:
    Key: Attempts
    Value: 5
    Key: RecoveryWaitTimeInSeconds
    Value: 21
    Key: TotalRecoveryWaitTimeInSeconds
    Value: 56
InnerException [of TransientFaultException]:
    TimeoutException (System)
Source:
    Cuemon
Message:
    The operation has timed out.

Status code in response was 200 - OK.
The headers of the response was Cache-Control: private, max-age=0, Content-Type: text/html; charset=ISO-8859-1, Date: Thu, 25 Apr 2013 01:09:52 GMT, Expires: -1, Set-Cookie: PREF=ID=fd0ca0865d752f5b:FF=0:TM=1366852192:LM=1366852192:S=KNM_xhwUAUmLa1-f; expires=Sat, 25-Apr-2015 01:09:52 GMT; path=/; domain=.google.dk,NID=67=vYXlkUfWQ_paZ7fdrkXaq2gmgUati-Y3FfiPzpRLQTTWn7lQgWowKZgE53z4_D1G04SmEk0N_4YdaUKC2RZkajhrCZ69QrCRHKBumdejJg4Z2MKak7fUF0QUbL7nKf3F; expires=Fri, 25-Oct-2013 01:09:52 GMT; path=/; domain=.google.dk; HttpOnly, P3P: CP="This is not a P3P policy! See http://www.google.com/support/accounts/bin/answer.py?hl=en&answer=151657 for more info.", Server: gws, X-XSS-Protection: 1; mode=block, X-Frame-Options: SAMEORIGIN, Transfer-Encoding: chunked.
Figure 3: The output of the test in Figure 2

If you don't like the default recovery wait time this can easily be added using one of the overloads on the TransientFaultUtility class. For your convenience, I have included the default implementation in Figure 4.


/// <summary>
/// Specifies the amount of time to wait for a transient fault to recover gracefully before trying a new attempt.
/// </summary>
/// <param name="currentAttempt">The current attempt.</param>
/// <returns>A <see cref="TimeSpan"/> that defines the amount of time to wait for a transient fault to recover gracefully.</returns>
/// <remarks>Default implementation is <see cref="RecoveryWaitTime"/> + 2^ to a maximum of 5; a total of 5 (default) + 32 = 37 seconds.</remarks>
public static TimeSpan RecoveryWaitTime(int currentAttempt)
{
    TimeSpan sleep = DefaultRecoveryWaitTime;
    sleep = sleep.Add(TimeSpan.FromSeconds(Math.Pow(2, currentAttempt > 5 ? 5 : currentAttempt)));
    return sleep;
}

Figure 4: The default implementation of the function delegate recoveryWaitTime



I hope this little introduction have inspired you to master Transient Fault Handling one way or another. Happy coding.