-
Notifications
You must be signed in to change notification settings - Fork 335
Fix | Scope configurable retry logic assembly resolution to opt-in callers #4547
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
priyankatiwari08
wants to merge
8
commits into
main
Choose a base branch
from
dev/prtiwar/fix-configurable-retry-assembly-resolution
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+547
−46
Open
Changes from 1 commit
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
9754eb0
Scope configurable retry logic assembly resolution
priyankatiwari08 6476d99
Test | Address review feedback on retry logic assembly probing tests
priyankatiwari08 4b8de9f
Test | Cover the successful retry logic type resolution path
priyankatiwari08 7ce702b
Test | Widen probe file cleanup to non-IO failures
priyankatiwari08 efc0d7a
Keep retry logic assembly probing active during provider construction
priyankatiwari08 bedc369
Treat a whitespace-only retryLogicType as not configured
priyankatiwari08 740223f
Remove the UseLegacyRetryLogicAssemblyResolution app context switch
priyankatiwari08 42319f2
Refactor retry assembly resolution subscription
priyankatiwari08 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
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
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
141 changes: 141 additions & 0 deletions
141
...SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlConfigurableRetryLogicLoaderTest.cs
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
| // See the LICENSE file in the project root for more information. | ||
|
|
||
| #if NET | ||
|
|
||
| using System; | ||
| using System.Linq; | ||
| using System.Reflection; | ||
| using System.Runtime.Loader; | ||
| using Xunit; | ||
|
|
||
| namespace Microsoft.Data.SqlClient.UnitTests; | ||
|
|
||
| /// <summary> | ||
| /// Unit tests validating that <see cref="SqlConfigurableRetryLogicLoader"/> never leaves a | ||
| /// process-wide assembly resolving handler attached to | ||
| /// <see cref="AssemblyLoadContext.Default"/>. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// A handler left attached there participates in resolution of every assembly the host | ||
| /// application fails to find, not just the retry logic assembly the loader was interested in. | ||
| /// That silently changes assembly loading behaviour for code that never opted in to this | ||
| /// feature, and can serve unrelated assemblies out of this component's probing directory. | ||
| /// </remarks> | ||
| public class SqlConfigurableRetryLogicLoaderTest | ||
| { | ||
| /// <summary> | ||
| /// The default code path: no configuration at all. The loader must not subscribe to the | ||
| /// default load context. | ||
| /// </summary> | ||
| [Fact] | ||
| public void Constructor_WithNoConfiguration_DoesNotSubscribeToDefaultLoadContext() | ||
| { | ||
| _ = new SqlConfigurableRetryLogicLoader(null, null); | ||
|
|
||
| AssertNoLoaderResolvingHandlerAttached(); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// A configuration that supplies only a retry method, which is the documented way to select | ||
| /// one of the built-in retry providers. No custom type is being requested, so no assembly | ||
| /// resolution is required and no handler may be subscribed - not even transiently. | ||
| /// </summary> | ||
| [Theory] | ||
| [InlineData(null)] | ||
| [InlineData("")] | ||
| public void Constructor_WithoutRetryLogicType_DoesNotSubscribeToDefaultLoadContext(string? retryLogicType) | ||
| { | ||
| TestRetryConnectionSection section = CreateSection(retryLogicType); | ||
|
|
||
| SqlConfigurableRetryLogicLoader loader = new(section, null); | ||
|
|
||
| // The built-in factory still resolves the requested method. | ||
| Assert.NotNull(loader.ConnectionProvider); | ||
| AssertNoLoaderResolvingHandlerAttached(); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// A configuration that requests a custom retry logic type legitimately needs assembly | ||
| /// resolution, but the handler must be removed again once type resolution has finished. | ||
| /// </summary> | ||
| [Fact] | ||
| public void Constructor_WithUnresolvableRetryLogicType_DoesNotLeaveHandlerSubscribed() | ||
| { | ||
| TestRetryConnectionSection section = | ||
| CreateSection("Some.Namespace.NoSuchType, Some.Assembly.That.Does.Not.Exist"); | ||
|
|
||
| SqlConfigurableRetryLogicLoader loader = new(section, null); | ||
|
|
||
| // Resolution fails and falls back to the built-in factory rather than throwing. | ||
| Assert.NotNull(loader.ConnectionProvider); | ||
| AssertNoLoaderResolvingHandlerAttached(); | ||
| } | ||
|
|
||
| private static TestRetryConnectionSection CreateSection(string? retryLogicType) => | ||
| new() | ||
| { | ||
| RetryLogicType = retryLogicType!, | ||
| RetryMethod = nameof(SqlConfigurableRetryFactory.CreateFixedRetryProvider), | ||
| NumberOfTries = 2, | ||
| DeltaTime = TimeSpan.FromSeconds(1), | ||
| MinTimeInterval = TimeSpan.Zero, | ||
| MaxTimeInterval = TimeSpan.FromSeconds(10), | ||
| }; | ||
|
|
||
| /// <summary> | ||
| /// Asserts that no delegate declared by <see cref="SqlConfigurableRetryLogicLoader"/> is | ||
| /// subscribed to the <see cref="AssemblyLoadContext.Default"/> resolving event. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// The event exposes only add/remove accessors, so its backing field is read reflectively. | ||
| /// If the runtime ever renames that field this assertion fails loudly rather than silently | ||
| /// passing, which is the desired behaviour for a regression test. | ||
| /// </remarks> | ||
| private static void AssertNoLoaderResolvingHandlerAttached() | ||
| { | ||
| FieldInfo? resolvingField = typeof(AssemblyLoadContext).GetField( | ||
|
priyankatiwari08 marked this conversation as resolved.
Outdated
|
||
| "_resolving", | ||
| BindingFlags.Instance | BindingFlags.NonPublic); | ||
|
|
||
| Assert.True( | ||
| resolvingField is not null, | ||
| "Could not locate the backing field of AssemblyLoadContext.Resolving. This test needs " + | ||
| "updating for the current runtime."); | ||
|
|
||
| Delegate? resolving = (Delegate?)resolvingField!.GetValue(AssemblyLoadContext.Default); | ||
|
|
||
| string[] offenders = resolving is null | ||
| ? [] | ||
| : resolving.GetInvocationList() | ||
| .Where(handler => handler.Method.DeclaringType == typeof(SqlConfigurableRetryLogicLoader)) | ||
| .Select(handler => handler.Method.Name) | ||
| .ToArray(); | ||
|
|
||
| Assert.True( | ||
| offenders.Length == 0, | ||
| $"SqlConfigurableRetryLogicLoader left {offenders.Length} handler(s) subscribed to " + | ||
| $"AssemblyLoadContext.Default.Resolving: {string.Join(", ", offenders)}. A process-wide " + | ||
| "handler changes assembly resolution for the entire application."); | ||
| } | ||
|
priyankatiwari08 marked this conversation as resolved.
|
||
|
|
||
| private sealed class TestRetryConnectionSection : ISqlConfigurableRetryConnectionSection | ||
| { | ||
| public TimeSpan DeltaTime { get; set; } | ||
|
|
||
| public TimeSpan MaxTimeInterval { get; set; } | ||
|
|
||
| public TimeSpan MinTimeInterval { get; set; } | ||
|
|
||
| public int NumberOfTries { get; set; } | ||
|
|
||
| public string RetryLogicType { get; set; } = string.Empty; | ||
|
|
||
| public string RetryMethod { get; set; } = string.Empty; | ||
|
|
||
| public string TransientErrors { get; set; } = string.Empty; | ||
| } | ||
| } | ||
|
|
||
| #endif | ||
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.