Polly icon indicating copy to clipboard operation
Polly copied to clipboard

[Question]: Is it recommended to use ResiliencePipeline instance as a singleton?

Open lnaie opened this issue 7 months ago • 2 comments

What are you wanting to achieve?

Hello,

Great job! A quick question, when I'm not using the .net core DI and I was wondering if I can use safely a single instance of ResiliencePipeline to make async calls with retries?

Thanks

What code or approach do you have so far?

I don't think this needs code. :)

Additional context

No response

lnaie avatar Jun 12 '25 22:06 lnaie

Yes you can 🙂

martincostello avatar Jun 12 '25 22:06 martincostello

Please bear in mind that it is safe only if you create a new ResilienceContext per execution!

New context for each execution

var taskIdKey = new ResiliencePropertyKey<int>("TaskId");
var pipeline = new ResiliencePipelineBuilder<string>()
	.AddRetry(new RetryStrategyOptions<string>()
	{
		MaxRetryAttempts = 10,
		Delay = TimeSpan.FromSeconds(1),
		ShouldHandle = new PredicateBuilder<string>().HandleResult(string.IsNullOrWhiteSpace),
		OnRetry = args => 
		{ 
			var taskId = args.Context.Properties.GetValue<int>(taskIdKey, -1);
			Console.WriteLine($"Task Id #{taskId} has its {args.AttemptNumber}th retry attempt"); 
			return default; 
		}
	})
	.Build();

List<Task> tasks = [];
List<ResilienceContext> contexts = [];

for(int i = 0; i < 10; i++)
{
	var context = ResilienceContextPool.Shared.Get(); // new context
	tasks.Add(pipeline.ExecuteAsync(async (ctx, taskId) => 
	{ 
		ctx.Properties.Set(taskIdKey, taskId);
		await Task.Delay(500); 
		return string.Empty; 
	}, context, i).AsTask());
    	contexts.Add(context);
}

await Task.WhenAll(tasks);
contexts.ForEach(ctx => ResilienceContextPool.Shared.Return(ctx));

The ouput will be something like this:

Task Id #0 has its 0th retry attempt
Task Id #1 has its 0th retry attempt
Task Id #2 has its 0th retry attempt
Task Id #3 has its 0th retry attempt
Task Id #4 has its 0th retry attempt
Task Id #6 has its 0th retry attempt
Task Id #7 has its 0th retry attempt
Task Id #8 has its 0th retry attempt
Task Id #5 has its 0th retry attempt
Task Id #9 has its 0th retry attempt
Task Id #0 has its 1th retry attempt
Task Id #2 has its 1th retry attempt
Task Id #1 has its 1th retry attempt
Task Id #3 has its 1th retry attempt
Task Id #5 has its 1th retry attempt
Task Id #6 has its 1th retry attempt
Task Id #9 has its 1th retry attempt
Task Id #8 has its 1th retry attempt
Task Id #7 has its 1th retry attempt
Task Id #4 has its 1th retry attempt
Task Id #0 has its 2th retry attempt
Task Id #1 has its 2th retry attempt
Task Id #2 has its 2th retry attempt
Task Id #3 has its 2th retry attempt
Task Id #5 has its 2th retry attempt
Task Id #8 has its 2th retry attempt
Task Id #7 has its 2th retry attempt
Task Id #9 has its 2th retry attempt
Task Id #6 has its 2th retry attempt
Task Id #4 has its 2th retry attempt
Task Id #3 has its 3th retry attempt
Task Id #0 has its 3th retry attempt
Task Id #2 has its 3th retry attempt
Task Id #1 has its 3th retry attempt
Task Id #7 has its 3th retry attempt
Task Id #5 has its 3th retry attempt
Task Id #9 has its 3th retry attempt
Task Id #4 has its 3th retry attempt
Task Id #6 has its 3th retry attempt
Task Id #8 has its 3th retry attempt
Task Id #3 has its 4th retry attempt
...

The same context for all execution

var taskIdKey = new ResiliencePropertyKey<int>("TaskId");
var pipeline = new ResiliencePipelineBuilder<string>()
	.AddRetry(new RetryStrategyOptions<string>()
	{
		MaxRetryAttempts = 10,
		Delay = TimeSpan.FromSeconds(1),
		ShouldHandle = new PredicateBuilder<string>().HandleResult(string.IsNullOrWhiteSpace),
		OnRetry = args => 
		{ 
			var taskId = args.Context.Properties.GetValue<int>(taskIdKey, -1);
			Console.WriteLine($"Task Id #{taskId} has its {args.AttemptNumber}th retry attempt"); 
			return default; 
		}
	})
	.Build();

List<Task> tasks = [];
var context = ResilienceContextPool.Shared.Get(); //shared context
for(int i = 0; i < 10; i++)
{
	
	tasks.Add(pipeline.ExecuteAsync(async (ctx, taskId) => 
	{ 
		ctx.Properties.Set(taskIdKey, taskId);
		await Task.Delay(500); 
		return string.Empty; 
	}, context, i).AsTask());
}

await Task.WhenAll(tasks);
ResilienceContextPool.Shared.Return(context);

The ouput will be something like this:

Task Id #9 has its 0th retry attempt
Task Id #9 has its 0th retry attempt
Task Id #9 has its 0th retry attempt
Task Id #9 has its 0th retry attempt
Task Id #9 has its 0th retry attempt
Task Id #9 has its 0th retry attempt
Task Id #9 has its 0th retry attempt
Task Id #9 has its 0th retry attempt
Task Id #9 has its 0th retry attempt
Task Id #9 has its 0th retry attempt
Task Id #6 has its 1th retry attempt
Task Id #6 has its 1th retry attempt
Task Id #6 has its 1th retry attempt
Task Id #6 has its 1th retry attempt
Task Id #6 has its 1th retry attempt
Task Id #6 has its 1th retry attempt
Task Id #6 has its 1th retry attempt
Task Id #6 has its 1th retry attempt
Task Id #6 has its 1th retry attempt
Task Id #6 has its 1th retry attempt
Task Id #6 has its 2th retry attempt
Task Id #6 has its 2th retry attempt
Task Id #6 has its 2th retry attempt
Task Id #6 has its 2th retry attempt
Task Id #6 has its 2th retry attempt
Task Id #6 has its 2th retry attempt
Task Id #6 has its 2th retry attempt
Task Id #6 has its 2th retry attempt
Task Id #6 has its 2th retry attempt
Task Id #6 has its 2th retry attempt
Task Id #6 has its 3th retry attempt
Task Id #6 has its 3th retry attempt
Task Id #6 has its 3th retry attempt
Task Id #6 has its 3th retry attempt
Task Id #6 has its 3th retry attempt
Task Id #6 has its 3th retry attempt
Task Id #6 has its 3th retry attempt
Task Id #6 has its 3th retry attempt
Task Id #6 has its 3th retry attempt
Task Id #6 has its 3th retry attempt
Task Id #6 has its 4th retry attempt
...

Dotnet Fiddle

peter-csala avatar Jun 16 '25 08:06 peter-csala