Supporting Retry and Circuit Breaker
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
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. Strategies are applied in the order they're added (inner to outer wrapping).
Retry + Circuit Breaker + Timeout
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.
Using Multiple Pipelines on a Handler
You can apply multiple resilience pipeline attributes to a handler. Each attribute wraps subsequent steps in the pipeline.
Execution order: Circuit Breaker → Retry → Handler
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 the builder:
Or using dependency injection with ASP.NET Core:
Migration Guide: V9 to V10
Step 1: Update NuGet Packages
Step 2: Replace Policy Registry with Resilience Pipeline Registry
V9:
V10:
Step 3: Replace Attributes in Handlers
V9:
V10:
Step 4: Update CommandProcessor Configuration
V9:
V10:
Note: You can use both
Policies()andResiliencePipelines()during migration to support both legacyUsePolicyand newUseResiliencePipelineattributes.
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.
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 migration guide above).
Additional Resources
Legacy: Using Polly v7 Policies (Deprecated)
⚠️ DEPRECATED: The following section documents the legacy Polly v7
UsePolicyattribute, which is deprecated in favor ofUseResiliencePipeline. This is maintained for backward compatibility only.
Using Brighter's UsePolicy Attribute (Legacy)
By adding the UsePolicy attribute, you instruct the Command Processor to insert a handler (filter) into the pipeline that runs all later steps using that Polly policy.
To configure the Polly policy you use the PolicyRegistry to register the Polly Policy with a name. At runtime we look up that Policy by name.
You can use multiple policies with a handler, instead of passing in a single policy identifier, you can pass in an array of policy identifiers:
So if in addition to the above policy we have:
then you can add them both to your handler as follows:
Where we have multiple policies they are evaluated left to right, so in this case "MyCircuitBreakerPolicy" wraps "MyExceptionPolicy".
When creating policies, refer to the Polly documentation.
Whilst Polly does not support a Policy that is both Circuit Breaker and Retry i.e. retry n times with an interval between each retry, and then break circuit, to implement that simply put a Circuit Breaker UsePolicy attribute as an earlier step than the Retry UsePolicy attribute. If retries expire, the exception will bubble out to the Circuit Breaker.
Timeout (Legacy - Deprecated)
⚠️ DEPRECATED: The
TimeoutPolicyattribute is obsolete in V10 and will be removed in V11. UseUseResiliencePipelinewith Polly's Timeout strategy instead.
You should not allow a handler that calls out to another process (e.g. a call to a Database, queue, or an API) to run without a timeout. If the process has failed, you will consume a resource in your application polling that resource. This can cause your application to fail because another process failed.
Usually the client library you are using will have a timeout value that you can set.
In some scenarios the client library does not provide a timeout, so you have no way to abort.
We provide the Timeout attribute for that circumstance. You can apply it to a Handler to force that Handler into a thread which we will timeout, if it does not complete within the required time period.
V10 Replacement:
Last updated
Was this helpful?
