Skip to content

Commit

Permalink
Use new chaos APIs to simplify the usage examples (#1912)
Browse files Browse the repository at this point in the history
  • Loading branch information
martintmk committed Jan 22, 2024
1 parent cf00835 commit 2e1a2a5
Show file tree
Hide file tree
Showing 11 changed files with 254 additions and 50 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,10 @@ Rate limiter strategy throws `RateLimiterRejectedException` if execution is reje

For more details, visit the [rate limiter strategy](https://www.pollydocs.org/strategies/rate-limiter) documentation.

## Chaos engineering

Starting with version `8.3.0`, Polly has integrated [Simmy](https://github.com/Polly-Contrib/Simmy), a chaos engineering library, directly into its core. For more information, please refer to the dedicated [chaos engineering documentation](https://www.pollydocs.org/chaos/).

## Next steps

To learn more about Polly, visit [pollydocs.org][polly-docs].
Expand Down
4 changes: 2 additions & 2 deletions docs/chaos/behavior.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ The behavior chaos strategy is designed to inject custom behaviors into system o

<!-- snippet: chaos-behavior-usage -->
```cs
// To use a custom function to generate the behavior to inject.
// To use a custom delegated for injected behavior
var optionsWithBehaviorGenerator = new BehaviorStrategyOptions
{
BehaviorAction = static args => RestartRedisVM(),
Expand All @@ -42,7 +42,7 @@ var optionsOnBehaviorInjected = new BehaviorStrategyOptions
new ResiliencePipelineBuilder().AddChaosBehavior(optionsWithBehaviorGenerator);
new ResiliencePipelineBuilder<HttpResponseMessage>().AddChaosBehavior(optionsOnBehaviorInjected);

// There are also a handy overload to inject the chaos easily.
// There are also a handy overload to inject the chaos easily
new ResiliencePipelineBuilder().AddChaosBehavior(0.05, RestartRedisVM);
```
<!-- endSnippet -->
Expand Down
66 changes: 60 additions & 6 deletions docs/chaos/fault.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,17 @@ The fault chaos strategy is designed to introduce faults (exceptions) into the s

<!-- snippet: chaos-fault-usage -->
```cs
// 10% of invocations will be randomly affected.
// 10% of invocations will be randomly affected and one of the exceptions will be thrown (equal probability).
var optionsBasic = new FaultStrategyOptions
{
FaultGenerator = static args => new ValueTask<Exception?>(new InvalidOperationException("Dummy exception")),
FaultGenerator = new FaultGenerator()
.AddException<InvalidOperationException>() // Uses default constructor
.AddException(() => new TimeoutException("Chaos timeout injected.")), // Custom exception generator
Enabled = true,
InjectionRate = 0.1
};

// To use a custom function to generate the fault to inject.
// To use a custom delegate to generate the fault to be injected
var optionsWithFaultGenerator = new FaultStrategyOptions
{
FaultGenerator = static args =>
Expand All @@ -34,7 +36,9 @@ var optionsWithFaultGenerator = new FaultStrategyOptions
{
"DataLayer" => new TimeoutException(),
"ApplicationLayer" => new InvalidOperationException(),
_ => null // When the fault generator returns null the strategy won't inject any fault and it will just invoke the user's callback
// When the fault generator returns null, the strategy won't inject
// any fault and just invokes the user's callback.
_ => null
};

return new ValueTask<Exception?>(exception);
Expand All @@ -46,7 +50,7 @@ var optionsWithFaultGenerator = new FaultStrategyOptions
// To get notifications when a fault is injected
var optionsOnFaultInjected = new FaultStrategyOptions
{
FaultGenerator = static args => new ValueTask<Exception?>(new InvalidOperationException("Dummy exception")),
FaultGenerator = new FaultGenerator().AddException<InvalidOperationException>(),
Enabled = true,
InjectionRate = 0.1,
OnFaultInjected = static args =>
Expand All @@ -60,7 +64,7 @@ var optionsOnFaultInjected = new FaultStrategyOptions
new ResiliencePipelineBuilder().AddChaosFault(optionsBasic);
new ResiliencePipelineBuilder<HttpResponseMessage>().AddChaosFault(optionsWithFaultGenerator);

// There are also a couple of handy overloads to inject the chaos easily.
// There are also a couple of handy overloads to inject the chaos easily
new ResiliencePipelineBuilder().AddChaosFault(0.1, () => new InvalidOperationException("Dummy exception"));
```
<!-- endSnippet -->
Expand Down Expand Up @@ -136,3 +140,53 @@ sequenceDiagram
F->>P: Throws injected Fault
P->>C: Propagates Exception
```

## Generating faults

To generate a fault, you need to specify a `FaultGenerator` delegate. You have the following options as to how you customize this delegate:

### Use `FaultGenerator` class to generate faults

The `FaultGenerator` is convenience API that allows you to specify what faults (exceptions) are to be injected. Additionally, it also allows assigning weight to each registered fault.

<!-- snippet: chaos-fault-generator-class -->
```cs
new ResiliencePipelineBuilder()
.AddChaosFault(new FaultStrategyOptions
{
// Use FaultGenerator to register exceptions to be injected
FaultGenerator = new FaultGenerator()
.AddException<InvalidOperationException>() // Uses default constructor
.AddException(() => new TimeoutException("Chaos timeout injected.")) // Custom exception generator
.AddException(context => CreateExceptionFromContext(context)) // Access the ResilienceContext
.AddException<TimeoutException>(weight: 50), // Assign weight to the exception, default is 100
});
```
<!-- endSnippet -->

### Use delegates to generate faults

Delegates give you the most flexibility at the expense of slightly more complicated syntax. Delegates also support asynchronous fault generation, if you ever need that possibility.

<!-- snippet: chaos-fault-generator-delegate -->
```cs
new ResiliencePipelineBuilder()
.AddChaosFault(new FaultStrategyOptions
{
// The same behavior can be achieved with delegates
FaultGenerator = args =>
{
Exception? exception = Random.Shared.Next(350) switch
{
< 100 => new InvalidOperationException(),
< 200 => new TimeoutException("Chaos timeout injected."),
< 300 => CreateExceptionFromContext(args.Context),
< 350 => new TimeoutException(),
_ => null
};

return new ValueTask<Exception?>(exception);
}
});
```
<!-- endSnippet -->
2 changes: 1 addition & 1 deletion docs/chaos/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ All the strategies' options implement the [`MonkeyStrategyOptions`](xref:Polly.S

| Property | Default Value | Description |
|--------------------------|---------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `InjectionRate` | 0.001 ms | A decimal between 0 and 1 inclusive. The strategy will inject the chaos, randomly, that proportion of the time, e.g.: if 0.2, twenty percent of calls will be randomly affected; if 0.01, one percent of calls; if 1, all calls. |
| `InjectionRate` | 0.001 | A decimal between 0 and 1 inclusive. The strategy will inject the chaos, randomly, that proportion of the time, e.g.: if 0.2, twenty percent of calls will be randomly affected; if 0.01, one percent of calls; if 1, all calls. |
| `InjectionRateGenerator` | `null` | Generates the injection rate for a given execution, which the value should be between [0, 1] (inclusive). |
| `Enabled` | `false` | Determines whether the strategy is enabled or not. |
| `EnabledGenerator` | `null` | The generator that indicates whether the chaos strategy is enabled for a given execution. |
Expand Down
10 changes: 6 additions & 4 deletions docs/chaos/latency.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ The latency chaos strategy is designed to introduce controlled delays into syste
// See https://www.pollydocs.org/chaos/latency#defaults for defaults.
var optionsDefault = new LatencyStrategyOptions();

// 10% of invocations will be randomly affected.
// 10% of invocations will be randomly affected
var basicOptions = new LatencyStrategyOptions
{
Latency = TimeSpan.FromSeconds(30),
Enabled = true,
InjectionRate = 0.1
};

// To use a custom function to generate the latency to inject.
// To use a custom function to generate the latency to inject
var optionsWithLatencyGenerator = new LatencyStrategyOptions
{
LatencyGenerator = static args =>
Expand All @@ -38,7 +38,9 @@ var optionsWithLatencyGenerator = new LatencyStrategyOptions
{
"DataLayer" => TimeSpan.FromMilliseconds(500),
"ApplicationLayer" => TimeSpan.FromSeconds(2),
_ => TimeSpan.Zero // When the latency generator returns Zero the strategy won't inject any delay and it will just invoke the user's callback
// When the latency generator returns Zero, the strategy
// won't inject any delay and just invokes the user's callback.
_ => TimeSpan.Zero
};

return new ValueTask<TimeSpan>(latency);
Expand All @@ -64,7 +66,7 @@ var optionsOnBehaviorInjected = new LatencyStrategyOptions
new ResiliencePipelineBuilder().AddChaosLatency(optionsDefault);
new ResiliencePipelineBuilder<HttpStatusCode>().AddChaosLatency(optionsWithLatencyGenerator);

// There are also a handy overload to inject the chaos easily.
// There are also a handy overload to inject the chaos easily
new ResiliencePipelineBuilder().AddChaosLatency(0.1, TimeSpan.FromSeconds(30));
```
<!-- endSnippet -->
Expand Down
70 changes: 58 additions & 12 deletions docs/chaos/result.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,22 @@ The outcome chaos strategy is designed to inject or substitute fake results into

<!-- snippet: chaos-result-usage -->
```cs
// To use a custom function to generate the result to inject.
// To use OutcomeGenerator<T> to register the results and exceptions to be injected (equal probability)
var optionsWithResultGenerator = new OutcomeStrategyOptions<HttpResponseMessage>
{
OutcomeGenerator = static args =>
{
var response = new HttpResponseMessage(HttpStatusCode.InternalServerError);
return new ValueTask<Outcome<HttpResponseMessage>?>(Outcome.FromResult(response));
},
OutcomeGenerator = new OutcomeGenerator<HttpResponseMessage>()
.AddResult(() => new HttpResponseMessage(HttpStatusCode.TooManyRequests))
.AddResult(() => new HttpResponseMessage(HttpStatusCode.InternalServerError))
.AddException(() => new HttpRequestException("Chaos request exception.")),
Enabled = true,
InjectionRate = 0.1
};

// To get notifications when a result is injected
var optionsOnBehaviorInjected = new OutcomeStrategyOptions<HttpResponseMessage>
{
OutcomeGenerator = static args =>
{
var response = new HttpResponseMessage(HttpStatusCode.InternalServerError);
return new ValueTask<Outcome<HttpResponseMessage>?>(Outcome.FromResult(response));
},
OutcomeGenerator = new OutcomeGenerator<HttpResponseMessage>()
.AddResult(() => new HttpResponseMessage(HttpStatusCode.InternalServerError)),
Enabled = true,
InjectionRate = 0.1,
OnOutcomeInjected = static args =>
Expand All @@ -52,7 +48,7 @@ var optionsOnBehaviorInjected = new OutcomeStrategyOptions<HttpResponseMessage>
new ResiliencePipelineBuilder<HttpResponseMessage>().AddChaosResult(optionsWithResultGenerator);
new ResiliencePipelineBuilder<HttpResponseMessage>().AddChaosResult(optionsOnBehaviorInjected);

// There are also a couple of handy overloads to inject the chaos easily.
// There are also a couple of handy overloads to inject the chaos easily
new ResiliencePipelineBuilder<HttpResponseMessage>().AddChaosResult(0.1, () => new HttpResponseMessage(HttpStatusCode.TooManyRequests));
```
<!-- endSnippet -->
Expand Down Expand Up @@ -136,3 +132,53 @@ sequenceDiagram
B->>P: Returns result
P->>C: Returns result
```

## Generating outcomes

To generate a faulted outcome (result or exception), you need to specify a `OutcomeGenerator` delegate. You have the following options as to how you customize this delegate:

### Use `OutcomeGenerator<T>` class to generate outcomes

The `OutcomeGenerator<T>` is a convenience API that allows you to specify what outcomes (results or exceptions) are to be injected. Additionally, it also allows assigning weight to each registered outcome.

<!-- snippet: chaos-outcome-generator-class -->
```cs
new ResiliencePipelineBuilder<HttpResponseMessage>()
.AddChaosResult(new OutcomeStrategyOptions<HttpResponseMessage>
{
// Use OutcomeGenerator<T> to register the results and exceptions to be injected
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
.AddException<HttpRequestException>(), // You can also register exceptions
});
```
<!-- endSnippet -->

### Use delegates to generate faults

Delegates give you the most flexibility at the expense of slightly more complicated syntax. Delegates also support asynchronous outcome generation, if you ever need that possibility.

<!-- snippet: chaos-outcome-generator-delegate -->
```cs
new ResiliencePipelineBuilder<HttpResponseMessage>()
.AddChaosResult(new OutcomeStrategyOptions<HttpResponseMessage>
{
// The same behavior can be achieved with delegates
OutcomeGenerator = 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)),
< 350 => Outcome.FromException<HttpResponseMessage>(new TimeoutException()),
_ => null
};

return ValueTask.FromResult(outcome);
}
});
```
<!-- endSnippet -->
2 changes: 1 addition & 1 deletion docs/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
- name: Advanced topics
expanded: true
items:
- name: Telemetry and monitoring
- name: Telemetry
href: advanced/telemetry.md
- name: Dependency injection
href: advanced/dependency-injection.md
Expand Down
4 changes: 2 additions & 2 deletions src/Snippets/Docs/Chaos.Behavior.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public static void BehaviorUsage()
static ValueTask RestartRedisVM() => ValueTask.CompletedTask;

#region chaos-behavior-usage
// To use a custom function to generate the behavior to inject.
// To use a custom delegated for injected behavior
var optionsWithBehaviorGenerator = new BehaviorStrategyOptions
{
BehaviorAction = static args => RestartRedisVM(),
Expand All @@ -37,7 +37,7 @@ public static void BehaviorUsage()
new ResiliencePipelineBuilder().AddChaosBehavior(optionsWithBehaviorGenerator);
new ResiliencePipelineBuilder<HttpResponseMessage>().AddChaosBehavior(optionsOnBehaviorInjected);

// There are also a handy overload to inject the chaos easily.
// There are also a handy overload to inject the chaos easily
new ResiliencePipelineBuilder().AddChaosBehavior(0.05, RestartRedisVM);
#endregion

Expand Down
Loading

0 comments on commit 2e1a2a5

Please sign in to comment.