-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Document outcome strategy anti-patterns #1994
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 10 commits
cd82241
c297b96
aa308c1
262b136
85d88c4
c8b618e
e47812c
4c78245
1ecd966
b8fde8d
5a7c5e4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,8 @@ | ||
| using System.Net; | ||
| using System.Net; | ||
| using System.Net.Http; | ||
| using Polly.Retry; | ||
| using Polly.Simmy; | ||
| using Polly.Simmy.Fault; | ||
| using Polly.Simmy.Outcomes; | ||
|
|
||
| namespace Snippets.Docs; | ||
|
|
@@ -10,6 +11,8 @@ namespace Snippets.Docs; | |
|
|
||
| internal static partial class Chaos | ||
| { | ||
| private static readonly int RandomThreshold = Random.Shared.Next(350); | ||
|
|
||
| public static void OutcomeUsage() | ||
| { | ||
| #region chaos-outcome-usage | ||
|
|
@@ -63,7 +66,7 @@ public static void OutcomeUsage() | |
| OutcomeGenerator = static args => | ||
| { | ||
| var response = new HttpResponseMessage(HttpStatusCode.InternalServerError); | ||
| return new ValueTask<Outcome<HttpResponseMessage>?>(Outcome.FromResult(response)); | ||
| return ValueTask.FromResult<Outcome<HttpResponseMessage>?>(Outcome.FromResult(response)); | ||
| }, | ||
| InjectionRate = 0.1 | ||
| }) | ||
|
|
@@ -82,7 +85,7 @@ public static void OutcomeGenerator() | |
| OutcomeGenerator = new OutcomeGenerator<HttpResponseMessage>() | ||
| .AddResult(() => new HttpResponseMessage(HttpStatusCode.InternalServerError)) // Result generator | ||
| .AddResult(() => new HttpResponseMessage(HttpStatusCode.TooManyRequests), weight: 50) // Result generator with weight | ||
| .AddResult(context => CreateResultFromContext(context)) // Access the ResilienceContext to create result | ||
| .AddResult(context => new HttpResponseMessage(CreateResultFromContext(context))) // Access the ResilienceContext to create result | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The usage samples advocate to use one of the anti-patterns ...
.AddResult(context => new HttpResponseMessage(CreateResultFromContext(context))) // Access the ResilienceContext to create result
.AddException<HttpRequestException>(), // You can also register exceptionsPlease update these
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When I originally opened the PR I had removed those guys since I had deprecated it, however, I put them back so that we support that scenario (injecting results and exceptions in the same strategy) but under their judgment after knowing the caveats/side effects explained in the anti-pattern sections and in the footer. I can remove that entirely from the docs but it would be hidden. Honestly, while I understand it is handy it feels like we're kinda discouraging people to use the right strategy to inject faults. We could also add a comment in the samples as I did in some snippets, like this: // |
||
| .AddException<HttpRequestException>(), // You can also register exceptions | ||
| }); | ||
|
|
||
|
|
@@ -97,15 +100,15 @@ public static void OutcomeGeneratorDelegates() | |
| .AddChaosOutcome(new ChaosOutcomeStrategyOptions<HttpResponseMessage> | ||
| { | ||
| // The same behavior can be achieved with delegates | ||
| OutcomeGenerator = args => | ||
| OutcomeGenerator = static args => | ||
| { | ||
| Outcome<HttpResponseMessage>? outcome = Random.Shared.Next(350) switch | ||
| { | ||
| < 100 => Outcome.FromResult(new HttpResponseMessage(HttpStatusCode.InternalServerError)), | ||
| < 150 => Outcome.FromResult(new HttpResponseMessage(HttpStatusCode.TooManyRequests)), | ||
| < 250 => Outcome.FromResult(CreateResultFromContext(args.Context)), | ||
| < 250 => Outcome.FromResult(new HttpResponseMessage(CreateResultFromContext(args.Context))), | ||
vany0114 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| < 350 => Outcome.FromException<HttpResponseMessage>(new TimeoutException()), | ||
| _ => null | ||
| _ => Outcome.FromResult(new HttpResponseMessage(HttpStatusCode.OK)) | ||
| }; | ||
|
|
||
| return ValueTask.FromResult(outcome); | ||
|
|
@@ -115,5 +118,127 @@ public static void OutcomeGeneratorDelegates() | |
| #endregion | ||
| } | ||
|
|
||
| private static HttpResponseMessage CreateResultFromContext(ResilienceContext context) => new(HttpStatusCode.TooManyRequests); | ||
| public static void AntiPattern_GeneratorDelegateInjectFault() | ||
| { | ||
| #region chaos-outcome-anti-pattern-generator-inject-fault | ||
| var pipeline = new ResiliencePipelineBuilder<HttpResponseMessage>() | ||
| .AddChaosOutcome(new ChaosOutcomeStrategyOptions<HttpResponseMessage> | ||
| { | ||
| InjectionRate = 0.5, // same injection rate for both fault and outcome | ||
vany0114 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| OutcomeGenerator = static args => | ||
| { | ||
| Outcome<HttpResponseMessage>? outcome = Random.Shared.Next(350) switch | ||
| { | ||
| < 100 => Outcome.FromResult(new HttpResponseMessage(HttpStatusCode.InternalServerError)), | ||
| < 150 => Outcome.FromResult(new HttpResponseMessage(HttpStatusCode.TooManyRequests)), | ||
| < 250 => Outcome.FromResult(new HttpResponseMessage(CreateResultFromContext(args.Context))), | ||
| < 350 => Outcome.FromException<HttpResponseMessage>(new HttpRequestException("Chaos request exception.")), // ⚠️ Avoid this ⚠️ | ||
| _ => Outcome.FromResult(new HttpResponseMessage(HttpStatusCode.OK)) | ||
| }; | ||
|
|
||
| return ValueTask.FromResult(outcome); | ||
| }, | ||
| OnOutcomeInjected = static args => | ||
| { | ||
| // You might have to put some logic here to determine what kind of output was injected. 😕 | ||
| if (args.Outcome.Exception != null) | ||
| { | ||
| Console.WriteLine($"OnBehaviorInjected, Exception: {args.Outcome.Exception.Message}, Operation: {args.Context.OperationKey}."); | ||
| } | ||
| else | ||
| { | ||
| Console.WriteLine($"OnBehaviorInjected, Outcome: {args.Outcome.Result}, Operation: {args.Context.OperationKey}."); | ||
| } | ||
|
|
||
| return default; | ||
| } | ||
| }) | ||
| .Build(); | ||
|
|
||
| #endregion | ||
| } | ||
|
|
||
| public static void Pattern_GeneratorDelegateInjectFaultAndOutcome() | ||
| { | ||
| #region chaos-outcome-pattern-generator-inject-fault | ||
| var pipeline = new ResiliencePipelineBuilder<HttpResponseMessage>() | ||
| .AddChaosFault(new ChaosFaultStrategyOptions | ||
| { | ||
| InjectionRate = 0.1, // Different injection rate for faults | ||
| EnabledGenerator = static args => ShouldEnableFaults(args.Context), // Different settings might apply to inject faults | ||
| FaultGenerator = static args => | ||
| { | ||
| Exception? exception = RandomThreshold switch | ||
| { | ||
| >= 250 and < 350 => new HttpRequestException("Chaos request exception."), | ||
| _ => null | ||
| }; | ||
|
|
||
| return ValueTask.FromResult(exception); | ||
| }, | ||
| OnFaultInjected = static args => | ||
| { | ||
| Console.WriteLine($"OnFaultInjected, Exception: {args.Fault.Message}, Operation: {args.Context.OperationKey}."); | ||
| return default; | ||
| } | ||
| }) | ||
| .AddChaosOutcome(new ChaosOutcomeStrategyOptions<HttpResponseMessage> | ||
| { | ||
| InjectionRate = 0.5, // Different injection rate for outcomes | ||
| EnabledGenerator = static args => ShouldEnableOutcome(args.Context), // Different settings might apply to inject outcomes | ||
| OutcomeGenerator = static args => | ||
| { | ||
| HttpStatusCode statusCode = RandomThreshold switch | ||
| { | ||
| < 100 => HttpStatusCode.InternalServerError, | ||
| < 150 => HttpStatusCode.TooManyRequests, | ||
| < 250 => CreateResultFromContext(args.Context), | ||
| _ => HttpStatusCode.OK | ||
| }; | ||
|
|
||
| return ValueTask.FromResult<Outcome<HttpResponseMessage>?>(Outcome.FromResult(new HttpResponseMessage(statusCode))); | ||
| }, | ||
| OnOutcomeInjected = static args => | ||
| { | ||
| Console.WriteLine($"OnBehaviorInjected, Outcome: {args.Outcome.Result}, Operation: {args.Context.OperationKey}."); | ||
| return default; | ||
| } | ||
| }) | ||
| .Build(); | ||
| #endregion | ||
| } | ||
|
|
||
| public static void AntiPattern_OnlyInjectFault() | ||
| { | ||
| #region chaos-outcome-anti-pattern-only-inject-fault | ||
|
|
||
| new ResiliencePipelineBuilder<HttpResponseMessage>() | ||
| .AddChaosOutcome(new ChaosOutcomeStrategyOptions<HttpResponseMessage> | ||
| { | ||
| OutcomeGenerator = new OutcomeGenerator<HttpResponseMessage>() | ||
| .AddException<HttpRequestException>(), // ⚠️ Avoid this ⚠️ | ||
| }); | ||
|
|
||
| #endregion | ||
| } | ||
|
|
||
| public static void Pattern_OnlyInjectFault() | ||
| { | ||
| #region chaos-outcome-pattern-only-inject-fault | ||
|
|
||
| new ResiliencePipelineBuilder<HttpResponseMessage>() | ||
| .AddChaosFault(new ChaosFaultStrategyOptions | ||
| { | ||
| FaultGenerator = new FaultGenerator() | ||
| .AddException<HttpRequestException>(), | ||
| }); | ||
|
|
||
| #endregion | ||
| } | ||
|
|
||
| private static ValueTask<bool> ShouldEnableFaults(ResilienceContext context) => ValueTask.FromResult(true); | ||
|
|
||
| private static ValueTask<bool> ShouldEnableOutcome(ResilienceContext context) => ValueTask.FromResult(true); | ||
|
|
||
| private static HttpStatusCode CreateResultFromContext(ResilienceContext context) => HttpStatusCode.TooManyRequests; | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.