From 7ec8c27a2198e1ce4671bb387121ed6ed98c4885 Mon Sep 17 00:00:00 2001 From: Bryan Hogan Date: Mon, 30 Sep 2019 22:31:59 -0400 Subject: [PATCH] working version --- .gitignore | 293 +++++++++++++++++++++++++++ Readme.md | 22 ++ advancedCircuitBreaker.md | 11 + basicCircuitBreaker.md | 9 + fallingBack.md | 11 + fallingBackAndReturningADefault.md | 9 + lettingItFail.md | 10 + retryIfException.md | 13 ++ retryIfIncorrectStatus.md | 11 + retryIfIncorrectStatusOrException.md | 11 + src/ErrorProneCode.cs | 156 ++++++++++++++ src/PollyDemo.csproj | 14 ++ src/Program.cs | 291 ++++++++++++++++++++++++++ src/Status.cs | 10 + waitAndRetry.md | 9 + 15 files changed, 880 insertions(+) create mode 100644 .gitignore create mode 100644 Readme.md create mode 100644 advancedCircuitBreaker.md create mode 100644 basicCircuitBreaker.md create mode 100644 fallingBack.md create mode 100644 fallingBackAndReturningADefault.md create mode 100644 lettingItFail.md create mode 100644 retryIfException.md create mode 100644 retryIfIncorrectStatus.md create mode 100644 retryIfIncorrectStatusOrException.md create mode 100644 src/ErrorProneCode.cs create mode 100644 src/PollyDemo.csproj create mode 100644 src/Program.cs create mode 100644 src/Status.cs create mode 100644 waitAndRetry.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ad75bd --- /dev/null +++ b/.gitignore @@ -0,0 +1,293 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates +*.zip +*.pptx +*.ppt +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs +.vscode/ +.git/ +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Typescript v1 declaration files +typings/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs +*.binlog diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..019e54b --- /dev/null +++ b/Readme.md @@ -0,0 +1,22 @@ +# Try .NET Enabled Samples +![dotnet try Enabled](https://img.shields.io/badge/Try_.NET-Enabled-501078.svg) + +*Please read the [Try .NET quick setup guide](Setup.md) before starting the tutorial below.* + +

+ +

+

Try .NET Samples of Polly, the .NET Resilience Framework

+ +

Polly is a resilience framework for .NET, with it your applications can easily tolerate transient faults and longer outages in remote systems or infrastructure. With just a few lines of code your application will be far more robust and reliable.

+ +### Table of Content +- [Before you add Polly](lettingItFail.md) +- [Retrying when you get an exception](retryIfException.md) +- [Retrying when you get a bad result](retryIfIncorrectStatus.md) +- [Combining Result and Exception Based Retries](retryIfIncorrectStatusOrException.md) +- [Waiting Before Retrying](waitAndRetry.md) +- [Fallbacks 1](fallingBack.md) +- [Fallbacks 2](fallingBackAndReturningADefault.md) +- [Basic Circuit Breaker](basicCircuitBreaker.md) +- [Advanced Circuit Breaker](advancedCircuitBreaker.md) \ No newline at end of file diff --git a/advancedCircuitBreaker.md b/advancedCircuitBreaker.md new file mode 100644 index 0000000..c00f0d0 --- /dev/null +++ b/advancedCircuitBreaker.md @@ -0,0 +1,11 @@ +# Circuit Breaker 2 + +### Advanced circuit breaker +The advanced circuit breaker breaks the circuit if a if a threshold of failures is reached within a specified time period and with a minimum number of requests reached in that time period. + +In this example the circuit breaks when there are 50% failures, in a 3 second sampling window, with a minimum throughput of 6 request in the 3 second window; if the circuit breaks it will be open for 5 seconds. When the circuit breaker is open no traffic will cross it, instead a `BrokenCircuitException` will be thrown. + +``` cs --region advancedCircuitBreaker --source-file .\src\Program.cs --project .\src\PollyDemo.csproj +``` + +#### Next: [Caching »](../caching.md) Previous: [Basic Circuit Breaker «](../basicCircuitBreaker.md) \ No newline at end of file diff --git a/basicCircuitBreaker.md b/basicCircuitBreaker.md new file mode 100644 index 0000000..edd8e2d --- /dev/null +++ b/basicCircuitBreaker.md @@ -0,0 +1,9 @@ +# Circuit Breaker 1 + +### Basic circuit breaker +The basic circuit breaker breaks the circuit if a set number of consecutive of errors occur. When the circuit breaker is open no traffic will cross it, instead a `BrokenCircuitException` will be thrown. + +``` cs --region basicCircuitBreaker --source-file .\src\Program.cs --project .\src\PollyDemo.csproj +``` + +#### Next: [Advanced Circuit Breaker »](../advancedCircuitBreaker.md) Previous: [Fallbacks 2 «](../fallingBackAndReturningADefault.md) \ No newline at end of file diff --git a/fallingBack.md b/fallingBack.md new file mode 100644 index 0000000..a0b8967 --- /dev/null +++ b/fallingBack.md @@ -0,0 +1,11 @@ +# Polly Fallbacks 1 + +### When all else fails...do something. +There are times when no number of retires will solve a problem, the underlying service just won't respond. In this case the Fallback policy is you last resort. It can page an admin, scale a service or return a default. + +This example "pages" an admin but you can do anything you want. + +``` cs --region fallingBack --source-file .\src\Program.cs --project .\src\PollyDemo.csproj +``` + +#### Next: [Fallbacks 2 »](../fallingBackAndReturningADefault.md) Previous: [Waiting Before Retrying «](../waitAndRetry.md) \ No newline at end of file diff --git a/fallingBackAndReturningADefault.md b/fallingBackAndReturningADefault.md new file mode 100644 index 0000000..4b244f1 --- /dev/null +++ b/fallingBackAndReturningADefault.md @@ -0,0 +1,9 @@ +# Polly Fallbacks 2 + +### When all else fails...return a default +Sometimes it makes sense to return a default or safe value from a fallback. In this example a quantity of zero is returned if the request fails. + +``` cs --region fallingBackAndReturningADefault --source-file .\src\Program.cs --project .\src\PollyDemo.csproj +``` + +#### Next: [Basic Circuit Breaker »](../basicCircuitBreaker.md) Previous: [Fallbacks 1 «](../fallingBack.md) \ No newline at end of file diff --git a/lettingItFail.md b/lettingItFail.md new file mode 100644 index 0000000..eb8145b --- /dev/null +++ b/lettingItFail.md @@ -0,0 +1,10 @@ +# Polly Retries Part 1 + +### Letting a request fail - not using Polly yet +Before adding Polly let's see how an application without Polly might behave. +Here we are trying to write to a database, but the request will fail and an exception will be thrown. + +``` cs --region lettingItFail --source-file .\src\Program.cs --project .\src\PollyDemo.csproj +``` + +#### Next: [Retrying When an Exception Occurs »](../retryIfException.md) Previous: [Home «](../README.md) \ No newline at end of file diff --git a/retryIfException.md b/retryIfException.md new file mode 100644 index 0000000..1e108ae --- /dev/null +++ b/retryIfException.md @@ -0,0 +1,13 @@ +# Polly Retries Part 2 + +### Retrying When an Exception Occurs +The Polly NuGet package has been added and we are going to use the Retry Policy when querying database. +The policy states that if an exception occurs, it will retry up to three times. + +Note how you execute the unreliable code inside the policy. `retryPolicy.Execute(() => errorProneCode.QueryTheDatabase());` + + +``` cs --region retryIfException --source-file .\src\Program.cs --project .\src\PollyDemo.csproj +``` + +#### Next: [Retrying Based on a Result »](./retryIfIncorrectStatus.md) Previous: [Home «](../README.md) \ No newline at end of file diff --git a/retryIfIncorrectStatus.md b/retryIfIncorrectStatus.md new file mode 100644 index 0000000..b60da19 --- /dev/null +++ b/retryIfIncorrectStatus.md @@ -0,0 +1,11 @@ +# Polly Retries Part 3 + +### Retries Based on a Result +Policies can handle exceptions `Handle`, as you saw in the previous example. Policies can also check the any result type that a method might return. In this case the policy checks `Status` returned - `HandleResult`. A policy can handle any type of result, e.g. `HandleResult`, `HandleResult`, `HandleResult`, etc. + +In this example the policy retries the request up to three times if an the result is not a success. + +``` cs --region retryIfIncorrectStatus --source-file .\src\Program.cs --project .\src\PollyDemo.csproj +``` + +#### Next: [Combining Result and Exception Based Retries »](./retryIfIncorrectStatusOrException.md) Previous: [Retrying When an Exception Occurs «](../retryIfException.md) \ No newline at end of file diff --git a/retryIfIncorrectStatusOrException.md b/retryIfIncorrectStatusOrException.md new file mode 100644 index 0000000..30a2e91 --- /dev/null +++ b/retryIfIncorrectStatusOrException.md @@ -0,0 +1,11 @@ +# Polly Retries Part 4 + +### Combining Result and Exception Based Retries +You can combine handling of exceptions and results. + +The policy retries the request up to three times if an the result is not a success or if an exception occurred. + +``` cs --region retryIfIncorrectStatusOrException --source-file .\src\Program.cs --project .\src\PollyDemo.csproj +``` + +#### Next: [Waiting Before Retrying »](./waitAndRetry.md) Previous: [Retries Based on a Result «](../retryIfIncorrectStatus.md) \ No newline at end of file diff --git a/src/ErrorProneCode.cs b/src/ErrorProneCode.cs new file mode 100644 index 0000000..77535ab --- /dev/null +++ b/src/ErrorProneCode.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; + +namespace PollyTryDemo +{ + public class ErrorProneCode + { + int _writeToSomeDbCounter = 0; + int _queryTheDatabaseCounter = 0; + int _getStatusCounter = 0; + int _callRemoteServiceCounter = 0; + + int _circuitBeakerTargetACounter = 0; + int _circuitBeakerTargetBCounter = 0; + + public Status TargetA() + { + _circuitBeakerTargetACounter ++; + if (_circuitBeakerTargetACounter >= 2) + { + return Status.Fail; + } + return Status.Success; + } + + public Status TargetB() + { + _circuitBeakerTargetBCounter ++; + if (_circuitBeakerTargetBCounter >= 2) + { + return Status.Fail; + } + return Status.Success; + } + + + public int WriteToSomeDb() + { + _writeToSomeDbCounter++; // seems to be an issue with static variables in try.net + if (_writeToSomeDbCounter <= 3) + { + throw new InsufficientMemoryException("You ran out of memory!"); + } + return 1; + } + + public void MakeRequestToADeadService(){ + throw new Exception("An error occurred, I hope you have Polly!"); + } + + + public int GetQuantityAvailable(){ + throw new Exception("An error occurred, I hope you have Polly!"); + } + + public int QueryTheDatabase() + { + _queryTheDatabaseCounter++; + if (_queryTheDatabaseCounter == 1) + { + throw new Exception("An error occurred, I hope you have Polly!"); + } + if (_queryTheDatabaseCounter == 2) + { + throw new InsufficientMemoryException("You ran out of memory!"); + } + // if (_queryTheDatabaseCounter == 3) + // { + // throw new StackOverflowException("Not the website."); + // } + if(_queryTheDatabaseCounter == 3) + { + return 0; + } + return -1; + } + + public Status GetStatus() + { + _getStatusCounter++; + switch (_getStatusCounter) + { + case 1: + return Status.ExceptionOccurred; + case 2: + return Status.Fail; + case 3: + _getStatusCounter = 0; + return Status.Success; + } + return Status.Fail; + } + + public Status CallRemoteService() + { + _callRemoteServiceCounter++; + switch (_callRemoteServiceCounter) + { + case 1: + return Status.ExceptionOccurred; + case 2: + throw new OutOfMemoryException(); + case 3: + return Status.Unknown; + case 4: + return Status.Success; + } + return Status.Fail; + } + + // public Status CallAnotherRemoteService() + // { + // _callAnotherRemoteServiceCounter++; + // switch (_callAnotherRemoteServiceCounter) + // { + // case 1: + // return Status.ExceptionOccurred; + // case 2: + // return Status.Fail; + // case 3: + // return Status.Unknown; + // case 4: + // return Status.Success; + // } + // return Status.Fail; + // } + + // public IEnumerable GetListOfNumbers() + // { + // _getListOfNumbersCounter++; + // if (_getListOfNumbersCounter == 1) + // { + // return new int[] { 1, 2, 3, 4, 5 }; + // } + // if (_getListOfNumbersCounter == 2) + // { + // return new int[] { 1, 2 }; + // } + // if (_getListOfNumbersCounter == 3) + // { + // return new int[] { 1, 2, 3 }; + // } + // return new int[] { 1, 2, 3, 4, 5, 6 }; + // } + + + // public void DoSomethingThatMightThrowException() + // { + // _exceptionCounter++; + // if (_exceptionCounter <= 3) + // { + // throw new Exception("Bad things happened"); + // } + // } + } +} diff --git a/src/PollyDemo.csproj b/src/PollyDemo.csproj new file mode 100644 index 0000000..fcdbe38 --- /dev/null +++ b/src/PollyDemo.csproj @@ -0,0 +1,14 @@ + + + + Exe + netcoreapp3.0 + + + + + + + + + diff --git a/src/Program.cs b/src/Program.cs new file mode 100644 index 0000000..451311e --- /dev/null +++ b/src/Program.cs @@ -0,0 +1,291 @@ +using Polly; +using Polly.CircuitBreaker; +using Polly.Fallback; +using Polly.Retry; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading; + +namespace PollyTryDemo +{ + class Program + { + + static void Main(string region = null, + string session = null, + string package = null, + string project = null, + string[] args = null) + { + //AdvancedCircuitBreaker(); + switch(region) + { + case "lettingItFail": + LettingItFail(); + break; + case "retryIfException": + RetryIfException(); + break; + case "retryIfIncorrectStatus": + RetryIfIncorrectStatus(); + break; + case "retryIfIncorrectStatusOrException": + RetryIfIncorrectStatusOrException(); + break; + case "waitAndRetry": + WaitAndRetry(); + break; + case "fallingBack": + FallingBack(); + break; + case "fallingBackAndReturningADefault": + FallingBackAndReturningADefault(); + break; + case "basicCircuitBreaker": + BasicCircuitBreaker(); + break; + case "advancedCircuitBreaker": + AdvancedCircuitBreaker(); + break; + } + } + + public static void BasicCircuitBreaker() + { + ErrorProneCode errorProneCode = new ErrorProneCode(); + + #region basicCircuitBreaker + Console.WriteLine("A BrokenCircuitException is expected in this demo."); + + var circuitBreakerPolicy = Policy + .HandleResult(r => r == Status.Fail) + .CircuitBreaker(2, TimeSpan.FromSeconds(3), OnBreak, OnReset, OnHalfOpen); + + for (int loop = 1; loop <= 3; loop++) + { + Status statusAResult = circuitBreakerPolicy.Execute(() => errorProneCode.TargetA()); + Console.WriteLine($"Target A (call {loop}) status: {statusAResult}"); + Status statusBResult = circuitBreakerPolicy.Execute(() => errorProneCode.TargetB()); + Console.WriteLine($"Target B (call {loop}) status: {statusBResult}"); + } + + #endregion + } + + public static void AdvancedCircuitBreaker() + { + ErrorProneCode errorProneCode = new ErrorProneCode(); + + #region advancedCircuitBreaker + Console.WriteLine("A BrokenCircuitException is expected in this demo."); + + var advancedCircuitBreakerPolicy = Policy + .HandleResult(r => r == Status.Fail) + .AdvancedCircuitBreaker(.5, TimeSpan.FromSeconds(3), 6, TimeSpan.FromSeconds(5), OnBreak, OnReset, OnHalfOpen ); + + for (int loop = 1; loop <= 4; loop++) + { + Status statusAResult = advancedCircuitBreakerPolicy.Execute(() => errorProneCode.TargetA()); + Console.WriteLine($"Target A (call {loop}) status: {statusAResult}"); + Status statusBResult = advancedCircuitBreakerPolicy.Execute(() => errorProneCode.TargetB()); + Console.WriteLine($"Target B (call {loop}) status: {statusBResult}"); + Thread.Sleep(1000); + } + + #endregion + } + + + private static void OnHalfOpen() + { + Console.WriteLine("Circuit test, one request will be allowed."); + } + + private static void OnReset() + { + Console.WriteLine("Circuit closed, request allowed."); + } + + private static void OnBreak(DelegateResult status, TimeSpan timeSpan) + { + Console.WriteLine($"Circuit open, too many failures, requests blocked."); + } + + public static void FallingBack() + { + ErrorProneCode errorProneCode = new ErrorProneCode(); + #region fallingBack + + FallbackPolicy fallbackPolicy = Policy.Handle() + .Fallback(() => PageAnAdmin() ); + + fallbackPolicy.Execute(() => errorProneCode.MakeRequestToADeadService()); + + #endregion + } + + private static void PageAnAdmin(){ + Console.WriteLine($"An exception occurred and the fallback policy kicked in, let me page someone for you..."); + } + public static void FallingBackAndReturningADefault() + { + ErrorProneCode errorProneCode = new ErrorProneCode(); + #region fallingBackAndReturningADefault + + FallbackPolicy fallbackPolicy = Policy + .Handle() + .Fallback(0); + + int quantity = fallbackPolicy.Execute(() => errorProneCode.GetQuantityAvailable()); + Console.WriteLine($"{quantity} items available"); + #endregion + } + public static void LettingItFail() + { + ErrorProneCode errorProneCode = new ErrorProneCode(); + #region lettingItFail + + int rowsWritten = errorProneCode.QueryTheDatabase(); + Console.WriteLine($"Received response of {rowsWritten}"); + + #endregion + } + public static void RetryIfException() + { + ErrorProneCode errorProneCode = new ErrorProneCode(); + #region retryIfException + + RetryPolicy retryPolicy = Policy.Handle() + .Retry(3, (exception, retryCount) => + { + Console.WriteLine($"{exception.GetType()} thrown, retrying {retryCount}"); + }); + + int result = retryPolicy.Execute(() => errorProneCode.QueryTheDatabase()); + + Console.WriteLine($"Received response of {result}"); + #endregion + } + public static void RetryIfIncorrectStatus() + { + ErrorProneCode errorProneCode = new ErrorProneCode(); + #region retryIfIncorrectStatus + + RetryPolicy retryPolicy = Policy.HandleResult(s => s!= Status.Success) + .Retry(3, (response, retryCount) => + { + Console.WriteLine($"Received a response of {response.Result}, retrying {retryCount}"); + }); + + Status result = retryPolicy.Execute(() => errorProneCode.GetStatus()); + + Console.WriteLine($"Received response of {result}"); + #endregion + } + + public static void RetryIfIncorrectStatusOrException() + { + ErrorProneCode errorProneCode = new ErrorProneCode(); + #region retryIfIncorrectStatusOrException + + RetryPolicy retryPolicy = Policy.HandleResult(s => s!= Status.Success) + .Or() + .Retry(3, (responseOrException, retryCount, ctx) => + { + Console.WriteLine($"Request failed, retrying {retryCount}"); + }); + + Status result = retryPolicy.Execute(() => errorProneCode.CallRemoteService()); + + Console.WriteLine($"Received response of {result}"); + #endregion + } + + public static void WaitAndRetry() + { + ErrorProneCode errorProneCode = new ErrorProneCode(); + #region waitAndRetry + + RetryPolicy retryPolicy = Policy.HandleResult(s => s!= Status.Success) + .WaitAndRetry(3, + sleepDurationProvider: (retryCount) => TimeSpan.FromSeconds(retryCount), + onRetry: (response, delay, retryCount, ctx) => + { + Console.WriteLine($"Received a response of {response.Result}."); + Console.WriteLine($"Slept for {delay.Seconds} second(s) before retrying."); + }); + + Status result = retryPolicy.Execute(() => errorProneCode.GetStatus()); + + Console.WriteLine($"Received response of {result}"); + #endregion + } + + // private void CheckForException() + // { + // ErrorProneCode errorProneCode = new ErrorProneCode(); + + // RetryPolicy retryIfException = Policy.Handle() + // .Retry(4, (exception, retryCount) => + // { + // Console.WriteLine($"Got a response of {exception} (expected 0), retrying {retryCount}"); + // }); + + // retryIfException.Execute(errorProneCode.DoSomethingThatMightThrowException); + // } + // private void CheckForNonZeroOrException() + // { + // ErrorProneCode errorProneCode = new ErrorProneCode(); + + // // Retry if the response is not 0 or there is a DivideByZeroException + // RetryPolicy retryPolicyNeedsAResponseOfOne = Policy.HandleResult(i => i != 0) + // .Or() + // .Retry(4, (response, retryCount) => + // { + // Console.WriteLine($"Got a response of {response.Result} (expected 0), retrying {retryCount}"); + // }); + + // int number = retryPolicyNeedsAResponseOfOne.Execute(() => errorProneCode.QueryTheDatabase()); + + // Console.WriteLine($"Got expected reponse = {number}\n\n"); + // } + + // private void CheckingSizeOfReturnedList() + // { + // ErrorProneCode errorProneCode = new ErrorProneCode(); + + // // Retry if the IEnumerable does not contain three items + // RetryPolicy> retryPolicyNeedsResponeWithTwoNumbers = Policy.HandleResult>(l => l.Count() != 3) + // .Retry(4, (response, retryCount) => + // { + // Console.WriteLine($"Got a response with {response.Result.Count()} entries (expected 3), retrying {retryCount}"); + // }); + + // var numbers = retryPolicyNeedsResponeWithTwoNumbers.Execute(() => errorProneCode.GetListOfNumbers()); + + // Console.WriteLine($"Got expected response of {numbers.Count()} entries\n\n"); + // } + + // public static void BoolResponse() + // { + // #region BoolResponse + // ErrorProneCode errorProneCode = new ErrorProneCode(); + + // // Retry if the result is not true + // RetryPolicy retryPolicyNeedsTrueResponse = Policy.HandleResult(b => b != true) + // .Retry(4, (response, retryCount) => + // { + // Console.WriteLine($"Got a response of {response.Result} entries (expected true), retrying {retryCount}"); + // }); + + // bool result = retryPolicyNeedsTrueResponse.Execute(() => errorProneCode.GetBool()); + + // Console.WriteLine($"Got expected response of {result}\n\n"); + // #endregion + // } + + + } +} diff --git a/src/Status.cs b/src/Status.cs new file mode 100644 index 0000000..ba8320a --- /dev/null +++ b/src/Status.cs @@ -0,0 +1,10 @@ +using System; +namespace PollyTryDemo +{ + public enum Status { + Success, + Fail, + Unknown, + ExceptionOccurred + }; +} \ No newline at end of file diff --git a/waitAndRetry.md b/waitAndRetry.md new file mode 100644 index 0000000..997621c --- /dev/null +++ b/waitAndRetry.md @@ -0,0 +1,9 @@ +# Polly Retries Part 5 + +### Waiting Before Retrying +There are many scenarios where adding a delay before retrying a request will be beneficial. The Wait and Retry policy adds a delay before retrying, this is especially useful for transient faults. + +``` cs --region waitAndRetry --source-file .\src\Program.cs --project .\src\PollyDemo.csproj +``` + +#### Next: [Fallbacks 1 »](../fallingBack.md) Previous: [Combining Result and Exception Based Retries «](../retryIfIncorrectStatusOrException.md) \ No newline at end of file