Skip to content
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

Fix ITelemetryModule singleton registration to support presence of keyed services #2908

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

CodeBlanch
Copy link

@CodeBlanch CodeBlanch commented Oct 22, 2024

Fixes #2879
Relates to dotnet/extensions#5222

Changes

  • Remove AddSingletonIfNotExists custom implementation for detection of registered services and call TryAddEnumerable instead because it understands keyed services added in 8.0.0

Comment on lines -16 to -21
/// <summary>
/// When working with IServiceCossection, it can store three types of Implementations:
/// ImplementationFactory, ImplementationInstance, and ImplementationType.
/// We want to be able to add a Singleton but only if a user hasn't already done so.
/// This class is to test all the various edge cases.
/// </summary>
Copy link
Author

@CodeBlanch CodeBlanch Oct 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue is as of 8.0.0 this summary is false.

There are new "keyed" forms of the possible registration types:

image

AddSingletonIfNotExists could be made smarter to understand the new shape. But that would require an 8.0.0 bump for M.E.DI/.A in Microsoft.ApplicationInsights.AspNetCore.

Easier IMO to just call into TryAddEnumerable and let it sort out what to do!

@CodeBlanch
Copy link
Author

Sorry about the whitespace on diff! Blame VS 🤣

@@ -37,6 +37,25 @@ namespace Microsoft.Extensions.DependencyInjection.Test

public class AddApplicationInsightsTelemetryTests : BaseTestClass
{
[Fact]
public static void TelemetryModuleResolvableWhenKeyedServiceRegistered()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test appears to pass even without your change.
I copied it locally and ran it in main branch.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. It looks like without the fix, worker service can't find any registered ITelemetryHttpModule and the test fails. AspNetCore though finds RequestTrackingTelemetryModule registered so the test passes.

With the fix in place there are actually 7 ITelemetryHttpModules registered. I updated the tests to validate all 7 are found when keyed service is added to the mix.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just pushed updates to the test to improve them further. Added validation that a) DiagnosticsTelemetryModule is present in the final list and b) if the user supplied one we don't end up with 2.

@xiang17
Copy link
Member

xiang17 commented Oct 22, 2024

The bug report is for .NET 8, but does it apply for other .NET versions and .NET Framework?

@CodeBlanch
Copy link
Author

The bug report is for .NET 8, but does it apply for other .NET versions and .NET Framework?

Could impact all runtimes. All user needs to do is take a dependency on something which a) brings in M.E.DI 8.0.0+ and b) registers a keyed service. In the case of the bug report, it is actually Polly registering the keyed service.

Copy link
Member

@TimothyMothra TimothyMothra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Member

@rajkumar-rangaraj rajkumar-rangaraj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, Thanks for fixing this quicker. Could you please add a changelog entry.

@CodeBlanch CodeBlanch marked this pull request as draft October 23, 2024 18:41
@CodeBlanch
Copy link
Author

@TimothyMothra @rajkumar-rangaraj I pushed a change to call TryAddEnumerable instead of TryAddSingleton. Please take another look.

I was looking through the history and trying to figure out why we had AddSingletonIfNotExists at all when there are other spots calling TryAddSingleton. What I came to realize is we want multiple ITelemetryModules to be registered. But we only want 1 DiagnosticsTelemetryModule. TryAddSingleton<ITelemetryModule, DiagnosticsTelemetryModule>() won't add DiagnosticsTelemetryModule if some other ITelemetryModule is registered. That is incorrect! TryAddEnumerable is what we want here. What it will do is only add the DiagnosticsTelemetryModule registration if it hasn't already been registered.

So my guess is we had AddSingletonIfNotExists because TryAddSingleton didn't work and we didn't realize TryAddEnumerable was the thing to use 😄

@CodeBlanch CodeBlanch marked this pull request as ready for review October 23, 2024 19:02
@@ -332,7 +312,7 @@ private static void AddCommonInitializers(IServiceCollection services)
private static void AddCommonTelemetryModules(IServiceCollection services)
{
// Previously users were encouraged to manually add the DiagnosticsTelemetryModule.
services.AddSingletonIfNotExists<ITelemetryModule, DiagnosticsTelemetryModule>();
services.TryAddEnumerable(ServiceDescriptor.Singleton<ITelemetryModule, DiagnosticsTelemetryModule>());
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TimothyMothra FYI I didn't make any other changes here but really all of the services.AddSingleton<ITelemetryModule, ...>(); calls should be TryAddEnumerable calls. If user only calls registration once everything will be fine. But if they call one of the "Add" extensions more than once, they'll get duplicates of all of these modules. Typically "Add" extensions should be safe to be called more than once which is accomplished by calling TryAddSingleton \ TryAddEnumerable 🤷

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't remember the full history, but I reviewed all my previous commit comments.

It looks like I was trying to add safety checks for every possible way that a user could have added the DiagnosticsTelemetryModule (link 1)(link 2). The TryAdd is substantially better :)

Thank you

@TimothyMothra
Copy link
Member

I pinged @cijothomas offline to also take a look. He was involved in the previous PR #1886

@pcn-michaelulloa
Copy link

Any updates on this? 👀👀

Comment on lines 23 to 26
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="2.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.1.0" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks as though it introduces version-mismatch. It's worth considering either using 2.1.0 across the board or bump all to 8.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

App Insights breaks when a keyed service is added
6 participants