-
Notifications
You must be signed in to change notification settings - Fork 151
[2.0] Add modified WCF CallTarget instrumentation via opt-in environment variable #2041
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
[2.0] Add modified WCF CallTarget instrumentation via opt-in environment variable #2041
Conversation
This comment has been minimized.
This comment has been minimized.
tracer/build/_build/PrepareRelease/GenerateIntegrationDefinitions.cs
Outdated
Show resolved
Hide resolved
tracer/build/_build/PrepareRelease/GenerateIntegrationDefinitions.cs
Outdated
Show resolved
Hide resolved
| int numArgs = useCustomArgumentsTargetMethodArguments | ||
| ? (int) methodArgumentsToLoad.size() | ||
| : caller->method_signature.NumberOfArguments(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just speculating, but do we expect any performance impact (hopefully improvement) from not passing all the arguments to our integrations? e.g. should we adopt this as "standard" for our integrations, where we only pass the arguments we strictly need?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure how much we would save from avoiding unnecessary ldarg IL instructions, but maybe it could reduce the amount of generic types are created at runtime? This would be good to benchmark at some point
|
|
||
| exceptionsSeen += await Invoke_ServerSyncAdd_Endpoints(calculator); | ||
| exceptionsSeen += await Invoke_ServerTaskAdd_Endpoints(calculator); | ||
| // exceptionsSeen += await Invoke_ServerAsyncAdd_Endpoints(calculator); // Ignore the async server endpoint because it is flaky in CI right now |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have any idea why it's flaky?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No I don't understand yet why it's flaky. When running this locally, I got consistent results run after run where all 3 spans were created. In CI, there'd be inconsistent results where 1-3 could be missing
This comment has been minimized.
This comment has been minimized.
|
We rebased |
59aa8f2 to
3724bd9
Compare
This comment has been minimized.
This comment has been minimized.
Additional changes include: - Move CreateScope method from ChannelHandlerIntegration.cs to WcfCommon.cs Strip out all CallTarget changes and use the new CallTarget ByRef instrumentation to instrument WCF methods which have out parameters (thanks Tony!)
3724bd9 to
907d39e
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
tonyredondo
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! nit: any reason to not use ducktyping in this integration?
tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Wcf/SyncMethodInvokerIntegration.cs
Outdated
Show resolved
Hide resolved
I wanted to limit the changes I made to just the integration points instead of doing a refactor of the WCF code, in order to minimize risk. The WCF code could definitely benefit from ducktyping but I'm thinking we can do that separately |
| object requestHeaders = requestMessage.GetProperty<object>("Headers").GetValueOrDefault(); | ||
| string action = requestHeaders.GetProperty<string>("Action").GetValueOrDefault(); | ||
| Uri requestHeadersTo = requestHeaders.GetProperty<Uri>("To").GetValueOrDefault(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we want to use duck typing going forwards instead of GetProperty<T>(string)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I'm surprised we haven't removed those extension methods yet)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Saw your comment later: #2041 (comment)
| /// Gets a value indicating whether to enable the updated WCF instrumentation that delays execution | ||
| /// until later in the WCF pipeline when the WCF server exception handling is established. | ||
| /// </summary> | ||
| internal bool DelayWcfInstrumentationEnabled { get; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Grammatically, either WcfInstrumentationDelayEnabled or DelayedWcfInstrumentationEnabled sounds better to me. If this is only meant as a temporary feature flag, then my level of care is lower.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have one customer using this already (via environment variable DD_TRACE_DELAY_WCF_INSTRUMENTATION_ENABLED) so I'd rather not change it. Fingers crossed we can make this the default behavior once we figure out some missing spans
lucaspimentel
left a comment
There was a problem hiding this 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 comment or two.
Benchmarks Report 🐌Benchmarks for #2041 compared to master:
The following thresholds were used for comparing the benchmark speeds:
Allocation changes below 0.5% are ignored. Benchmark detailsBenchmarks.Trace.AgentWriterBenchmark - Same speed ✔️ Same allocations ✔️Raw results
Benchmarks.Trace.AspNetCoreBenchmark - Same speed ✔️ Same allocations ✔️Raw results
Benchmarks.Trace.DbCommandBenchmark - Same speed ✔️ Same allocations ✔️Raw results
Benchmarks.Trace.ElasticsearchBenchmark - Slower
|
| Benchmark | diff/base | Base Median (ns) | Diff Median (ns) | Modality |
|---|---|---|---|---|
| Benchmarks.Trace.ElasticsearchBenchmark.CallElasticsearchAsync‑net472 | 1.408 | 3,225.78 | 4,540.78 |
Raw results
| Branch | Method | Toolchain | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
|---|---|---|---|---|---|---|---|---|---|
| master | CallElasticsearch |
net472 | 3.110 μs | 0.0608 μs | 0.0676 μs | 0.1284 | 0.0000 | 0.0000 | 827 B |
| master | CallElasticsearch |
netcoreapp3.1 | 1.614 μs | 0.0316 μs | 0.0280 μs | 0.0106 | 0.0000 | 0.0000 | 768 B |
| master | CallElasticsearchAsync |
net472 | 3.251 μs | 0.0614 μs | 0.0575 μs | 0.1499 | 0.0000 | 0.0000 | 963 B |
| master | CallElasticsearchAsync |
netcoreapp3.1 | 1.717 μs | 0.0241 μs | 0.0226 μs | 0.0126 | 0.0000 | 0.0000 | 888 B |
| #2041 | CallElasticsearch |
net472 | 3.213 μs | 0.0611 μs | 0.0750 μs | 0.1286 | 0.0000 | 0.0000 | 827 B |
| #2041 | CallElasticsearch |
netcoreapp3.1 | 1.603 μs | 0.0317 μs | 0.0494 μs | 0.0109 | 0.0000 | 0.0000 | 768 B |
| #2041 | CallElasticsearchAsync |
net472 | 4.560 μs | 0.0890 μs | 0.0832 μs | 0.1492 | 0.0000 | 0.0000 | 963 B |
| #2041 | CallElasticsearchAsync |
netcoreapp3.1 | 1.662 μs | 0.0228 μs | 0.0213 μs | 0.0124 | 0.0000 | 0.0000 | 888 B |
Benchmarks.Trace.GraphQLBenchmark - Same speed ✔️ Same allocations ✔️
Raw results
| Branch | Method | Toolchain | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
|---|---|---|---|---|---|---|---|---|---|
| master | ExecuteAsync |
net472 | 3.447 μs | 0.0660 μs | 0.0706 μs | 0.1688 | 0.0000 | 0.0000 | 1083 B |
| master | ExecuteAsync |
netcoreapp3.1 | 1.844 μs | 0.0235 μs | 0.0220 μs | 0.0144 | 0.0000 | 0.0000 | 1008 B |
| #2041 | ExecuteAsync |
net472 | 3.598 μs | 0.0710 μs | 0.0697 μs | 0.1691 | 0.0000 | 0.0000 | 1083 B |
| #2041 | ExecuteAsync |
netcoreapp3.1 | 1.817 μs | 0.0243 μs | 0.0190 μs | 0.0141 | 0.0000 | 0.0000 | 1008 B |
Benchmarks.Trace.HttpClientBenchmark - Same speed ✔️ Same allocations ✔️
Raw results
| Branch | Method | Toolchain | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
|---|---|---|---|---|---|---|---|---|---|
| master | SendAsync |
net472 | 6.058 μs | 0.1185 μs | 0.2106 μs | 0.3487 | 0.0000 | 0.0000 | 2.19 KB |
| master | SendAsync |
netcoreapp3.1 | 4.440 μs | 0.0538 μs | 0.0503 μs | 0.0283 | 0.0000 | 0.0000 | 2.09 KB |
| #2041 | SendAsync |
net472 | 6.324 μs | 0.1156 μs | 0.1800 μs | 0.3490 | 0.0000 | 0.0000 | 2.19 KB |
| #2041 | SendAsync |
netcoreapp3.1 | 4.320 μs | 0.0837 μs | 0.1352 μs | 0.0294 | 0.0000 | 0.0000 | 2.09 KB |
Benchmarks.Trace.ILoggerBenchmark - Faster 🎉 Same allocations ✔️
Faster 🎉 in #2041
Benchmark
base/diff
Base Median (ns)
Diff Median (ns)
Modality
Benchmarks.Trace.ILoggerBenchmark.EnrichedLog‑net472
1.176
4,595.36
3,906.72
| Benchmark | base/diff | Base Median (ns) | Diff Median (ns) | Modality |
|---|---|---|---|---|
| Benchmarks.Trace.ILoggerBenchmark.EnrichedLog‑net472 | 1.176 | 4,595.36 | 3,906.72 |
Raw results
| Branch | Method | Toolchain | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
|---|---|---|---|---|---|---|---|---|---|
| master | EnrichedLog |
net472 | 4.604 μs | 0.0488 μs | 0.0433 μs | 0.2852 | 0.0000 | 0.0000 | 1.79 KB |
| master | EnrichedLog |
netcoreapp3.1 | 3.798 μs | 0.0747 μs | 0.0734 μs | 0.0275 | 0.0000 | 0.0000 | 1.94 KB |
| #2041 | EnrichedLog |
net472 | 3.916 μs | 0.0757 μs | 0.0984 μs | 0.2872 | 0.0000 | 0.0000 | 1.79 KB |
| #2041 | EnrichedLog |
netcoreapp3.1 | 3.879 μs | 0.0472 μs | 0.0394 μs | 0.0270 | 0.0000 | 0.0000 | 1.94 KB |
Benchmarks.Trace.Log4netBenchmark - Same speed ✔️ Same allocations ✔️
Raw results
| Branch | Method | Toolchain | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
|---|---|---|---|---|---|---|---|---|---|
| master | EnrichedLog |
net472 | 182.4 μs | 3.45 μs | 5.06 μs | 0.7013 | 0.2630 | 0.0000 | 5.23 KB |
| master | EnrichedLog |
netcoreapp3.1 | 146.5 μs | 2.06 μs | 1.93 μs | 0.0000 | 0.0000 | 0.0000 | 5.05 KB |
| #2041 | EnrichedLog |
net472 | 176.2 μs | 3.51 μs | 4.31 μs | 0.6859 | 0.2572 | 0.0000 | 5.23 KB |
| #2041 | EnrichedLog |
netcoreapp3.1 | 146.8 μs | 2.43 μs | 2.28 μs | 0.0000 | 0.0000 | 0.0000 | 5.05 KB |
Benchmarks.Trace.NLogBenchmark - Same speed ✔️ Same allocations ✔️
Raw results
| Branch | Method | Toolchain | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
|---|---|---|---|---|---|---|---|---|---|
| master | EnrichedLog |
net472 | 11.31 μs | 0.194 μs | 0.181 μs | 0.8496 | 0.0000 | 0.0000 | 5.31 KB |
| master | EnrichedLog |
netcoreapp3.1 | 10.10 μs | 0.150 μs | 0.140 μs | 0.0897 | 0.0000 | 0.0000 | 6.28 KB |
| #2041 | EnrichedLog |
net472 | 11.324 μs | 0.2261 μs | 0.2005 μs | 0.8524 | 0.0000 | 0.0000 | 5.31 KB |
| #2041 | EnrichedLog |
netcoreapp3.1 | 9.984 μs | 0.1902 μs | 0.1868 μs | 0.0941 | 0.0000 | 0.0000 | 6.28 KB |
Benchmarks.Trace.RedisBenchmark - Same speed ✔️ Same allocations ✔️
Raw results
| Branch | Method | Toolchain | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
|---|---|---|---|---|---|---|---|---|---|
| master | SendReceive |
net472 | 2.139 μs | 0.0331 μs | 0.0310 μs | 0.1542 | 0.0000 | 0.0000 | 987 B |
| master | SendReceive |
netcoreapp3.1 | 1.988 μs | 0.0239 μs | 0.0223 μs | 0.0138 | 0.0000 | 0.0000 | 984 B |
| #2041 | SendReceive |
net472 | 2.199 μs | 0.0432 μs | 0.0722 μs | 0.1549 | 0.0000 | 0.0000 | 987 B |
| #2041 | SendReceive |
netcoreapp3.1 | 1.977 μs | 0.0281 μs | 0.0249 μs | 0.0133 | 0.0000 | 0.0000 | 984 B |
Benchmarks.Trace.SerilogBenchmark - Slower ⚠️ Same allocations ✔️
Slower ⚠️ in #2041
Benchmark
diff/base
Base Median (ns)
Diff Median (ns)
Modality
Benchmarks.Trace.SerilogBenchmark.EnrichedLog‑netcoreapp3.1
1.151
7,043.97
8,110.68
| Benchmark | diff/base | Base Median (ns) | Diff Median (ns) | Modality |
|---|---|---|---|---|
| Benchmarks.Trace.SerilogBenchmark.EnrichedLog‑netcoreapp3.1 | 1.151 | 7,043.97 | 8,110.68 |
Raw results
| Branch | Method | Toolchain | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
|---|---|---|---|---|---|---|---|---|---|
| master | EnrichedLog |
net472 | 7.834 μs | 0.1542 μs | 0.1443 μs | 0.4461 | 0.0000 | 0.0000 | 2.8 KB |
| master | EnrichedLog |
netcoreapp3.1 | 7.011 μs | 0.0950 μs | 0.1167 μs | 0.0374 | 0.0000 | 0.0000 | 2.61 KB |
| #2041 | EnrichedLog |
net472 | 8.085 μs | 0.1604 μs | 0.2086 μs | 0.4471 | 0.0000 | 0.0000 | 2.8 KB |
| #2041 | EnrichedLog |
netcoreapp3.1 | 8.200 μs | 0.1616 μs | 0.3075 μs | 0.0379 | 0.0000 | 0.0000 | 2.61 KB |
Benchmarks.Trace.SpanBenchmark - Same speed ✔️ Same allocations ✔️
Raw results
| Branch | Method | Toolchain | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
|---|---|---|---|---|---|---|---|---|---|
| master | StartFinishSpan |
net472 | 925.1 ns | 18.53 ns | 25.98 ns | 0.0678 | 0.0000 | 0.0000 | 433 B |
| master | StartFinishSpan |
netcoreapp3.1 | 903.8 ns | 13.90 ns | 12.32 ns | 0.0057 | 0.0000 | 0.0000 | 432 B |
| master | StartFinishScope |
net472 | 1,080.7 ns | 18.28 ns | 32.50 ns | 0.0807 | 0.0000 | 0.0000 | 514 B |
| master | StartFinishScope |
netcoreapp3.1 | 1,021.1 ns | 17.67 ns | 15.66 ns | 0.0075 | 0.0000 | 0.0000 | 552 B |
| #2041 | StartFinishSpan |
net472 | 895.6 ns | 16.98 ns | 14.18 ns | 0.0680 | 0.0000 | 0.0000 | 433 B |
| #2041 | StartFinishSpan |
netcoreapp3.1 | 886.2 ns | 14.07 ns | 12.47 ns | 0.0061 | 0.0000 | 0.0000 | 432 B |
| #2041 | StartFinishScope |
net472 | 1,064.8 ns | 20.99 ns | 20.62 ns | 0.0803 | 0.0000 | 0.0000 | 514 B |
| #2041 | StartFinishScope |
netcoreapp3.1 | 1,021.4 ns | 14.12 ns | 13.21 ns | 0.0076 | 0.0000 | 0.0000 | 552 B |
Cherry-picks the changes from #1992
Additional changes:
@DataDog/apm-dotnet