Skip to content

Commit

Permalink
Migrate the first half of the async demos (#46)
Browse files Browse the repository at this point in the history
* Move statistics fields from SyncDemo to DemoBase

* Migrate async demo 0

* Migrate async demo 1

* Migrate async demo 2

* Migrate async demo 3

* Migrate async demo 4

* Migrate async demo 5

* Polish comments
  • Loading branch information
peter-csala authored Oct 2, 2023
1 parent 137bab1 commit fe72f44
Show file tree
Hide file tree
Showing 12 changed files with 362 additions and 428 deletions.
13 changes: 5 additions & 8 deletions PollyDemos/Async/AsyncDemo.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using PollyDemos.OutputHelpers;
using PollyDemos.OutputHelpers;

namespace PollyDemos.Async
{
public abstract class AsyncDemo : DemoBase
{
public abstract Task ExecuteAsync(CancellationToken cancellationToken, IProgress<DemoProgress> progress);

public async Task<string> IssueRequestAndProcessResponseAsync(HttpClient client, CancellationToken cancellationToken)
=> await client.GetStringAsync($"{Configuration.WEB_API_ROOT}/api/values/{totalRequests}", cancellationToken);
}
}
}
78 changes: 0 additions & 78 deletions PollyDemos/Async/AsyncDemo00_NoPolicy.cs

This file was deleted.

64 changes: 64 additions & 0 deletions PollyDemos/Async/AsyncDemo00_NoStrategy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using PollyDemos.OutputHelpers;

namespace PollyDemos.Async
{
/// <summary>
/// Uses no strategy. Demonstrates behavior of 'faulting server' we are testing against.
/// Loops through a series of HTTP requests, keeping track of each requested
/// item and reporting server failures when encountering exceptions.
/// </summary>
public class AsyncDemo00_NoStrategy : AsyncDemo
{
public override string Description =>
"This demo demonstrates how our faulting server behaves, with no Polly strategy in use.";

public override async Task ExecuteAsync(CancellationToken cancellationToken, IProgress<DemoProgress> progress)
{
ArgumentNullException.ThrowIfNull(progress);

// Let's call a web API service to make repeated requests to a server.
// The service is configured to fail after 3 requests in 5 seconds.

eventualSuccesses = 0;
eventualFailures = 0;
totalRequests = 0;

PrintHeader(progress, nameof(AsyncDemo00_NoStrategy));

var client = new HttpClient();
var internalCancel = false;

// Do the following until a key is pressed
while (!(internalCancel || cancellationToken.IsCancellationRequested))
{
totalRequests++;

try
{
// Make a request and get a response
var responseBody = await IssueRequestAndProcessResponseAsync(client, cancellationToken);

// Display the response message on the console
progress.Report(ProgressWithMessage($"Response : {responseBody}", Color.Green));
eventualSuccesses++;
}
catch (Exception e)
{
progress.Report(ProgressWithMessage($"Request {totalRequests} eventually failed with: {e.Message}", Color.Red));
eventualFailures++;
}

await Task.Delay(TimeSpan.FromSeconds(0.5), cancellationToken);
internalCancel = TerminateDemosByKeyPress && Console.KeyAvailable;
}
}

public override Statistic[] LatestStatistics => new Statistic[]
{
new("Total requests made", totalRequests),
new("Requests which eventually succeeded", eventualSuccesses, Color.Green),
new("Retries made to help achieve success", retries, Color.Yellow),
new("Requests which eventually failed", eventualFailures, Color.Red),
};
}
}
118 changes: 56 additions & 62 deletions PollyDemos/Async/AsyncDemo01_RetryNTimes.cs
Original file line number Diff line number Diff line change
@@ -1,97 +1,91 @@
using Polly;
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using PollyDemos.OutputHelpers;
using PollyDemos.OutputHelpers;

namespace PollyDemos.Async
{
/// <summary>
/// Demonstrates the Retry policy coming into action.
/// Loops through a series of Http requests, keeping track of each requested
/// Demonstrates the Retry strategy coming into action.
/// Loops through a series of HTTP requests, keeping track of each requested
/// item and reporting server failures when encountering exceptions.
///
/// Observations: There's no wait among these retries. Can be appropriate sometimes.
///
/// Observations: There's no wait among these retries. It can be appropriate sometimes.
/// In this case, no wait hasn't given underlying system time to recover, so calls still fail despite retries.
/// </summary>
public class AsyncDemo01_RetryNTimes : AsyncDemo
{
private int totalRequests;
private int eventualSuccesses;
private int retries;
private int eventualFailures;

public override string Description =>
"This demo demonstrates a first Retry. It retries three times, immediately.";

public override async Task ExecuteAsync(CancellationToken cancellationToken, IProgress<DemoProgress> progress)
{
if (progress == null) throw new ArgumentNullException(nameof(progress));
ArgumentNullException.ThrowIfNull(progress);

// Let's call a web API service to make repeated requests to a server.
// The service is configured to fail after 3 requests in 5 seconds.

eventualSuccesses = 0;
retries = 0;
eventualFailures = 0;
totalRequests = 0;

progress.Report(ProgressWithMessage(nameof(AsyncDemo01_RetryNTimes)));
progress.Report(ProgressWithMessage("======"));
progress.Report(ProgressWithMessage(string.Empty));

// Define our policy:
var policy = Policy.Handle<Exception>().RetryAsync(3, (exception, attempt) =>
{
// This is your new exception handler!
// Tell the user what they've won!
progress.Report(ProgressWithMessage("Policy logging: " + exception.Message, Color.Yellow));
retries++;
});
PrintHeader(progress, nameof(AsyncDemo01_RetryNTimes));

using (var client = new HttpClient())
// Define our strategy:
var strategy = new ResiliencePipelineBuilder().AddRetry(new()
{
totalRequests = 0;
var internalCancel = false;
// Do the following until a key is pressed
while (!internalCancel && !cancellationToken.IsCancellationRequested)
ShouldHandle = new PredicateBuilder().Handle<Exception>(),
MaxRetryAttempts = 3, // Retry up to 3 times
OnRetry = args =>
{
totalRequests++;
// Due to how we have defined ShouldHandle, this delegate is called only if an exception occurred.
// Note the ! sign (null-forgiving operator) at the end of the command.
var exception = args.Outcome.Exception!; // The Exception property is nullable
try
{
// Retry the following call according to the policy - 3 times.
await policy.ExecuteAsync(async () =>
{
// This code is executed within the Policy
// Tell the user what happened
progress.Report(ProgressWithMessage($"Strategy logging: {exception.Message}", Color.Yellow));
retries++;
return default;
}
}).Build();

// Make a request and get a response
var msg = await client.GetStringAsync(
Configuration.WEB_API_ROOT + "/api/values/" + totalRequests);
var client = new HttpClient();
var internalCancel = false;

// Display the response message on the console
progress.Report(ProgressWithMessage("Response : " + msg, Color.Green));
eventualSuccesses++;
});
}
catch (Exception e)
// Do the following until a key is pressed
while (!(internalCancel || cancellationToken.IsCancellationRequested))
{
totalRequests++;

try
{
// Retry the following call according to the strategy.
// The cancellationToken passed in to ExecuteAsync() enables the strategy to cancel retries when the token is signalled.
await strategy.ExecuteAsync(async token =>
{
progress.Report(ProgressWithMessage(
"Request " + totalRequests + " eventually failed with: " + e.Message, Color.Red));
eventualFailures++;
}
// This code is executed within the strategy
// Wait half second
await Task.Delay(TimeSpan.FromSeconds(0.5), cancellationToken);
var responseBody = await IssueRequestAndProcessResponseAsync(client, token);
progress.Report(ProgressWithMessage($"Response : {responseBody}", Color.Green));
eventualSuccesses++;
internalCancel = TerminateDemosByKeyPress && Console.KeyAvailable;
}, cancellationToken);
}
catch (Exception e)
{
progress.Report(ProgressWithMessage($"Request {totalRequests} eventually failed with: {e.Message}", Color.Red));
eventualFailures++;
}

await Task.Delay(TimeSpan.FromSeconds(0.5), cancellationToken);
internalCancel = TerminateDemosByKeyPress && Console.KeyAvailable;
}
}

public override Statistic[] LatestStatistics => new[]
public override Statistic[] LatestStatistics => new Statistic[]
{
new Statistic("Total requests made", totalRequests),
new Statistic("Requests which eventually succeeded", eventualSuccesses, Color.Green),
new Statistic("Retries made to help achieve success", retries, Color.Yellow),
new Statistic("Requests which eventually failed", eventualFailures, Color.Red),
new("Total requests made", totalRequests),
new("Requests which eventually succeeded", eventualSuccesses, Color.Green),
new("Retries made to help achieve success", retries, Color.Yellow),
new("Requests which eventually failed", eventualFailures, Color.Red),
};
}
}
}
Loading

0 comments on commit fe72f44

Please sign in to comment.