• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mehdihadeli / food-delivery-microservices / 28675368858

03 Jul 2026 05:34PM UTC coverage: 16.207% (-46.9%) from 63.071%
28675368858

push

github

web-flow
feat: replace masstransit with wolverine (#253)

337 of 3348 branches covered (10.07%)

Branch coverage included in aggregate %.

125 of 258 new or added lines in 26 files covered. (48.45%)

1396 of 7345 relevant lines covered (19.01%)

0.39 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

41.69
/src/BuildingBlocks/BuildingBlocks.Resiliency/DependencyInjectionExtensions.cs
1
using System.Net;
2
using BuildingBlocks.Core.Extensions;
3
using BuildingBlocks.Core.Extensions.ServiceCollectionExtensions;
4
using BuildingBlocks.Resiliency.Options;
5
using Microsoft.Extensions.Hosting;
6
using Microsoft.Extensions.Http.Resilience;
7
using Microsoft.Extensions.Options;
8
using Microsoft.Extensions.ServiceDiscovery;
9
using Polly;
10
using Polly.CircuitBreaker;
11
using Polly.Retry;
12
using Polly.Timeout;
13
using Polly.Wrap;
14

15
namespace BuildingBlocks.Resiliency;
16

17
public static class DependencyInjectionExtensions
18
{
19
    public static IHostApplicationBuilder AddCustomResiliency(
20
        this IHostApplicationBuilder builder,
21
        bool globalHttpClientResiliency = true
22
    )
23
    {
24
        builder.Services.AddServiceDiscovery();
2✔
25

26
        // https://learn.microsoft.com/en-us/dotnet/core/extensions/service-discovery?tabs=dotnet-cli#scheme-selection-when-resolving-https-endpoints
NEW
27
        builder.Services.Configure<ServiceDiscoveryOptions>(options => options.AllowAllSchemes = true);
✔
28

29
        AddResiliencyCore(builder);
2✔
30

31
        if (globalHttpClientResiliency)
2✔
32
        {
33
            // https://learn.microsoft.com/en-us/dotnet/core/resilience/http-resilience
34
            // set resiliency globally on clients
35
            builder.Services.ConfigureHttpClientDefaults(httpClientBuilder =>
2✔
36
            {
2✔
37
                // https://learn.microsoft.com/en-us/dotnet/core/extensions/service-discovery?tabs=dotnet-cli#example-usage
2✔
38
                // Turn on service discovery by default on all http clients
2✔
39
                httpClientBuilder.AddServiceDiscovery();
2✔
40

2✔
41
                // Turn on resilience by default
2✔
42
                httpClientBuilder
2✔
43
                    .AddStandardResilienceHandler()
2✔
44
                    .Configure(
2✔
45
                        (cfg, sp) =>
2✔
46
                        {
2✔
47
                            // resiliency using `Microsoft.Extensions.Http.Resilience`
2✔
48
                            // https://learn.microsoft.com/en-us/dotnet/core/resilience/
2✔
49
                            // https://learn.microsoft.com/en-us/dotnet/core/resilience/http-resilience
2✔
50
                            var policyOptions = sp.GetRequiredService<IOptions<PolicyOptions>>().Value.NotBeNull();
×
51

2✔
52
                            cfg.AttemptTimeout = new HttpTimeoutStrategyOptions
×
53
                            {
×
54
                                Timeout = TimeSpan.FromSeconds(policyOptions.TimeoutPolicyOptions.TimeoutInSeconds),
×
55
                            };
×
56

2✔
57
                            cfg.TotalRequestTimeout = new HttpTimeoutStrategyOptions
×
58
                            {
×
59
                                Timeout = TimeSpan.FromSeconds(policyOptions.TimeoutPolicyOptions.TimeoutInSeconds),
×
60
                            };
×
61

2✔
62
                            cfg.Retry = new HttpRetryStrategyOptions
×
63
                            {
×
64
                                BackoffType = DelayBackoffType.Exponential,
×
65
                                MaxRetryAttempts = policyOptions.RetryPolicyOptions.Count,
×
66
                                UseJitter = true,
×
67
                            };
×
68

2✔
69
                            cfg.CircuitBreaker = new HttpCircuitBreakerStrategyOptions
×
70
                            {
×
71
                                SamplingDuration = TimeSpan.FromSeconds(
×
72
                                    policyOptions.CircuitBreakerPolicyOptions.SamplingDuration
×
73
                                ), // Ensure this is >= 2 * TimeoutInSeconds
×
74
                                BreakDuration = TimeSpan.FromSeconds(
×
75
                                    policyOptions.CircuitBreakerPolicyOptions.DurationOfBreak
×
76
                                ),
×
77
                                MinimumThroughput = policyOptions
×
78
                                    .CircuitBreakerPolicyOptions
×
79
                                    .ExceptionsAllowedBeforeBreaking,
×
80
                                ShouldHandle = static args =>
×
81
                                    ValueTask.FromResult(
×
82
                                        args
×
83
                                            is {
×
84
                                                Outcome.Result.StatusCode: HttpStatusCode.RequestTimeout
×
85
                                                    or HttpStatusCode.TooManyRequests
×
86
                                            }
×
87
                                    ),
×
88
                            };
×
89
                        }
×
90
                    );
2✔
91

2✔
92
                // // https://learn.microsoft.com/en-us/dotnet/core/resilience/http-resilience?tabs=dotnet-cli#add-custom-resilience-handlers
2✔
93
                // httpClientBuilder.AddResilienceHandler(
2✔
94
                //     "CustomPipeline",
2✔
95
                //     (pipelineBuilder, ctx) =>
2✔
96
                //     {
2✔
97
                //         var policyOptions = ctx
2✔
98
                //             .ServiceProvider.GetRequiredService<IOptions<PolicyOptions>>()
2✔
99
                //             .Value.NotBeNull();
2✔
100
                //
2✔
101
                //         // See: https://www.pollydocs.org/strategies/retry.html
2✔
102
                //         pipelineBuilder.AddRetry(
2✔
103
                //             new HttpRetryStrategyOptions
2✔
104
                //             {
2✔
105
                //                 BackoffType = DelayBackoffType.Exponential,
2✔
106
                //                 MaxRetryAttempts = policyOptions.RetryPolicyOptions.Count,
2✔
107
                //                 UseJitter = true,
2✔
108
                //             }
2✔
109
                //         );
2✔
110
                //
2✔
111
                //         // See: https://www.pollydocs.org/strategies/circuit-breaker.html
2✔
112
                //         pipelineBuilder.AddCircuitBreaker(
2✔
113
                //             new HttpCircuitBreakerStrategyOptions
2✔
114
                //             {
2✔
115
                //                 BreakDuration = TimeSpan.FromSeconds(
2✔
116
                //                     policyOptions.CircuitBreakerPolicyOptions.DurationOfBreak
2✔
117
                //                 ),
2✔
118
                //                 SamplingDuration = TimeSpan.FromSeconds(
2✔
119
                //                     policyOptions.CircuitBreakerPolicyOptions.SamplingDuration
2✔
120
                //                 ),
2✔
121
                //                 FailureRatio = 0.2,
2✔
122
                //                 MinimumThroughput = policyOptions
2✔
123
                //                     .CircuitBreakerPolicyOptions
2✔
124
                //                     .ExceptionsAllowedBeforeBreaking,
2✔
125
                //                 ShouldHandle = static args =>
2✔
126
                //                 {
2✔
127
                //                     return ValueTask.FromResult(
2✔
128
                //                         args
2✔
129
                //                             is {
2✔
130
                //                                 Outcome.Result.StatusCode: HttpStatusCode.RequestTimeout
2✔
131
                //                                     or HttpStatusCode.TooManyRequests
2✔
132
                //                             }
2✔
133
                //                     );
2✔
134
                //                 },
2✔
135
                //             }
2✔
136
                //         );
2✔
137
                //
2✔
138
                //         // See: https://www.pollydocs.org/strategies/timeout.html
2✔
139
                //         pipelineBuilder.AddTimeout(
2✔
140
                //             TimeSpan.FromSeconds(policyOptions.TimeoutPolicyOptions.TimeoutInSeconds)
2✔
141
                //         );
2✔
142
                //     }
2✔
143
                // );
2✔
144
            });
2✔
145
        }
146

147
        // https://learn.microsoft.com/en-us/dotnet/core/resilience/?tabs=dotnet-cli#build-a-resilience-pipeline
148
        builder.Services.AddResiliencePipeline(
2✔
149
            "shared-resilience-pipeline",
2✔
150
            static (pipelineBuilder, ctx) =>
2✔
151
            {
2✔
152
                var policyOptions = ctx.ServiceProvider.GetRequiredService<IOptions<PolicyOptions>>().Value.NotBeNull();
×
153

2✔
154
                // See: https://www.pollydocs.org/strategies/retry.html
2✔
155
                pipelineBuilder.AddRetry(
×
156
                    new RetryStrategyOptions
×
157
                    {
×
158
                        BackoffType = DelayBackoffType.Exponential,
×
159
                        MaxRetryAttempts = policyOptions.RetryPolicyOptions.Count,
×
160
                        UseJitter = true,
×
161
                    }
×
162
                );
×
163

2✔
164
                // See: https://www.pollydocs.org/strategies/circuit-breaker.html
2✔
165
                pipelineBuilder.AddCircuitBreaker(
×
166
                    new CircuitBreakerStrategyOptions
×
167
                    {
×
168
                        BreakDuration = TimeSpan.FromSeconds(policyOptions.CircuitBreakerPolicyOptions.DurationOfBreak),
×
169
                        SamplingDuration = TimeSpan.FromSeconds(
×
170
                            policyOptions.CircuitBreakerPolicyOptions.SamplingDuration
×
171
                        ),
×
172
                        FailureRatio = 0.2,
×
173
                        MinimumThroughput = policyOptions.CircuitBreakerPolicyOptions.ExceptionsAllowedBeforeBreaking,
×
174
                    }
×
175
                );
×
176

2✔
177
                // See: https://www.pollydocs.org/strategies/timeout.html
2✔
178
                pipelineBuilder.AddTimeout(TimeSpan.FromSeconds(policyOptions.TimeoutPolicyOptions.TimeoutInSeconds));
×
179
            }
×
180
        );
2✔
181

182
        return builder;
2✔
183
    }
184

185
    public static IHttpClientBuilder ConfigureStandardResilienceHandler(this IHttpClientBuilder httpClientBuilder)
186
    {
187
        httpClientBuilder
×
188
            .AddStandardResilienceHandler()
×
189
            .Configure(
×
190
                (cfg, sp) =>
×
191
                {
×
192
                    // resiliency using `Microsoft.Extensions.Http.Resilience`
×
193
                    // https://learn.microsoft.com/en-us/dotnet/core/resilience/
×
194
                    // https://learn.microsoft.com/en-us/dotnet/core/resilience/http-resilience
×
195
                    var policyOptions = sp.GetRequiredService<IOptions<PolicyOptions>>().Value.NotBeNull();
×
196

×
197
                    cfg.AttemptTimeout = new HttpTimeoutStrategyOptions
×
198
                    {
×
199
                        Timeout = TimeSpan.FromSeconds(policyOptions.TimeoutPolicyOptions.TimeoutInSeconds),
×
200
                    };
×
201

×
202
                    cfg.TotalRequestTimeout = new HttpTimeoutStrategyOptions
×
203
                    {
×
204
                        Timeout = TimeSpan.FromSeconds(policyOptions.TimeoutPolicyOptions.TimeoutInSeconds),
×
205
                    };
×
206

×
207
                    cfg.Retry = new HttpRetryStrategyOptions
×
208
                    {
×
209
                        BackoffType = DelayBackoffType.Exponential,
×
210
                        MaxRetryAttempts = policyOptions.RetryPolicyOptions.Count,
×
211
                        UseJitter = true,
×
212
                    };
×
213

×
214
                    cfg.CircuitBreaker = new HttpCircuitBreakerStrategyOptions
×
215
                    {
×
216
                        SamplingDuration = TimeSpan.FromSeconds(
×
217
                            policyOptions.CircuitBreakerPolicyOptions.SamplingDuration
×
218
                        ), // Ensure this is >= 2 * TimeoutInSeconds
×
219
                        BreakDuration = TimeSpan.FromSeconds(policyOptions.CircuitBreakerPolicyOptions.DurationOfBreak),
×
220
                        MinimumThroughput = policyOptions.CircuitBreakerPolicyOptions.ExceptionsAllowedBeforeBreaking,
×
221
                        ShouldHandle = static args =>
×
222
                        {
×
223
                            return ValueTask.FromResult(
×
224
                                args
×
225
                                    is {
×
226
                                        Outcome.Result.StatusCode: HttpStatusCode.RequestTimeout
×
227
                                            or HttpStatusCode.TooManyRequests
×
228
                                    }
×
229
                            );
×
230
                        },
×
231
                    };
×
232
                }
×
233
            );
×
234

235
        return httpClientBuilder;
×
236
    }
237

238
    public static IHttpClientBuilder ConfigureDefaultResilienceHandler(this IHttpClientBuilder httpClientBuilder)
239
    {
240
        // https://learn.microsoft.com/en-us/dotnet/core/resilience/http-resilience?tabs=dotnet-cli#add-custom-resilience-handlers
241
        httpClientBuilder.AddResilienceHandler(
×
242
            "CustomPipeline",
×
243
            (pipelineBuilder, ctx) =>
×
244
            {
×
245
                var policyOptions = ctx.ServiceProvider.GetRequiredService<IOptions<PolicyOptions>>().Value.NotBeNull();
×
246

×
247
                // See: https://www.pollydocs.org/strategies/retry.html
×
248
                pipelineBuilder.AddRetry(
×
249
                    new HttpRetryStrategyOptions
×
250
                    {
×
251
                        BackoffType = DelayBackoffType.Exponential,
×
252
                        MaxRetryAttempts = policyOptions.RetryPolicyOptions.Count,
×
253
                        UseJitter = true,
×
254
                    }
×
255
                );
×
256

×
257
                // See: https://www.pollydocs.org/strategies/circuit-breaker.html
×
258
                pipelineBuilder.AddCircuitBreaker(
×
259
                    new HttpCircuitBreakerStrategyOptions
×
260
                    {
×
261
                        BreakDuration = TimeSpan.FromSeconds(policyOptions.CircuitBreakerPolicyOptions.DurationOfBreak),
×
262
                        SamplingDuration = TimeSpan.FromSeconds(
×
263
                            policyOptions.CircuitBreakerPolicyOptions.SamplingDuration
×
264
                        ),
×
265
                        FailureRatio = 0.2,
×
266
                        MinimumThroughput = policyOptions.CircuitBreakerPolicyOptions.ExceptionsAllowedBeforeBreaking,
×
267
                        ShouldHandle = static args =>
×
268
                        {
×
269
                            return ValueTask.FromResult(
×
270
                                args
×
271
                                    is {
×
272
                                        Outcome.Result.StatusCode: HttpStatusCode.RequestTimeout
×
273
                                            or HttpStatusCode.TooManyRequests
×
274
                                    }
×
275
                            );
×
276
                        },
×
277
                    }
×
278
                );
×
279

×
280
                // See: https://www.pollydocs.org/strategies/timeout.html
×
281
                pipelineBuilder.AddTimeout(TimeSpan.FromSeconds(policyOptions.TimeoutPolicyOptions.TimeoutInSeconds));
×
282
            }
×
283
        );
×
284

285
        return httpClientBuilder;
×
286
    }
287

288
    private static void AddResiliencyCore(IHostApplicationBuilder builder)
289
    {
290
        builder.Services.AddValidationOptions<PolicyOptions>();
2✔
291
        var policyOptions = builder.Configuration.BindOptions<PolicyOptions>();
2✔
292

293
        // `AsyncPolicyWrap<HttpResponseMessage>` can be injected in clients and can be reused.
294
        builder.Services.AddSingleton<AsyncPolicyWrap<HttpResponseMessage>>(sp =>
2✔
295
        {
2✔
296
            // Retry policy: Do not retry on 404 Not Found
2✔
297
            var retryPolicy = Policy
×
298
                .Handle<HttpRequestException>(ex => ex.StatusCode != HttpStatusCode.NotFound) // Exclude 404
×
299
                .OrResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode && r.StatusCode != HttpStatusCode.NotFound) // Exclude 404
×
300
                .RetryAsync(policyOptions.RetryPolicyOptions.Count);
×
301

2✔
302
            // HttpClient itself will still enforce its own timeout, which is 100 seconds by default. To fix this issue, you need to set the HttpClient.Timeout property to match or exceed the timeout configured in Polly's policy.
2✔
303
            var timeoutPolicy = Policy.TimeoutAsync(
×
304
                policyOptions.TimeoutPolicyOptions.TimeoutInSeconds,
×
305
                TimeoutStrategy.Pessimistic
×
306
            );
×
307

2✔
308
            // at any given time there will 3 parallel requests execution for specific service call and another 6 requests for other services can be in the queue. So that if the response from customer service is delayed or blocked then we don’t use too many resources
2✔
309
            var bulkheadPolicy = Policy.BulkheadAsync<HttpResponseMessage>(3, 6);
×
310

2✔
311
            // https://github.com/App-vNext/Polly#handing-return-values-and-policytresult
2✔
312
            var circuitBreakerPolicy = Policy
×
313
                .Handle<HttpRequestException>()
×
314
                .OrResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
×
315
                .CircuitBreakerAsync(
×
316
                    policyOptions.RetryPolicyOptions.Count + 1,
×
317
                    TimeSpan.FromSeconds(policyOptions.CircuitBreakerPolicyOptions.DurationOfBreak)
×
318
                );
×
319

2✔
320
            var combinedPolicy = Policy.WrapAsync(retryPolicy, circuitBreakerPolicy, bulkheadPolicy);
×
321

2✔
322
            var finalPolicy = combinedPolicy.WrapAsync(timeoutPolicy);
×
323

2✔
324
            return finalPolicy;
×
325
        });
2✔
326

327
        builder.Services.AddSingleton<AsyncPolicyWrap>(sp =>
2✔
328
        {
2✔
329
            // Retry policy
2✔
330
            var retryPolicy = Policy
2✔
331
                .Handle<HttpRequestException>(ex => ex.StatusCode != HttpStatusCode.NotFound) // Exclude 404
×
332
                .RetryAsync(policyOptions.RetryPolicyOptions.Count);
2✔
333

2✔
334
            // Timeout policy
2✔
335
            var timeoutPolicy = Policy.TimeoutAsync(
2✔
336
                policyOptions.TimeoutPolicyOptions.TimeoutInSeconds,
2✔
337
                TimeoutStrategy.Pessimistic
2✔
338
            );
2✔
339

2✔
340
            // Bulkhead policy
2✔
341
            var bulkheadPolicy = Policy.BulkheadAsync(3, 6);
2✔
342

2✔
343
            // Circuit breaker policy
2✔
344
            var circuitBreakerPolicy = Policy
2✔
345
                .Handle<HttpRequestException>()
2✔
346
                .CircuitBreakerAsync(
2✔
347
                    policyOptions.CircuitBreakerPolicyOptions.ExceptionsAllowedBeforeBreaking,
2✔
348
                    TimeSpan.FromSeconds(policyOptions.CircuitBreakerPolicyOptions.DurationOfBreak)
2✔
349
                );
2✔
350

2✔
351
            // Combine policies
2✔
352
            // Combine policies
2✔
353
            var combinedPolicy = Policy.WrapAsync(retryPolicy, circuitBreakerPolicy, bulkheadPolicy);
2✔
354

2✔
355
            var finalPolicy = combinedPolicy.WrapAsync(timeoutPolicy);
2✔
356

2✔
357
            return finalPolicy;
2✔
358
        });
2✔
359
    }
2✔
360
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc