From 72b1c60674dcc56ca209d8e0a3eb2e6795905469 Mon Sep 17 00:00:00 2001 From: Peter Csala Date: Thu, 10 Apr 2025 10:24:53 +0200 Subject: [PATCH 1/7] Shift and lift --- .../pipelines/resilience-pipeline-registry.md | 110 ++++++++++++++++++ .../Docs/ResiliencePipelineRegistry.cs | 104 +++++++++++++++++ 2 files changed, 214 insertions(+) diff --git a/docs/pipelines/resilience-pipeline-registry.md b/docs/pipelines/resilience-pipeline-registry.md index bde422d3cc4..6d9f62ebe4f 100644 --- a/docs/pipelines/resilience-pipeline-registry.md +++ b/docs/pipelines/resilience-pipeline-registry.md @@ -207,6 +207,116 @@ Both `AddReloadToken(...)` and `OnPipelineDisposed(...)` are used to implement t Resource disposal occurs when the registry is disposed of or when the pipeline undergoes changes due to [dynamic reloads](#dynamic-reloads). Upon disposal, all callbacks registered through the `OnPipelineDisposed` method are invoked. However, actual resource disposal is deferred until the pipeline completes all outgoing executions. It's vital to note that dispose callbacks are associated only with a specific instance of the pipeline. +If one is using custom rate limiters and wants to dispose them on pipeline reload or when a registry is disposed, then one should use the `OnPipelineDisposed` callback. + +Consider the following runnable example. It creates a registry with a concurrency strategy and a chained rate limiter strategy (which contains multiple rate limiters): + + +```cs +public static class Program +{ + public static void Main() + { + using var pra = new PipelineRegistryAdapter(); + pra.GetOrCreateResiliencePipeline("Pipeline foo", 1, 10, 100, 1000); + pra.GetOrCreateResiliencePipeline("Pipeline bar", 2, 20, 200, 2000); + } +} + +public sealed class PipelineRegistryAdapter : IDisposable +{ + private readonly ResiliencePipelineRegistry _resiliencePipelineRegistry = new(); + private bool _disposed; + + public void Dispose() + { + if (_disposed) + { + return; + } + + _resiliencePipelineRegistry.Dispose(); + _disposed = true; + } + + private static PartitionedRateLimiter CreateConcurrencyLimiter(string partitionKey, int permitLimit) => + PartitionedRateLimiter.Create(context => + RateLimitPartition.GetConcurrencyLimiter( + partitionKey: partitionKey, + factory: partitionKey => new ConcurrencyLimiterOptions { PermitLimit = permitLimit, QueueLimit = 0 })); + + private static PartitionedRateLimiter CreateFixedWindowLimiter(string partitionKey, int permitLimit, TimeSpan window) => + PartitionedRateLimiter.Create(context => + RateLimitPartition.GetFixedWindowLimiter( + partitionKey: partitionKey, + factory: partitionKey => new FixedWindowRateLimiterOptions { PermitLimit = permitLimit, QueueLimit = 0, Window = window })); + + public ResiliencePipeline GetOrCreateResiliencePipeline(string partitionKey, int maximumConcurrentThreads, int sendLimitPerSecond, int sendLimitPerHour, int sendLimitPerDay) + { + // return pipeline if exists + if (_resiliencePipelineRegistry.TryGetPipeline(partitionKey, out var pipeline)) + { + return pipeline; + } + + // else create pipeline with multiple strategies + var wasCreated = _resiliencePipelineRegistry.TryAddBuilder(partitionKey, (builder, context) => + { + PartitionedRateLimiter? threadLimiter = null; + PartitionedRateLimiter? requestLimiter = null; + + // outer strategy: limit threads + builder.AddRateLimiter(new RateLimiterStrategyOptions + { + RateLimiter = args => + { + threadLimiter = CreateConcurrencyLimiter(partitionKey, maximumConcurrentThreads); + return threadLimiter.AcquireAsync(args.Context, permitCount: 1, args.Context.CancellationToken); + } + }); + + // inner strategy: limit requests (by second, hour, day) + builder.AddRateLimiter(new RateLimiterStrategyOptions + { + RateLimiter = args => + { + PartitionedRateLimiter[] limiters = [ + CreateFixedWindowLimiter(partitionKey, sendLimitPerSecond, TimeSpan.FromSeconds(1)), + CreateFixedWindowLimiter(partitionKey, sendLimitPerHour, TimeSpan.FromHours(1)), + CreateFixedWindowLimiter(partitionKey, sendLimitPerDay, TimeSpan.FromDays(1)), + ]; + requestLimiter = PartitionedRateLimiter.CreateChained(limiters); + return requestLimiter.AcquireAsync(args.Context, permitCount: 1, args.Context.CancellationToken); + } + }); + + // unlike other strategies, rate limiters disposed manually + context.OnPipelineDisposed(() => + { + threadLimiter?.Dispose(); + requestLimiter?.Dispose(); + Console.WriteLine($"Disposed pipeline '{partitionKey}'"); + }); + + }); + + if (wasCreated) + { + Console.WriteLine($"Created pipeline '{partitionKey}'"); + } + else + { + throw new InvalidOperationException($"Failed to create pipeline '{partitionKey}'"); + } + + return _resiliencePipelineRegistry.GetPipeline(partitionKey); + } +} +``` + + +Notice how the rate limiters are disposed manually in the callback. + ## Complex registry keys Though the pipeline registry supports complex keys, we suggest you use them when defining pipelines with the [Dependency Injection](../advanced/dependency-injection.md) (DI) containers. For further information, see the [section on complex pipeline keys](../advanced/dependency-injection.md#complex-pipeline-keys). diff --git a/src/Snippets/Docs/ResiliencePipelineRegistry.cs b/src/Snippets/Docs/ResiliencePipelineRegistry.cs index c908e8d92f7..c2c63ae670f 100644 --- a/src/Snippets/Docs/ResiliencePipelineRegistry.cs +++ b/src/Snippets/Docs/ResiliencePipelineRegistry.cs @@ -1,4 +1,6 @@ using System.Net.Http; +using System.Threading.RateLimiting; +using Polly.RateLimiting; using Polly.Registry; using Polly.Retry; @@ -171,5 +173,107 @@ private static void RegisterCancellationSource(CancellationTokenSource cancellat { // Register the source } + + #region registry-ratelimiter-dispose + public static class Program + { + public static void Main() + { + using var pra = new PipelineRegistryAdapter(); + pra.GetOrCreateResiliencePipeline("Pipeline foo", 1, 10, 100, 1000); + pra.GetOrCreateResiliencePipeline("Pipeline bar", 2, 20, 200, 2000); + } + } + + public sealed class PipelineRegistryAdapter : IDisposable + { + private readonly ResiliencePipelineRegistry _resiliencePipelineRegistry = new(); + private bool _disposed; + + public void Dispose() + { + if (_disposed) + { + return; + } + + _resiliencePipelineRegistry.Dispose(); + _disposed = true; + } + + private static PartitionedRateLimiter CreateConcurrencyLimiter(string partitionKey, int permitLimit) => + PartitionedRateLimiter.Create(context => + RateLimitPartition.GetConcurrencyLimiter( + partitionKey: partitionKey, + factory: partitionKey => new ConcurrencyLimiterOptions { PermitLimit = permitLimit, QueueLimit = 0 })); + + private static PartitionedRateLimiter CreateFixedWindowLimiter(string partitionKey, int permitLimit, TimeSpan window) => + PartitionedRateLimiter.Create(context => + RateLimitPartition.GetFixedWindowLimiter( + partitionKey: partitionKey, + factory: partitionKey => new FixedWindowRateLimiterOptions { PermitLimit = permitLimit, QueueLimit = 0, Window = window })); + + public ResiliencePipeline GetOrCreateResiliencePipeline(string partitionKey, int maximumConcurrentThreads, int sendLimitPerSecond, int sendLimitPerHour, int sendLimitPerDay) + { + // return pipeline if exists + if (_resiliencePipelineRegistry.TryGetPipeline(partitionKey, out var pipeline)) + { + return pipeline; + } + + // else create pipeline with multiple strategies + var wasCreated = _resiliencePipelineRegistry.TryAddBuilder(partitionKey, (builder, context) => + { + PartitionedRateLimiter? threadLimiter = null; + PartitionedRateLimiter? requestLimiter = null; + + // outer strategy: limit threads + builder.AddRateLimiter(new RateLimiterStrategyOptions + { + RateLimiter = args => + { + threadLimiter = CreateConcurrencyLimiter(partitionKey, maximumConcurrentThreads); + return threadLimiter.AcquireAsync(args.Context, permitCount: 1, args.Context.CancellationToken); + } + }); + + // inner strategy: limit requests (by second, hour, day) + builder.AddRateLimiter(new RateLimiterStrategyOptions + { + RateLimiter = args => + { + PartitionedRateLimiter[] limiters = [ + CreateFixedWindowLimiter(partitionKey, sendLimitPerSecond, TimeSpan.FromSeconds(1)), + CreateFixedWindowLimiter(partitionKey, sendLimitPerHour, TimeSpan.FromHours(1)), + CreateFixedWindowLimiter(partitionKey, sendLimitPerDay, TimeSpan.FromDays(1)), + ]; + requestLimiter = PartitionedRateLimiter.CreateChained(limiters); + return requestLimiter.AcquireAsync(args.Context, permitCount: 1, args.Context.CancellationToken); + } + }); + + // unlike other strategies, rate limiters disposed manually + context.OnPipelineDisposed(() => + { + threadLimiter?.Dispose(); + requestLimiter?.Dispose(); + Console.WriteLine($"Disposed pipeline '{partitionKey}'"); + }); + + }); + + if (wasCreated) + { + Console.WriteLine($"Created pipeline '{partitionKey}'"); + } + else + { + throw new InvalidOperationException($"Failed to create pipeline '{partitionKey}'"); + } + + return _resiliencePipelineRegistry.GetPipeline(partitionKey); + } + } + #endregion } From 0af0ac85c8190471196256cf93f535f433652433 Mon Sep 17 00:00:00 2001 From: Peter Csala Date: Thu, 10 Apr 2025 10:28:39 +0200 Subject: [PATCH 2/7] Add missing header --- docs/pipelines/resilience-pipeline-registry.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/pipelines/resilience-pipeline-registry.md b/docs/pipelines/resilience-pipeline-registry.md index 6d9f62ebe4f..8d40292f524 100644 --- a/docs/pipelines/resilience-pipeline-registry.md +++ b/docs/pipelines/resilience-pipeline-registry.md @@ -207,6 +207,8 @@ Both `AddReloadToken(...)` and `OnPipelineDisposed(...)` are used to implement t Resource disposal occurs when the registry is disposed of or when the pipeline undergoes changes due to [dynamic reloads](#dynamic-reloads). Upon disposal, all callbacks registered through the `OnPipelineDisposed` method are invoked. However, actual resource disposal is deferred until the pipeline completes all outgoing executions. It's vital to note that dispose callbacks are associated only with a specific instance of the pipeline. +### Disposal of encapsulated rate limiters + If one is using custom rate limiters and wants to dispose them on pipeline reload or when a registry is disposed, then one should use the `OnPipelineDisposed` callback. Consider the following runnable example. It creates a registry with a concurrency strategy and a chained rate limiter strategy (which contains multiple rate limiters): From fe57f828201de676096d971b9d4118242b37bbaa Mon Sep 17 00:00:00 2001 From: Peter Csala Date: Thu, 10 Apr 2025 13:37:16 +0200 Subject: [PATCH 3/7] Add runnable to wordlist --- .github/wordlist.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/wordlist.txt b/.github/wordlist.txt index 835e9b94dff..fe5f2dacbd8 100644 --- a/.github/wordlist.txt +++ b/.github/wordlist.txt @@ -54,6 +54,7 @@ rethrow rethrows retryable reusability +runnable runtime saas sdk From 0a041ff4033371f1c20f54a2dcbcc06589622833 Mon Sep 17 00:00:00 2001 From: Peter Csala Date: Fri, 11 Apr 2025 08:38:11 +0200 Subject: [PATCH 4/7] Optimize sample code --- .../pipelines/resilience-pipeline-registry.md | 40 +++++-------------- .../Docs/ResiliencePipelineRegistry.cs | 38 ++++-------------- 2 files changed, 17 insertions(+), 61 deletions(-) diff --git a/docs/pipelines/resilience-pipeline-registry.md b/docs/pipelines/resilience-pipeline-registry.md index 8d40292f524..b6a12434fad 100644 --- a/docs/pipelines/resilience-pipeline-registry.md +++ b/docs/pipelines/resilience-pipeline-registry.md @@ -232,13 +232,11 @@ public sealed class PipelineRegistryAdapter : IDisposable public void Dispose() { - if (_disposed) + if (!_disposed) { - return; + _resiliencePipelineRegistry.Dispose(); + _disposed = true; } - - _resiliencePipelineRegistry.Dispose(); - _disposed = true; } private static PartitionedRateLimiter CreateConcurrencyLimiter(string partitionKey, int permitLimit) => @@ -255,14 +253,7 @@ public sealed class PipelineRegistryAdapter : IDisposable public ResiliencePipeline GetOrCreateResiliencePipeline(string partitionKey, int maximumConcurrentThreads, int sendLimitPerSecond, int sendLimitPerHour, int sendLimitPerDay) { - // return pipeline if exists - if (_resiliencePipelineRegistry.TryGetPipeline(partitionKey, out var pipeline)) - { - return pipeline; - } - - // else create pipeline with multiple strategies - var wasCreated = _resiliencePipelineRegistry.TryAddBuilder(partitionKey, (builder, context) => + return _resiliencePipelineRegistry.GetOrAddPipeline(partitionKey, (builder, context) => { PartitionedRateLimiter? threadLimiter = null; PartitionedRateLimiter? requestLimiter = null; @@ -283,10 +274,10 @@ public sealed class PipelineRegistryAdapter : IDisposable RateLimiter = args => { PartitionedRateLimiter[] limiters = [ - CreateFixedWindowLimiter(partitionKey, sendLimitPerSecond, TimeSpan.FromSeconds(1)), - CreateFixedWindowLimiter(partitionKey, sendLimitPerHour, TimeSpan.FromHours(1)), - CreateFixedWindowLimiter(partitionKey, sendLimitPerDay, TimeSpan.FromDays(1)), - ]; + CreateFixedWindowLimiter(partitionKey, sendLimitPerSecond, TimeSpan.FromSeconds(1)), + CreateFixedWindowLimiter(partitionKey, sendLimitPerHour, TimeSpan.FromHours(1)), + CreateFixedWindowLimiter(partitionKey, sendLimitPerDay, TimeSpan.FromDays(1)), + ]; requestLimiter = PartitionedRateLimiter.CreateChained(limiters); return requestLimiter.AcquireAsync(args.Context, permitCount: 1, args.Context.CancellationToken); } @@ -297,27 +288,14 @@ public sealed class PipelineRegistryAdapter : IDisposable { threadLimiter?.Dispose(); requestLimiter?.Dispose(); - Console.WriteLine($"Disposed pipeline '{partitionKey}'"); }); - }); - - if (wasCreated) - { - Console.WriteLine($"Created pipeline '{partitionKey}'"); - } - else - { - throw new InvalidOperationException($"Failed to create pipeline '{partitionKey}'"); - } - - return _resiliencePipelineRegistry.GetPipeline(partitionKey); } } ``` -Notice how the rate limiters are disposed manually in the callback. +Notice how the rate limiters are disposed manually in the `OnPipelineDisposed` callback. ## Complex registry keys diff --git a/src/Snippets/Docs/ResiliencePipelineRegistry.cs b/src/Snippets/Docs/ResiliencePipelineRegistry.cs index c2c63ae670f..e00727dea35 100644 --- a/src/Snippets/Docs/ResiliencePipelineRegistry.cs +++ b/src/Snippets/Docs/ResiliencePipelineRegistry.cs @@ -192,13 +192,11 @@ public sealed class PipelineRegistryAdapter : IDisposable public void Dispose() { - if (_disposed) + if (!_disposed) { - return; + _resiliencePipelineRegistry.Dispose(); + _disposed = true; } - - _resiliencePipelineRegistry.Dispose(); - _disposed = true; } private static PartitionedRateLimiter CreateConcurrencyLimiter(string partitionKey, int permitLimit) => @@ -215,14 +213,7 @@ private static PartitionedRateLimiter CreateFixedWindowLimite public ResiliencePipeline GetOrCreateResiliencePipeline(string partitionKey, int maximumConcurrentThreads, int sendLimitPerSecond, int sendLimitPerHour, int sendLimitPerDay) { - // return pipeline if exists - if (_resiliencePipelineRegistry.TryGetPipeline(partitionKey, out var pipeline)) - { - return pipeline; - } - - // else create pipeline with multiple strategies - var wasCreated = _resiliencePipelineRegistry.TryAddBuilder(partitionKey, (builder, context) => + return _resiliencePipelineRegistry.GetOrAddPipeline(partitionKey, (builder, context) => { PartitionedRateLimiter? threadLimiter = null; PartitionedRateLimiter? requestLimiter = null; @@ -243,10 +234,10 @@ public ResiliencePipeline GetOrCreateResiliencePipeline(string partitionKey, int RateLimiter = args => { PartitionedRateLimiter[] limiters = [ - CreateFixedWindowLimiter(partitionKey, sendLimitPerSecond, TimeSpan.FromSeconds(1)), - CreateFixedWindowLimiter(partitionKey, sendLimitPerHour, TimeSpan.FromHours(1)), - CreateFixedWindowLimiter(partitionKey, sendLimitPerDay, TimeSpan.FromDays(1)), - ]; + CreateFixedWindowLimiter(partitionKey, sendLimitPerSecond, TimeSpan.FromSeconds(1)), + CreateFixedWindowLimiter(partitionKey, sendLimitPerHour, TimeSpan.FromHours(1)), + CreateFixedWindowLimiter(partitionKey, sendLimitPerDay, TimeSpan.FromDays(1)), + ]; requestLimiter = PartitionedRateLimiter.CreateChained(limiters); return requestLimiter.AcquireAsync(args.Context, permitCount: 1, args.Context.CancellationToken); } @@ -257,21 +248,8 @@ public ResiliencePipeline GetOrCreateResiliencePipeline(string partitionKey, int { threadLimiter?.Dispose(); requestLimiter?.Dispose(); - Console.WriteLine($"Disposed pipeline '{partitionKey}'"); }); - }); - - if (wasCreated) - { - Console.WriteLine($"Created pipeline '{partitionKey}'"); - } - else - { - throw new InvalidOperationException($"Failed to create pipeline '{partitionKey}'"); - } - - return _resiliencePipelineRegistry.GetPipeline(partitionKey); } } #endregion From 664b7fd87727c68ec6158e1afbc7568ea36c8bf9 Mon Sep 17 00:00:00 2001 From: Peter Csala Date: Fri, 11 Apr 2025 08:39:43 +0200 Subject: [PATCH 5/7] Optimize sample code --- docs/pipelines/resilience-pipeline-registry.md | 6 +++--- src/Snippets/Docs/ResiliencePipelineRegistry.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/pipelines/resilience-pipeline-registry.md b/docs/pipelines/resilience-pipeline-registry.md index b6a12434fad..b6ce9e5f60b 100644 --- a/docs/pipelines/resilience-pipeline-registry.md +++ b/docs/pipelines/resilience-pipeline-registry.md @@ -219,9 +219,9 @@ public static class Program { public static void Main() { - using var pra = new PipelineRegistryAdapter(); - pra.GetOrCreateResiliencePipeline("Pipeline foo", 1, 10, 100, 1000); - pra.GetOrCreateResiliencePipeline("Pipeline bar", 2, 20, 200, 2000); + using var registryAdapter = new PipelineRegistryAdapter(); + registryAdapter.GetOrCreateResiliencePipeline("Pipeline foo", 1, 10, 100, 1000); + registryAdapter.GetOrCreateResiliencePipeline("Pipeline bar", 2, 20, 200, 2000); } } diff --git a/src/Snippets/Docs/ResiliencePipelineRegistry.cs b/src/Snippets/Docs/ResiliencePipelineRegistry.cs index e00727dea35..20999de5aa1 100644 --- a/src/Snippets/Docs/ResiliencePipelineRegistry.cs +++ b/src/Snippets/Docs/ResiliencePipelineRegistry.cs @@ -179,9 +179,9 @@ public static class Program { public static void Main() { - using var pra = new PipelineRegistryAdapter(); - pra.GetOrCreateResiliencePipeline("Pipeline foo", 1, 10, 100, 1000); - pra.GetOrCreateResiliencePipeline("Pipeline bar", 2, 20, 200, 2000); + using var registryAdapter = new PipelineRegistryAdapter(); + registryAdapter.GetOrCreateResiliencePipeline("Pipeline foo", 1, 10, 100, 1000); + registryAdapter.GetOrCreateResiliencePipeline("Pipeline bar", 2, 20, 200, 2000); } } From c7e971f172432ff187a96c04a9b3145bbfadb44c Mon Sep 17 00:00:00 2001 From: peter-csala <57183693+peter-csala@users.noreply.github.com> Date: Fri, 11 Apr 2025 13:04:27 +0200 Subject: [PATCH 6/7] Apply suggestions from code review Co-authored-by: Martin Costello --- docs/pipelines/resilience-pipeline-registry.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pipelines/resilience-pipeline-registry.md b/docs/pipelines/resilience-pipeline-registry.md index b6ce9e5f60b..f9b8789cd1e 100644 --- a/docs/pipelines/resilience-pipeline-registry.md +++ b/docs/pipelines/resilience-pipeline-registry.md @@ -209,7 +209,7 @@ Resource disposal occurs when the registry is disposed of or when the pipeline u ### Disposal of encapsulated rate limiters -If one is using custom rate limiters and wants to dispose them on pipeline reload or when a registry is disposed, then one should use the `OnPipelineDisposed` callback. +If you are using custom rate limiters and want to dispose them on pipeline reload or when a registry is disposed, then you should use the `OnPipelineDisposed` callback. Consider the following runnable example. It creates a registry with a concurrency strategy and a chained rate limiter strategy (which contains multiple rate limiters): From 4ee33bf123c273a83b80a5eb7e6396672670eeff Mon Sep 17 00:00:00 2001 From: Peter Csala Date: Fri, 11 Apr 2025 13:06:47 +0200 Subject: [PATCH 7/7] Fix indentation --- docs/pipelines/resilience-pipeline-registry.md | 12 ++++++------ src/Snippets/Docs/ResiliencePipelineRegistry.cs | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/pipelines/resilience-pipeline-registry.md b/docs/pipelines/resilience-pipeline-registry.md index f9b8789cd1e..4f9a1e143e1 100644 --- a/docs/pipelines/resilience-pipeline-registry.md +++ b/docs/pipelines/resilience-pipeline-registry.md @@ -241,15 +241,15 @@ public sealed class PipelineRegistryAdapter : IDisposable private static PartitionedRateLimiter CreateConcurrencyLimiter(string partitionKey, int permitLimit) => PartitionedRateLimiter.Create(context => - RateLimitPartition.GetConcurrencyLimiter( - partitionKey: partitionKey, - factory: partitionKey => new ConcurrencyLimiterOptions { PermitLimit = permitLimit, QueueLimit = 0 })); + RateLimitPartition.GetConcurrencyLimiter( + partitionKey: partitionKey, + factory: partitionKey => new ConcurrencyLimiterOptions { PermitLimit = permitLimit, QueueLimit = 0 })); private static PartitionedRateLimiter CreateFixedWindowLimiter(string partitionKey, int permitLimit, TimeSpan window) => PartitionedRateLimiter.Create(context => - RateLimitPartition.GetFixedWindowLimiter( - partitionKey: partitionKey, - factory: partitionKey => new FixedWindowRateLimiterOptions { PermitLimit = permitLimit, QueueLimit = 0, Window = window })); + RateLimitPartition.GetFixedWindowLimiter( + partitionKey: partitionKey, + factory: partitionKey => new FixedWindowRateLimiterOptions { PermitLimit = permitLimit, QueueLimit = 0, Window = window })); public ResiliencePipeline GetOrCreateResiliencePipeline(string partitionKey, int maximumConcurrentThreads, int sendLimitPerSecond, int sendLimitPerHour, int sendLimitPerDay) { diff --git a/src/Snippets/Docs/ResiliencePipelineRegistry.cs b/src/Snippets/Docs/ResiliencePipelineRegistry.cs index 20999de5aa1..cefcf2893b8 100644 --- a/src/Snippets/Docs/ResiliencePipelineRegistry.cs +++ b/src/Snippets/Docs/ResiliencePipelineRegistry.cs @@ -201,15 +201,15 @@ public void Dispose() private static PartitionedRateLimiter CreateConcurrencyLimiter(string partitionKey, int permitLimit) => PartitionedRateLimiter.Create(context => - RateLimitPartition.GetConcurrencyLimiter( - partitionKey: partitionKey, - factory: partitionKey => new ConcurrencyLimiterOptions { PermitLimit = permitLimit, QueueLimit = 0 })); + RateLimitPartition.GetConcurrencyLimiter( + partitionKey: partitionKey, + factory: partitionKey => new ConcurrencyLimiterOptions { PermitLimit = permitLimit, QueueLimit = 0 })); private static PartitionedRateLimiter CreateFixedWindowLimiter(string partitionKey, int permitLimit, TimeSpan window) => PartitionedRateLimiter.Create(context => - RateLimitPartition.GetFixedWindowLimiter( - partitionKey: partitionKey, - factory: partitionKey => new FixedWindowRateLimiterOptions { PermitLimit = permitLimit, QueueLimit = 0, Window = window })); + RateLimitPartition.GetFixedWindowLimiter( + partitionKey: partitionKey, + factory: partitionKey => new FixedWindowRateLimiterOptions { PermitLimit = permitLimit, QueueLimit = 0, Window = window })); public ResiliencePipeline GetOrCreateResiliencePipeline(string partitionKey, int maximumConcurrentThreads, int sendLimitPerSecond, int sendLimitPerHour, int sendLimitPerDay) {