Supporting Retry and Circuit Breaker
How-to · Applies to Brighter V10
Brighter is a Command Processor and supports a pipeline of Handlers to handle orthogonal requests.
Amongst the valuable uses of orthogonal requests is patterns to support Quality of Service in a distributed environment: Timeout, Retry, and Circuit Breaker.
Even if you don't believe that you are writing a distributed system that needs this protection, consider that as soon as you have multiple processes, such as a database server, you are distributed.
Brighter uses Polly to support Retry and Circuit-Breaker. Through our Russian Doll Model we are able to run the target handler in the context of a Policy Handler, that catches exceptions, and applies a Policy on how to deal with them.
Polly v8 Resilience Pipelines
Brighter supports Polly v8 Resilience Pipelines, which provides a modern, streamlined API for building resilience strategies:
Full Polly v8 Support: Access to all Polly v8 resilience strategies (Retry, Circuit Breaker, Timeout, Rate Limiter, Fallback, Hedging)
New
UseResiliencePipelineAttribute: ReplacesUsePolicyattribute for Polly v8 pipelinesEnhanced Context Integration: Request context integrates with Polly's resilience context
Proper CancellationToken Flow: Cancellation tokens flow correctly through resilience pipelines
Type-Scoped Pipelines: Support for per-handler-type pipelines (useful for Circuit Breakers)
Migration from V9 to V10
[UsePolicy]
[UseResiliencePipeline]
New attribute for Polly v8 pipelines
[TimeoutPolicy]
[UseResiliencePipeline] with Timeout strategy
TimeoutPolicy deprecated in V10, removed in V11
PolicyRegistry
ResiliencePipelineRegistry<string>
From Polly.Registry namespace
Policies configured with Policy.Handle<>()
Pipelines configured with ResiliencePipelineBuilder
New fluent API
⚠️ DEPRECATION NOTICE: The
TimeoutPolicyAttributeis marked as obsolete in V10 and will be removed in V11. Migrate toUseResiliencePipelinewith a Timeout strategy instead.
Using Brighter's UseResiliencePipeline Attribute
By adding the UseResiliencePipeline attribute, you instruct the Command Processor to insert a handler (filter) into the pipeline that runs all later steps using that Polly resilience pipeline.
Basic Example
Async Handlers Take UseResiliencePipelineAsync
UseResiliencePipeline decorates a synchronous Handle. An async handler takes the async attribute instead, and pairing them the other way round leaves the pipeline out of the chain:
Both attributes read from the same ResiliencePipelineRegistry<string>, so a pipeline name registered once serves handlers of either kind.
Configuring Resilience Pipelines
To configure a Polly resilience pipeline, you use the ResiliencePipelineRegistry<string> to register pipelines with a name. At runtime, Brighter looks up that pipeline by name.
Retry Strategy Example
Circuit Breaker Strategy Example
Timeout Strategy Example
V10: Use Polly's Timeout strategy instead of the deprecated TimeoutPolicyAttribute:
Handler Usage:
Combining Multiple Strategies
You can combine multiple resilience strategies in a single pipeline. The first strategy you add is the outermost: it wraps every strategy added after it. So to time out each attempt, retry the attempts that fail, and stop retrying while a service is known to be down, add the circuit breaker first and the timeout last.
Retry + Circuit Breaker + Timeout
Added the other way round, the timeout would wrap the retries and limit all of them together to 10 seconds, and the circuit breaker would sit inside the retry.
Handler Usage:
How it works:
Circuit breaker checks if circuit is open (fails fast if open)
Retry wraps the operation (retries on failures)
Timeout wraps each individual attempt (times out after 10 seconds per attempt)
Note: If retries are exhausted, the exception will bubble out to the Circuit Breaker, which will count it as a failure.
Combining Resilience Strategies on a Handler
A handler method takes one [UseResiliencePipeline]. The attribute is not repeatable, so a second one on the same method does not compile (CS0579, "Duplicate 'UseResiliencePipeline' attribute"). To layer strategies, compose them in one pipeline, as Combining Multiple Strategies shows, and name that:
Other attributes, such as [FallbackPolicy] or [RejectMessageOnError], can still sit beside it at their own steps.
Type-Scoped Pipelines
For strategies like Circuit Breaker, you often want a separate instance per handler type (so failures in one handler don't affect others). Use UseTypePipeline = true to scope pipelines by handler type.
How it works: When UseTypePipeline = true, Brighter looks up the pipeline using a key composed of the handler type's full name + the policy name. This allows different instances of the same resilience strategy to be associated uniquely with each handler type.
Configuration:
All Available Polly v8 Strategies
Polly v8 provides the following resilience strategies, all of which can be used with Brighter:
Retry
Retries operations that fail transiently
Network glitches, temporary service unavailability
Circuit Breaker
Prevents cascading failures by breaking the circuit
Dependency is down, prevent overwhelming failed services
Timeout
Limits operation execution time
Prevent hanging on slow operations
Rate Limiter
Controls the rate of operations
Throttle requests to external APIs
Fallback
Provides alternative value/action on failure
Graceful degradation, cached responses
Hedging
Executes parallel operations and takes first result
Improve latency in distributed systems
Rate Limiter Example
Hedging Example
CancellationToken Integration
Polly v8 resilience pipelines properly integrate with CancellationToken, allowing you to cancel operations in progress.
Request Context Integration
The request context integrates with Polly's resilience context, allowing you to access request metadata within resilience callbacks.
See Request Context documentation for more details on accessing the resilience context from handlers.
Registering Pipelines with CommandProcessor
When creating your CommandProcessor, pass the ResiliencePipelineRegistry<string> to Resilience:
Resilience takes the Polly v8 registry first and an optional Polly v7 IPolicyRegistry<string> second — one call, not two. If you have no legacy policies to carry, call DefaultResilience() instead and Brighter supplies its own registry.
Call
AddBrighterDefaultwhenever you build the registry yourself. Brighter requires a pipeline registered underCommandProcessor.OutboxProducer, andResiliencethrowsConfigurationExceptionon its first statement when the registry does not have one — at startup, before any message is sent. Brighter only fills the gap for you when you supply no registry at all, because the container does it with??=.
Or using dependency injection with ASP.NET Core:
There is no fluent ConfigureResiliencePipelines method on the DI builder. The registry is a settable property on BrighterOptions, set inside the AddBrighter options delegate as above. This is the same ??= case as the builder: assigning your own registry means Brighter never calls AddBrighterDefault for you, so call it yourself.
Retry and Circuit Breaker Best Practices
Use Resilience Pipelines for New Code: Prefer
UseResiliencePipelineover legacyUsePolicyfor new handlers.Migrate Away from TimeoutPolicy: Replace
[TimeoutPolicy]with[UseResiliencePipeline]using Polly's Timeout strategy. TimeoutPolicy will be removed in V11.Use Type-Scoped Pipelines for Circuit Breakers: Set
UseTypePipeline = truewhen using Circuit Breakers to avoid failures in one handler affecting others.Combine Strategies Thoughtfully: When combining timeout, retry, and circuit breaker:
Put timeout innermost (times out individual attempts)
Put retry in the middle (retries failed attempts)
Put circuit breaker outermost (prevents retry when service is known to be down)
Configure Appropriate Delays: Use exponential backoff for retries to avoid overwhelming recovering services.
Monitor Circuit Breaker State: Use
OnOpened,OnClosed, andOnHalfOpenedcallbacks to log and monitor circuit breaker state changes.Pass CancellationTokens: Always pass and respect
CancellationTokenin async handlers to support cancellation through resilience pipelines.Use Request Context: Leverage request context integration for correlation IDs, tracing, and logging within resilience callbacks.
Retry and Circuit Breaker Troubleshooting
Pipeline Not Found Exception
Symptom: InvalidOperationException: Resilience pipeline with key 'MyPipeline' not found
Solution: Ensure you've registered the pipeline with the exact name referenced in the attribute:
Type Pipeline Not Found Exception
Symptom: InvalidOperationException: Resilience pipeline with key 'MyNamespace.MyHandler.MyPipeline' not found
Solution: When using UseTypePipeline = true, register the pipeline with the full key:
TimeoutPolicy Obsolete Warning
Symptom: Compiler warning: 'TimeoutPolicyAttribute' is obsolete: 'It is recommended to use UsePolicyAttribute or UseResiliencePipelineAttribute instead'
Solution: Replace [TimeoutPolicy] with [UseResiliencePipeline] using Polly's Timeout strategy (see Migrating to Polly v8).
Further Reading
Migrating to Polly v8 - The V9 to V10 migration steps, and the deprecated Polly v7 attribute
Last updated
Was this helpful?
