HiveBrain v1.2.0
Get Started
← Back to all entries
debugcsharpMinorCanonical

General Retry Strategy #2

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
strategyretrygeneral

Problem

Previous version

Now supports async operations and cancellation.

Let’s say we copy some file using retry strategy (it might be blocked, etc.). App code comes bellow:

class Processor
{
    public void CopyData() =>
        CopyData(IOTry.Slow);

    public void CopyData(Try loop) =>
        loop.Execute(() => 
            File.Copy(@"c\a.txt", @"c:\b.txt"));
}


Where:

public class IOTry
{
    public static readonly Try Slow = Try.Retry(delay: 1000, times: 4, ratio: 3);
    public static readonly Try Fast = Try.Retry(delay: 100, times: 4, ratio: 3);
}


Library code is a way longer now - anybody see a way to simplify?

public abstract class Try
{
    public static readonly Try Never = new Never();
    public static readonly Try Once = Retry(delay: 0, times: 0, ratio: 0);

    public static Try Retry(int delay, int times, double ratio) =>
        RetryAfter(from i in Enumerable.Range(0, times)
                   select delay * Math.Pow(ratio, i) into d
                   select (int)d);

    public static Try RetryAfter(params int[] delays) => RetryAfter(delays.AsEnumerable());
    public static Try RetryAfter(IEnumerable delays) => new Retry(delays);

    public void Execute(Action action) => Execute(action, CancellationToken.None);
    public abstract void Execute(Action action, CancellationToken cancellationToken);

    public Task ExecuteAsync(Action action) => ExecuteAsync(action, CancellationToken.None);
    public Task ExecuteAsync(Action action, CancellationToken cancellationToken) => 
        ExecuteAsync(() => { action(); return Task.CompletedTask; }, cancellationToken);

    public Task ExecuteAsync(Func action) => ExecuteAsync(action, CancellationToken.None);
    public abstract Task ExecuteAsync(Func action, CancellationToken cancellationToken);

    public Try FailFast() => FailFast(0);
    public Try FailFast(int timeout) => new Breaker(this, timeout);
}


And NULL Object pattern:

```
class Never : Try
{
public override voi

Solution

I was just going to borrow your code and implement it in my application ;-) but there is one thing that I'm missing here. I needed to know which exceptions were thrown.

That's why I want to suggest you to extend the framework so that after the retries are finished you can log what happened.

I could imagine the new API like this:

class Processor
{
    private readonly ILogger _logger = ...;

    public void CopyData() =>
        CopyData(IOTry.Slow);

    public void CopyData(Try loop) =>
        loop.Execute(() => 
            File.Copy(@"c\a.txt", @"c:\b.txt"),
            ex => _logger.Log(ex)
        );
}


where the second lambda passes either each exception that occured or a collection of exceptions or an AggregateException - perhaps all three APIs make sense in different situations.

The API could either pass each exception that occured right away or collect them and pass them to me when the retry is finished. I'm not sure what could work best.

Code Snippets

class Processor
{
    private readonly ILogger _logger = ...;

    public void CopyData() =>
        CopyData(IOTry.Slow);

    public void CopyData(Try loop) =>
        loop.Execute(() => 
            File.Copy(@"c\a.txt", @"c:\b.txt"),
            ex => _logger.Log(ex)
        );
}

Context

StackExchange Code Review Q#140233, answer score: 2

Revisions (0)

No revisions yet.