MetalamaCommented examplesException HandlingRetry
Open sandboxFocusImprove this doc

Sample: retry

Retrying failed methods is crucial for ensuring reliability and efficiency in systems such as database transactions or online services. As temporary faults such as network congestion, hardware issues, or software glitches can cause initial failures, it is important to implement retry mechanisms with exponential backoff to increase the probability of success, minimize data loss, and provide a seamless user experience in the face of transient obstacles. Retry mechanisms enable systems to be dependable and resilient.

This series of articles describes how to construct an aspect that automatically retries a failed method. This aspect modifies a method in the following way:

Source Code





1internal class RemoteCalculator
2{
3    private static int _attempts;
4
5    [Retry( Attempts = 5 )]
6    public int Add( int a, int b )
7    {




8        // Let's pretend this method executes remotely
9        // and can fail for network reasons.
10
11        Thread.Sleep( 10 );
12
13        _attempts++;
14        Console.WriteLine( $"Trying for the {_attempts}-th time." );
15
16        if ( _attempts <= 3 )
17        {
18            throw new InvalidOperationException();
19        }
20
21        Console.WriteLine( $"Succeeded." );
22
23        return a + b;









24    }
25
26    [Retry( Attempts = 5 )]
27    public async Task<int> AddAsync( int a, int b )
28    {


















29        // Let's pretend this method executes remotely
30        // and can fail for network reasons.
31
32        await Task.Delay( 10 );
33
34        _attempts++;
35        Console.WriteLine( $"Trying for the {_attempts}-th time." );
36
37        if ( _attempts <= 3 )
38        {
39            throw new InvalidOperationException();
40        }
41
42        Console.WriteLine( $"Succeeded." );
43
44        return a + b;








45    }
46}
Transformed Code
1using System;
2using System.Threading;
3using System.Threading.Tasks;
4using Microsoft.Extensions.Logging;
5
6internal class RemoteCalculator
7{
8    private static int _attempts;
9
10    [Retry( Attempts = 5 )]
11    public int Add( int a, int b )
12    {
13        for (var i = 0; ; i++)
14        {
15            try
16            {
17                // Let's pretend this method executes remotely
18                // and can fail for network reasons.
19
20                Thread.Sleep( 10 );
21
22        _attempts++;
23        Console.WriteLine( $"Trying for the {_attempts}-th time." );
24
25        if ( _attempts <= 3 )
26        {
27            throw new InvalidOperationException();
28        }
29
30        Console.WriteLine( $"Succeeded." );
31
32        return a + b;
33            }
34            catch (Exception e) when (i < 5)
35            {
36                var delay = 100 * Math.Pow(2, i + 1);
37                LoggerExtensions.LogWarning(this._logger, $"RemoteCalculator.Add(a = {{{a}}}, b = {{{b}}}) has failed: {e.Message} Retrying in {delay} ms.");
38                Thread.Sleep((int)delay);
39                LoggerExtensions.LogTrace(this._logger, $"RemoteCalculator.Add(a = {{{a}}}, b = {{{b}}}): retrying now.");
40            }
41        }
42    }
43
44    [Retry( Attempts = 5 )]
45    public async Task<int> AddAsync( int a, int b )
46    {
47        for (var i = 0; ; i++)
48        {
49            try
50            {
51                return await this.AddAsync_Source(a, b);
52            }
53            catch (Exception e) when (i < 5)
54            {
55                var delay = 100 * Math.Pow(2, i + 1);
56                LoggerExtensions.LogWarning(this._logger, $"RemoteCalculator.AddAsync(a = {{{a}}}, b = {{{b}}}) has failed: {e.Message} Retrying in {delay} ms.");
57                await Task.Delay((int)delay);
58                LoggerExtensions.LogTrace(this._logger, $"RemoteCalculator.AddAsync(a = {{{a}}}, b = {{{b}}}): retrying now.");
59            }
60        }
61    }
62
63    private async Task<int> AddAsync_Source(int a, int b)
64    {
65        // Let's pretend this method executes remotely
66        // and can fail for network reasons.
67
68        await Task.Delay( 10 );
69
70        _attempts++;
71        Console.WriteLine( $"Trying for the {_attempts}-th time." );
72
73        if ( _attempts <= 3 )
74        {
75            throw new InvalidOperationException();
76        }
77
78        Console.WriteLine( $"Succeeded." );
79
80        return a + b;
81    }
82
83    private ILogger _logger;
84
85    public RemoteCalculator
86    (ILogger<RemoteCalculator> logger = default)
87    {
88        this._logger = logger ?? throw new System.ArgumentNullException(nameof(logger));
89    }
90}

In this series

We start with the most basic implementation and add features progressively.

Description Article
Retry example, step 1: Getting started This is the most basic retry aspect.
Retry example, step 2: Handling async methods In this example, we add support for async methods and call await Task.Delay instead of Thread.Sleep.
Retry example, step 3: Handling cancellation tokens Here, we add support for CancellationToken parameters, which we pass to Task.Delay.
Retry example, step 4: Adding logging We now add proper logging using ILogger and dependency injection.
Retry example, step 5: Using Polly Finally, we show how to use Polly instead of our custom and naïve implementation of the retry logic.