Table of Contents

Resilience context

The ResilienceContext class in Polly provides an execution-scoped instance that accompanies each execution through a Polly resilience pipeline and across all strategies in the pipeline. This class serves to share context and facilitate information exchange between the pre-execution, mid-execution, and post-execution phases.

The resilience context exposes several properties:

  • OperationKey: A user-defined identifier for the operation.
  • CancellationToken: The cancellation token linked to the operation.
  • Properties: An instance of ResilienceProperties for attaching custom data to the context.
  • ContinueOnCapturedContext: Specifies whether the asynchronous execution should continue on the captured context.

Usage

Below is an example demonstrating how to work with ResilienceContext:

// Retrieve a context with a cancellation token
ResilienceContext context = ResilienceContextPool.Shared.Get(cancellationToken);

// Attach custom data to the context
context.Properties.Set(MyResilienceKeys.Key1, "my-data");
context.Properties.Set(MyResilienceKeys.Key2, 123);

// Utilize the context in a resilience pipeline
ResiliencePipeline pipeline = new ResiliencePipelineBuilder()
    .AddRetry(new()
    {
        OnRetry = static args =>
        {
            // Retrieve custom data from the context, if available
            if (args.Context.Properties.TryGetValue(MyResilienceKeys.Key1, out var data))
            {
                Console.WriteLine("OnRetry, Custom Data: {0}", data);
            }

            return default;
        }
    })
    .Build();

// Execute the resilience pipeline asynchronously
await pipeline.ExecuteAsync(
    static async context =>
    {
        // Insert your execution logic here
    },
    context);

// Return the context to the pool
ResilienceContextPool.Shared.Return(context);

Where ResilienceKeys is defined as:

public static class MyResilienceKeys
{
    public static readonly ResiliencePropertyKey<string> Key1 = new("my-key-1");

    public static readonly ResiliencePropertyKey<int> Key2 = new("my-key-2");
}
Note

We recommend defining a static class to hold the resilience property keys used in your project. This approach makes these keys easier to discover and maintain. For simpler scenarios, you can directly use the creation of ResiliencePropertyKey<string> since it's a cheap, struct-based API.

Sequence diagram

sequenceDiagram
    autonumber
    actor C as Caller
    participant CP as ResilienceContextPool
    participant P as Pipeline
    participant R as Retry
    participant D as DecoratedUserCallback
    participant O as OnRetryUserCallback

    C->>CP: Rents a context
    CP->>C: Gives a context
    C->>P: Calls ExecuteAsync<br/>with context
    P->>R: Calls ExecuteCore<br/>with context
    Note over R,D: Initial attempt
    R->>+D: Invokes<br/>with context
    D->>-R: Fails
    R->>+O: Invokes<br/>with context
    O->>-R: Completes
    R-->>R: Sleeps
    Note over R,D: 1st retry attempt
    R->>+D: Invokes<br/>with context
    D->>-R: Returns result
    R->>P: Returns result
    P->>C: Returns result
    C-->>C: Accesses context
    C->>CP: Returns the context

Resilient context pooling

The ResilienceContext object is resource-intensive to create, and recreating it for each execution would negatively impact performance. To address this issue, Polly provides a ResilienceContextPool. This pool allows you to obtain and reuse ResilienceContext instances. Once you've finished using a context instance, you can return it to the pool. This action will reset the context to its initial state, making it available for reuse.

The ResilienceContextPool offers several Get methods. These methods not only allow you to retrieve a ResilienceContext instance, but also enable you to initialize some of its properties at the time of retrieval.

// Retrieve a context with a cancellation token
ResilienceContext context = ResilienceContextPool.Shared.Get(cancellationToken);

try
{
    // Retrieve a context with a specific operation key
    context = ResilienceContextPool.Shared.Get("my-operation-key", cancellationToken);

    // Retrieve a context with multiple properties
    context = ResilienceContextPool.Shared.Get(
        operationKey: "my-operation-key",
        continueOnCapturedContext: true,
        cancellationToken: cancellationToken);

    // Use the pool here
}
finally
{
    // Returning the context back to the pool is recommended, but not required as it reduces the allocations.
    // It is also OK to not return the context in case of exceptions, if you want to avoid try-catch blocks.
    ResilienceContextPool.Shared.Return(context);
}
Note

The OperationKey values are reported in telemetry. Beware of using very large or unbounded combinations for the operation key. See best practices for more details.