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

Cache tags from DbCommand #774

Merged
merged 5 commits into from
Jul 2, 2020
Merged

Cache tags from DbCommand #774

merged 5 commits into from
Jul 2, 2020

Conversation

kevingosse
Copy link
Collaborator

Parsing the connection string is a costly operation that we do at every query, even though we can reasonably expect users to have a limited set of connection strings.

Benchmark:

// With connection string: Server=myServerName,myPortNumber;Database=myDataBase;User Id=myUsername;Password=myPassword;
SpanExtensions.AddTagsFromDbCommand(span, dbCommand);
BenchmarkDotNet=v0.12.0, OS=Windows 10.0.19041
Intel Core i7-9750H CPU 2.60GHz, 1 CPU, 12 logical and 6 physical cores
  [Host]     : .NET Framework 4.8 (4.8.4180.0), X64 RyuJIT
  Job-VTGAFA : .NET Framework 4.8 (4.8.4180.0), X64 RyuJIT
  Job-MNUBEA : .NET Core 3.1.5 (CoreCLR 4.700.20.26901, CoreFX 4.700.20.27001), X64 RyuJIT

Method Runtime Mean Error StdDev Ratio Gen 0 Gen 1 Gen 2 Allocated
Old .NET 4.7.2 4,336.9 ns 49.97 ns 44.30 ns 1.00 0.3433 - - 2182 B
New .NET 4.7.2 242.4 ns 1.54 ns 1.45 ns 0.06 - - - -
Old .NET Core 3.1 3,495.5 ns 12.84 ns 10.02 ns 1.00 0.3510 - - 2208 B
New .NET Core 3.1 236.4 ns 3.97 ns 3.52 ns 0.07 - - - -

@kevingosse
Copy link
Collaborator Author

2 things to take care of before merging:

  • This would cause a memory leak if there's some random part in a connection string. I can't think of a situation where that would happen, but I'm going to do some research to make sure
  • I'll add a unit test

@kevingosse
Copy link
Collaborator Author

I could think of a situation where we would have an infinite number of connection strings: if the user opens arbitrary Excel files with ADO.NET. This is very unlikely but still possible, so I added a fail-safe mechanism. There's no noticeable impact on the benchmark (expected, since the fast path doesn't change).

BenchmarkDotNet=v0.12.0, OS=Windows 10.0.19041
Intel Core i7-9750H CPU 2.60GHz, 1 CPU, 12 logical and 6 physical cores
  [Host]     : .NET Framework 4.8 (4.8.4180.0), X64 RyuJIT
  Job-VTGAFA : .NET Framework 4.8 (4.8.4180.0), X64 RyuJIT
  Job-MNUBEA : .NET Core 3.1.5 (CoreCLR 4.700.20.26901, CoreFX 4.700.20.27001), X64 RyuJIT

Method Runtime Mean Error StdDev Ratio Gen 0 Gen 1 Gen 2 Allocated
Old .NET 4.7.2 4,464.6 ns 72.36 ns 67.69 ns 1.00 0.3433 - - 2182 B
New .NET 4.7.2 244.2 ns 4.59 ns 4.50 ns 0.05 - - - -
Old .NET Core 3.1 3,711.8 ns 73.20 ns 78.33 ns 1.00 0.3510 - - 2208 B
New .NET Core 3.1 242.2 ns 2.32 ns 1.94 ns 0.07 - - - -

@kevingosse kevingosse marked this pull request as ready for review July 1, 2020 11:16
@kevingosse kevingosse requested a review from a team as a code owner July 1, 2020 11:16
Copy link
Member

@lucaspimentel lucaspimentel left a comment

Choose a reason for hiding this comment

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

LGTM. Left a non-blocking question to satisfy my curiosity.

{
// Populating the cache. This path should be hit only during application warmup
// ReSharper disable once ConvertClosureToMethodGroup -- Lambdas are cached by the compiler
return cache.GetOrAdd(connectionString, cs => ExtractTagsFromConnectionString(cs));
Copy link
Member

Choose a reason for hiding this comment

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

Just curious, is the "Resharper disable" comment just to avoid writing it like this? If so, is there a performance reason for this, or just cosmetic?

Suggested change
return cache.GetOrAdd(connectionString, cs => ExtractTagsFromConnectionString(cs));
return cache.GetOrAdd(connectionString, ExtractTagsFromConnectionString);

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, the comment is there to prevent Resharper from rewriting into a method group. The delegate is cached when using lambdas but not when using method groups, so that would cause an additional heap allocation at each call.
dotnet/roslyn#5835

Copy link
Member

Choose a reason for hiding this comment

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

TIL. Thanks!

@kevingosse kevingosse merged commit 382ea15 into master Jul 2, 2020
@kevingosse kevingosse deleted the kevin/dbcommandtags branch July 2, 2020 08:52
@zacharycmontoya zacharycmontoya added this to the 1.18.2 milestone Jul 9, 2020
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.

None yet

3 participants