-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Keys and Context Data
Note
This documentation describes the previous Polly v7 API. If you are using the new v8 API, please refer to pollydocs.org.
An execution-scoped instance of the Polly.Context class travels with every execution through a Polly policy.
The role of this class is to provide execution context and to allow the exchange of information between the pre-execution, mid-execution, and post-execution phases.
Context carries two kinds of information:
- Values for pre-defined keys - these can uniquely identify the policy in use, policywrap in use, execution call site, and a unique correlation-id per execution.
-
Custom data - you can add any custom data to
Context, using Dictionary<string, object> semantics.
The context instance travelling with the execution is available:
- before execution (if you define it yourself and pass it in)
- to the main delegate
.Execute/Async(...)-d through the policy (when you use an overload takingContext) - to every delegate hook (
onRetry,onBreaketc) - after execution.
It can therefore be used to pass information into executions, get information out of executions, and exchange information among any part of a policy execution.
The following four pre-defined keys are available as properties directly on Context:
| Key | Scope (ie unique to) | How to set | Default (if not set) |
Purpose |
|---|---|---|---|---|
PolicyWrapKey |
PolicyWrap instance (in nested wrap, takes outermost specified value) |
policyWrap .WithPolicyKey("someValue")
|
PolicyWrap-partguid; or null if no PolicyWrap in use |
Indicate the PolicyWrap in use |
PolicyKey |
Policy instance |
policy .WithPolicyKey("someValue")
|
PolicyType-partguid | Indicate the Policy in use |
OperationKey |
call site (if code specifies it) |
pass new Context("someOperationKey") into the Execute call |
null |
Indicate where the policy in use Used by CachePolicy as the cache key for the execution |
CorrelationId |
an individual execution | not user-settable | a unique guid | Uniquely correlate logs/ metrics for a single execution |
OperationKey was named ExecutionKey prior to v6.
CorrelationId was named ExecutionGuid prior to v6.
// Identify policies with a PolicyKey, using the WithPolicyKey() extension method
// (for example, for correlation in logs or metrics)
var policy = Policy
.Handle<DataAccessException>()
.Retry(3, onRetry: (exception, retryCount, context) =>
{
logger.Error($"Retry {retryCount} of {context.PolicyKey} at {context.OperationKey}, due to: {exception}.");
})
.WithPolicyKey("MyDataAccessPolicy");
// Identify call sites with an OperationKey, by passing in a Context
var customerDetails = policy.Execute(myDelegate, new Context("GetCustomerDetails"));
// "MyDataAccessPolicy" -> context.PolicyKey
// "GetCustomerDetails" -> context.OperationKeyContext has full Dictionary<string, object> semantics. You can set data on an instance of Context
var context = new Polly.Context();
context["MyCustomData"] = foo;and get it back later anywhere context is available - within an onRetry hook, within the delegate executed through the policy, after policy execution:
var myFooFromElsewhere = context["MyCustomData"];Given a policy (abbreviated):
// Policy which logs information available from both pre-defined and custom user context data
var policy = Policy
.Handle<FooException>()
.RetryAsync(3, onRetry: (exception, retryCount, context) =>
{
logger.Error($"Retry {retryCount} of {context.PolicyKey} at {context.OperationKey}, getting {context["Type"]} of id {context["Id"]}, due to: {exception}. CorrelationId: {context.CorrelationId}");
})
.WithPolicyKey("GetEntitiesPolicy");An example call site could pass in extra information (abbreviated example code):
public class EntityDataAccess<T>
{
private IAsyncPolicy Policy { get; }
public EntityDataAccess(IAsyncPolicy policy) { Policy = policy; }
public async T GetAsync(int id)
{
var context = new Context($"GetAsync-{typeof(T).Name}-{Id}", new Dictionary<string, object>() {
{ "Type" , typeof(T).Name },
{ "Id" , id }
});
return await Policy.ExecuteAsync(ctx => database.GetAsync(id), context);
}
}A common use of Context is to bridge the fact that policies are often defined (including policy callback delegates) on startup; but some information you want to use within the delegate hooks (such as an ILogger) might only be available via dependency injection at the call site.
The policy can be pre-defined in StartUp to retrieve the information from context:
var policy = Policy.Handle<FooException>()
.WaitAndRetryAsync(3,
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
(exception, timeSpan, retryCount, context) =>
{
var msg = $"Retrying request for {retryCount} " +
$"at {context.OperationKey}, " +
$"due to: {exception}.";
var logger = context.GetLogger();
logger?.LogWarning(msg);
});And the call site can configure the ILogger onto Context prior to execution:
// Meanwhile at the call site, given some ILogger logger available by dependency injection
Context context = new Context().WithLogger(logger);
return await policy.ExecuteAsync(ctx => FooAsync(), context);In the above code, we used some helper methods to make the storage and retrieval of the ILogger more concise and robust:
public static class ContextExtensions
{
private static readonly string LoggerKey = "LoggerKey";
public static Context WithLogger(this Context context, ILogger logger)
{
context[LoggerKey] = logger;
return context;
}
public static ILogger GetLogger(this Context context)
{
if (context.TryGetValue(LoggerKey, out object logger))
{
return logger as ILogger;
}
return null;
}
}See the Polly with HttpClientFactory page.
- Home
- Polly RoadMap
- Contributing
- Transient fault handling and proactive resilience engineering
- Supported targets
- Retry
- Circuit Breaker
- Advanced Circuit Breaker
- Timeout
- Bulkhead
- Cache
- Rate-Limit
- Fallback
- PolicyWrap
- NoOp
- PolicyRegistry
- Polly and HttpClientFactory
- Asynchronous action execution
- Handling InnerExceptions and AggregateExceptions
- Statefulness of policies
- Keys and Context Data
- Non generic and generic policies
- Polly and interfaces
- Some policy patterns
- Debugging with Polly in Visual Studio
- Unit-testing with Polly
- Polly concept and architecture
- Polly v6 breaking changes
- Polly v7 breaking changes
- DISCUSSION PROPOSAL- Polly eventing and metrics architecture