Repository navigation
Improve pipeline execution performance - #3287
Merged
Merged
Conversation
- Avoid generic virtual method dispatch between delegating components in the pipeline chain. - Execute non-generic strategies directly rather than through their bridge component. - Skip the circuit breaker lock on pre-execution when the circuit is closed.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3287 +/- ##
=======================================
Coverage 98.02% 98.03%
=======================================
Files 321 321
Lines 7103 7131 +28
Branches 1089 1099 +10
=======================================
+ Hits 6963 6991 +28
Misses 98 98
Partials 42 42
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. |
Add more test coverage for `DelegatingComponent`.
Add test for missing coverage (hopefully).
martincostello
marked this pull request as ready for review
October 25, 2026 15:34
martincostello
enabled auto-merge (squash)
October 25, 2026 15:35
This was referenced Oct 26, 2026
SonnyRR
pushed a commit
to SonnyRR/stacked-deck-templates
that referenced
this pull request
Oct 26, 2026
Updated [Polly](https://github.com/App-vNext/Polly) from 8.8.0 to 8.8.1. <details> <summary>Release notes</summary> _Sourced from [Polly's releases](https://github.com/App-vNext/Polly/releases)._ ## 8.8.1 ## Highlights * Prevent the circuit breaker from capturing the ambient ExecutionContext by @Nacl-HCl in App-vNext/Polly#3284 * Improve pipeline execution performance by @martincostello in App-vNext/Polly#3287 * Fix cleanup of discarded registry pipelines by @harshvarudkar in App-vNext/Polly#3288 ## What's Changed * Add Copilot code review skill by @martincostello in App-vNext/Polly#3247 * Fix benchmarks by @martincostello in App-vNext/Polly#3248 * Update PR template by @martincostello in App-vNext/Polly#3249 * Update README with OSMF details for Polly by @joelhulen in App-vNext/Polly#3263 * Fix lint warnings by @martincostello in App-vNext/Polly#3264 * Update FUNDING by @martincostello in App-vNext/Polly#3258 * Remove obsolete packages references by @martincostello in App-vNext/Polly#3267 * Validate packages in parallel by @martincostello in App-vNext/Polly#3283 * .NET 11 preparation by @martincostello in App-vNext/Polly#3285 ## New Contributors * @Nacl-HCl made their first contribution in App-vNext/Polly#3284 * @harshvarudkar made their first contribution in App-vNext/Polly#3288 **Full Changelog**: App-vNext/Polly@8.8.0...8.8.1 Commits viewable in [compare view](App-vNext/Polly@8.8.0...8.8.1). </details> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This was referenced Oct 26, 2026
This was referenced Nov 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pull Request
The issue or feature being addressed
Improve the performance of the hot path when executing resilience pipelines.
Details on the issue fix or feature implementation
Profiling a pipeline of five strategies showed that a significant part of the time Polly itself spends per execution is generic virtual method (GVM) dispatch between the components of the pipeline chain (each hop costs roughly 7 ns and there were up to three per strategy), plus the circuit breaker taking its lock twice per execution.
DelegatingComponentexposes a non-virtualExecute()method that the composite root and the previous link in the chain call directly when the next component is aDelegatingComponent.BridgeComponent, which is a trivial forwarder.DelegatingComponentunwraps the strategy at construction time.OnActionPreExecuteAsync(), so a volatile read is sufficient and a concurrent transition after the read is indistinguishable from one that happens right after the lock would have been released.There are no public API changes and no changes to allocations.
Benchmarks
Benchmarks were run against
main(3ab3ec0) in a separate worktree and against this branch, pinned to the performance cores (--affinity 4095) with two interleaved rounds per side. The figures below are the mean of the two rounds, andRatiois branch/main.PipelineBenchmark.ExecutePipeline_V8PipelineBenchmark.ExecutePipeline_V8PipelineBenchmark.ExecutePipeline_V8PipelineBenchmark.ExecutePipeline_V8MultipleStrategiesBenchmark.ExecuteStrategyPipeline_Generic_V8MultipleStrategiesBenchmark.ExecuteStrategyPipeline_NonGeneric_V8CircuitBreakerBenchmark.ExecuteCircuitBreaker_V8TimeoutBenchmark.ExecuteTimeout_V8RetryBenchmark.ExecuteRetry_V8The retry strategy is generic and is not affected by these changes, so
RetryBenchmarkacts as a control for run-to-run noise on this machine (its two rounds were 110.0/101.5 ns onmainand 99.1/99.2 ns on this branch). The 40 B allocated byMultipleStrategiesBenchmarkis theConcurrencyLeasefromSystem.Threading.RateLimitingand is unchanged.An attempt to skip the retry strategy's
GetTimestamp()calls when neither telemetry norOnRetrycan observe the duration was measured at ~1.7 ns and discarded as noise.Confirm the following
Note
This PR and its description were generated with Claude Code.