Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Polly-Contrib/Simmy/llms.txt

Use this file to discover all available pages before exploring further.

Simmy is designed to be composed directly into Polly’s PolicyWrap mechanism alongside your existing resilience policies such as retry, circuit breaker, and fallback. The key principle is straightforward: place the Simmy policy innermost in the wrap. This positioning lets the injected faults and delays propagate outward through your real resilience policies exactly as a genuine failure would in production, giving you an honest test of how your resilience configuration behaves under adverse conditions.

Why Placement Matters

PolicyWrap executes policies from outermost to innermost on the way in, and innermost to outermost on the way out. When a Simmy policy sits at the innermost position it intercepts the call at the last possible moment — subverting the real outbound call and substituting a fault, a result, or a delay. The outer Polly policies (retry, timeout, fallback, circuit breaker) never know the fault was synthetic: they react to it exactly as they would react to a real failure from the downstream dependency. If you placed the chaos policy outermost, the injected fault would bypass all your resilience policies entirely, which defeats the purpose of testing them.
Request in ──► Fallback ──► Retry ──► Timeout ──► [Chaos] ──► Real call

                                           Chaos injects fault here.
                                           Retry, Timeout, and Fallback
                                           all see a real-looking failure.
Placing the Simmy policy anywhere other than innermost means your resilience policies will not be exercised by the injected faults. Always verify that chaosPolicy is the last argument to Policy.Wrap or Policy.WrapAsync.

Synchronous PolicyWrap

// 1. Build your existing resilience policies (unchanged)
var fallbackPolicy = Policy
    .Handle<Exception>()
    .Fallback(() => GetFallbackValue());

var timeoutPolicy = Policy
    .Timeout(TimeSpan.FromSeconds(10));

// 2. Build the Simmy chaos policy
var chaosLatencyPolicy = MonkeyPolicy.InjectLatency(with =>
    with.Latency(TimeSpan.FromSeconds(5))
        .InjectionRate(0.1)
        .Enabled()
);

// 3. Compose: chaos is the innermost (last) policy
var policyWrap = Policy
    .Wrap(fallbackPolicy, timeoutPolicy, chaosLatencyPolicy);

// 4. Execute normally — no call-site changes needed
policyWrap.Execute(() => someMethod());
The call flows: fallbackPolicytimeoutPolicychaosLatencyPolicysomeMethod(). If chaosLatencyPolicy injects a 5-second delay, the timeoutPolicy will fire before someMethod() is ever called — which is precisely the scenario you want to validate.

Asynchronous PolicyWrap

// 1. Build async resilience policies
var fallbackPolicy = Policy<HttpResponseMessage>
    .Handle<Exception>()
    .FallbackAsync(new HttpResponseMessage(HttpStatusCode.OK));

var timeoutPolicy = Policy
    .TimeoutAsync(TimeSpan.FromSeconds(10));

// 2. Build the async Simmy chaos policy
var chaosLatencyPolicy = MonkeyPolicy.InjectLatencyAsync(with =>
    with.Latency(TimeSpan.FromSeconds(5))
        .InjectionRate(0.1)
        .Enabled()
);

// 3. Compose: chaos innermost
var policyWrap = Policy
    .WrapAsync(fallbackPolicy, timeoutPolicy, chaosLatencyPolicy);

// 4. Execute with cancellation token
var result = await policyWrap.ExecuteAsync(
    token => service.GetFoo(parametersBar, token),
    myCancellationToken
);
Because the async latency policy is cancellable (v0.2.0), the timeoutPolicy can cancel the injected delay just as it would cancel a real slow call. This means your timeout thresholds are tested authentically.

Adding Chaos to an Exception Policy Wrap

The same pattern works for fault injection. The retry policy will see the injected SocketException and apply its retry logic exactly as if the network had failed:
var retryPolicy = Policy
    .Handle<SocketException>()
    .Retry(3);

var chaosFaultPolicy = MonkeyPolicy.InjectException(with =>
    with.Fault(new SocketException(errorCode: 10013))
        .InjectionRate(0.05)   // 5 % of calls throw
        .Enabled()
);

var policyWrap = Policy.Wrap(retryPolicy, chaosFaultPolicy);

policyWrap.Execute(() => remoteService.Call());

Mixing Multiple Simmy Policies

You can include more than one chaos policy in a wrap. Each is evaluated independently, and you can give each a different injection rate and enabled condition.
var chaosLatency = MonkeyPolicy.InjectLatency(with =>
    with.Latency(TimeSpan.FromSeconds(3))
        .InjectionRate(0.05)
        .Enabled()
);

var chaosFault = MonkeyPolicy.InjectException(with =>
    with.Fault(new TimeoutException())
        .InjectionRate(0.02)
        .Enabled()
);

// Both chaos policies are innermost; resilience policies wrap them both
var policyWrap = Policy.Wrap(fallbackPolicy, retryPolicy, chaosLatency, chaosFault);
policyWrap.Execute(() => someMethod());

DI and HttpClientFactory Integration

When policies are configured through dependency injection — for example via ASP.NET Core’s HttpClientFactory — you can insert Simmy policies into an existing PolicyWrap at startup without modifying any of the existing policy configuration code. The typical approach is:
  1. Retrieve or build your existing array of resilience policies.
  2. Append a Simmy chaos policy to the end of that array (making it innermost).
  3. Pass the combined array to Policy.WrapAsync.
// In Startup.cs / Program.cs
services.AddHttpClient("MyClient")
    .AddPolicyHandler(GetRetryPolicy())
    .AddPolicyHandler(GetTimeoutPolicy())
    .AddPolicyHandler(GetChaosPolicy());   // chaos added last = innermost

IAsyncPolicy GetChaosPolicy()
{
    return MonkeyPolicy.InjectExceptionAsync(with =>
        with.Fault(new HttpRequestException("Chaos fault"))
            .InjectionRate(0.05)
            .EnabledWhen(async (ctx, ct) =>
                await chaosConfig.IsChaosEnabledAsync(ctx.OperationKey, ct))
    );
}
HttpClientFactory applies policies in the order they are added via AddPolicyHandler, with the last-added policy being innermost. Always add the Simmy policy last in the chain when using HttpClientFactory.
For a full walkthrough of startup-time DI patterns, see the Dependency Injection guide.

Execution Summary

Wrap positionWhat it tests
Innermost (recommended)Outer resilience policies (retry, timeout, fallback, circuit breaker) respond to the injected chaos exactly as they would to real failures
OutermostInjected faults bypass all resilience policies — resilience code is not tested
MiddleOnly policies outside the chaos position are tested; inner policies are bypassed

Build docs developers (and LLMs) love