Skip to content

[Tracer] Add endpoint discovery and dropping of P0 traces to stats computation feature#2988

Closed
zacharycmontoya wants to merge 23 commits into
masterfrom
zach/stats-followup
Closed

[Tracer] Add endpoint discovery and dropping of P0 traces to stats computation feature#2988
zacharycmontoya wants to merge 23 commits into
masterfrom
zach/stats-followup

Conversation

@zacharycmontoya

@zacharycmontoya zacharycmontoya commented Jul 19, 2022

Copy link
Copy Markdown
Contributor

Summary of changes

Adds two new capabilities to the stats computation feature:

  1. When stats computation is enabled through configuration, the tracer sends a request to the trace agent's /info endpoint to ensure that this feature is supported by the trace agent. If not, the feature is disabled.
  2. The tracer also checks whether it can drop P0 traces (user-rejected traces or sampler-rejected traces). When this feature is supported by the trace agent, P0 traces are no longer serialized to the agent and the "Datadog-Client-Dropped-P0-Traces" and "Datadog-Client-Dropped-P0-Spans" request headers are set on the next trace payload.

Additionally, adds unit tests and integration tests that test all of the agreed upon cross-tracer tests for the stats computation feature.

Reason for change

Computing stats in the tracer reduces a large amount of span serialization (from the tracer) and span deserialization (from the trace agent), which reduces CPU overhead for users with significant amounts of trace agent traffic. The increased testing also adds confidence that our implementation is on par with the implementations within other tracers (since this is moving a trace agent feature to the individual language tracers).

Implementation details

StatsAggregator initialization now executes Endpoint Discovery

Background: When stats computation is enabled, the StatsAggregator object is responsible for buffering stats and periodically sending them to the trace agent's stats endpoint. Stats are computed by passing in an array of spans into the AddRange API, which is called by the AgentWriter when a local trace is finished and is buffered/serialized.

Much like the LiveDebugger initialization, the constructor of StatsAggregator will invoke a task on the threadpool to ping the agent URL at the /info endpoint to check what endpoints and features it supports. We specifically check for two properties:

  • endpoints[] contains "/v0.6/stats". If true, stats computation is enabled via the new property CanComputeStats.
  • client_drop_p0s property is set to true. If so, dropping P0 spans is enabled via the new property CanDropP0s.

Note: Since spans and traces can be finishing while the service discovery is ongoing, we let the StatsAggregator store stats points while the operation is ongoing. If initialization succeeds, the stats aggregrator can start sending stats to the trace agent. If initialization fails, the stats aggregator does not store new stats points and refuses to send stats to the trace agent.

TraceContext changes

Background: The TraceContext object is responsible for flushing finished spans to its Tracer object reference when either all spans in the local trace are finished or the number of finished spans in the ongoing trace exceeds the partial flush limit. After this point, the finished spans are sent to the AgentWriter to be buffered/serialized, so we must make the decision here whether to drop P0 spans.

If Tracer.CanDropP0s == true, then the following algorithm is run on each of the finished spans to determine if the span should be kept. If at least one span is kept, then the entire trace is kept. If not, then the entire trace is dropped.

  • Is the sampling priority greater than 0?
  • Does the span have an error?
  • Does the span have an app analytics sample rate? If so, does the sampling decision determine it should be sampled?

We then pass the keep/drop decision along with the finished span array to the AgentWriter.WriteTrace API.

Note: There will need to be some special handling if a trace is supposed to be sampled out but the new single span ingestion controls determine that single spans should be kept. This is documented in the RFC and will need to be handled when the feature is implemented in the .NET Tracer.

AgentWriter / IApi changes

Background: The AgentWriter puts finished spans into buffers, and when a buffer is full those spans are sent to the trace agent by calling IApi.SendTracesAsync.

When the AgentWriter receives spans, it now accumulates the number of dropped P0 traces and dropped P0 spans. Then, when a span buffer needs to be serialized and IApi.SendTracesAsync is invoked, it passes along these counts and they are added as headers to the trace agent request.

Small changes

  • The DiscoveryService.Create method was refactored because the IConfigurationSource parameter was redundant.
  • The MockTracerAgent test class and all of its subclasses have been updated to accept an AgentConfiguration object which allows us to mock different agent capabilities. This was used to test scenarios where stats computation was allowed but not the dropping of P0 spans.

Test coverage

New tests cover those documented by internal RFCs.

Other details

N/A

@zacharycmontoya zacharycmontoya added the area:tests unit tests, integration tests label Jul 19, 2022
@zacharycmontoya zacharycmontoya self-assigned this Jul 19, 2022
@andrewlock

This comment has been minimized.

@andrewlock

Copy link
Copy Markdown
Member

Code Coverage Report 📊

✔️ Merging #2988 into master will not change line coverage
✔️ Merging #2988 into master will not change branch coverage
⛔ Merging #2988 into master will will increase complexity by 1006

master #2988 Change
Lines 15180 / 20801 16576 / 22643
Lines % 73% 73% 0% ✔️
Branches 9241 / 13129 9835 / 13970
Branches % 70% 70% 0% ✔️
Complexity 14014 15020 1006

View the full report for further details:

Datadog.Trace Breakdown ✔️

master #2988 Change
Lines % 73% 73% 0% ✔️
Branches % 70% 70% 0% ✔️
Complexity 14014 15020 1006

The following classes have significant coverage changes.

File Line coverage change Branch coverage change Complexity change
Datadog.Trace.Ci.GitInfo -17% -11% 0 ✔️
Datadog.Trace.Agent.Transports.HttpStreamRequest -4% ⚠️ 25% ✔️ 3
Datadog.Trace.Ci.CIVisibility 7% ✔️ 7% ✔️ 0 ✔️
Datadog.Trace.Ci.Processors.OriginTagTraceProcessor 9% ✔️ 17% ✔️ 0 ✔️

The following classes were added in #2988:

File Line coverage Branch coverage Complexity
Datadog.Trace.Agent.DiscoveryService.DiscoveryService 83% 50% 14
Datadog.Trace.Debugger.CapturedContext 100% 100% 5
Datadog.Trace.Debugger.CapturedLines 100% 100% 2
Datadog.Trace.Debugger.CapturedValue 84% 60% 11
Datadog.Trace.Debugger.Configurations.ConfigurationPoller 65% 50% 17
...And 62 more

3 classes were removed from Datadog.Trace in #2988

View the full reports for further details:

@GreenMatan
GreenMatan force-pushed the live-debugger branch 2 times, most recently from 6f678b7 to 7084b89 Compare July 20, 2022 12:22
Base automatically changed from live-debugger to master July 20, 2022 21:18
@andrewlock

This comment has been minimized.

@zacharycmontoya
zacharycmontoya force-pushed the zach/stats-followup branch 2 times, most recently from f4909b5 to e74fd99 Compare August 1, 2022 16:26
@andrewlock

This comment has been minimized.

… support them. Since this has not yet been implemented, the test is skipped
… case is skipped because the feature is not yet implemented
Caveat: We may have the StatsComputationSetting enabled but the agent we try to reach does not support it. That complicated mess will be addressed next.
…ss when StatsAggregator is constructed. If the agent is unable to receive stats from tracers, the feature is disabled correctly and no stats are submitted.
…ion tests to allow us to delay test code until we've confirmed that the MockTracerAgent can receive the tracer stats. The delay should make the test results more stable
… object which can be converted directly to JSON
…0-Traces" and "Datadog-Client-Dropped-P0-Spans" when we drop P0 traces
@andrewlock

This comment has been minimized.

if (store.Counts.Length > 0 && !store.IsEmpty())
{
Counts = new double[store.MaxIndex - store.MinIndex + 1];
// This change seems correct based on the code in the java repo: https://github.com/DataDog/sketches-java/blob/5031ddff8e6d6de5174d2c06e2ced618f3a48621/src/main/java/com/datadoghq/sketch/ddsketch/store/DenseStore.java#L60-L64

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@kevingosse what do you think? I haven't noticed any particular issues stemming from this code, but I noticed a discrepancy when reviewing this code

@zacharycmontoya zacharycmontoya added the area:tracer The core tracer library (Datadog.Trace, does not include OpenTracing, native code, or integrations) label Aug 2, 2022
@zacharycmontoya
zacharycmontoya marked this pull request as ready for review August 2, 2022 21:43
@zacharycmontoya
zacharycmontoya requested review from a team as code owners August 2, 2022 21:43
@zacharycmontoya zacharycmontoya changed the title [Tracer] Add more stats computation tests [Tracer] Add endpoint discovery and dropping of P0 traces to stats computation feature Aug 2, 2022
@andrewlock

Copy link
Copy Markdown
Member

Benchmarks Report 🐌

Benchmarks for #2988 compared to master:

  • 1 benchmarks are faster, with geometric mean 1.153
  • 2 benchmarks are slower, with geometric mean 1.158
  • 2 benchmarks have more allocations

The following thresholds were used for comparing the benchmark speeds:

  • Mann–Whitney U test with statistical test for significance of 5%
  • Only results indicating a difference greater than 10% and 0.3 ns are considered.

Allocation changes below 0.5% are ignored.

Benchmark details

Benchmarks.Trace.AgentWriterBenchmark - Slower ⚠️ More allocations ⚠️

Slower ⚠️ in #2988

Benchmark diff/base Base Median (ns) Diff Median (ns) Modality
Benchmarks.Trace.AgentWriterBenchmark.WriteAndFlushEnrichedTraces‑netcoreapp3.1 1.181 460,907.57 544,367.68

More allocations ⚠️ in #2988

Benchmark Base Allocated Diff Allocated Change Change %
Benchmarks.Trace.AgentWriterBenchmark.WriteAndFlushEnrichedTraces‑net472 3.18 KB 6.78 KB 3.6 KB 113.48%
Benchmarks.Trace.AgentWriterBenchmark.WriteAndFlushEnrichedTraces‑netcoreapp3.1 2.58 KB 5.34 KB 2.75 KB 106.54%

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master WriteAndFlushEnrichedTraces net472 711μs 439ns 1.64μs 0.353 0 0 3.18 KB
master WriteAndFlushEnrichedTraces netcoreapp3.1 461μs 414ns 1.55μs 0 0 0 2.58 KB
#2988 WriteAndFlushEnrichedTraces net472 759μs 385ns 1.49μs 0.758 0 0 6.78 KB
#2988 WriteAndFlushEnrichedTraces netcoreapp3.1 544μs 419ns 1.62μs 0 0 0 5.34 KB
Benchmarks.Trace.AppSecBodyBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master AllCycleSimpleBody net472 1.69μs 1.18ns 4.43ns 0.237 0 0 1.49 KB
master AllCycleSimpleBody netcoreapp3.1 1.82μs 4.03ns 15.1ns 0.0189 0 0 1.37 KB
master AllCycleMoreComplexBody net472 17.1μs 16.4ns 63.4ns 1.38 0.025 0 8.75 KB
master AllCycleMoreComplexBody netcoreapp3.1 14.1μs 20.2ns 72.7ns 0.106 0 0 7.85 KB
master BodyExtractorSimpleBody net472 257ns 0.255ns 0.989ns 0.0574 0 0 361 B
master BodyExtractorSimpleBody netcoreapp3.1 220ns 0.383ns 1.43ns 0.00367 0 0 272 B
master BodyExtractorMoreComplexBody net472 15.4μs 12.3ns 47.5ns 1.21 0.0154 0 7.62 KB
master BodyExtractorMoreComplexBody netcoreapp3.1 11.7μs 10.9ns 40.7ns 0.0875 0 0 6.75 KB
#2988 AllCycleSimpleBody net472 1.68μs 1.62ns 6.27ns 0.237 0 0 1.49 KB
#2988 AllCycleSimpleBody netcoreapp3.1 1.82μs 4ns 15.5ns 0.0191 0 0 1.37 KB
#2988 AllCycleMoreComplexBody net472 16.8μs 13.3ns 51.5ns 1.39 0.0251 0 8.75 KB
#2988 AllCycleMoreComplexBody netcoreapp3.1 14.1μs 13ns 50.4ns 0.105 0 0 7.85 KB
#2988 BodyExtractorSimpleBody net472 258ns 0.165ns 0.618ns 0.0573 0 0 361 B
#2988 BodyExtractorSimpleBody netcoreapp3.1 219ns 0.257ns 0.962ns 0.00374 0 0 272 B
#2988 BodyExtractorMoreComplexBody net472 15.1μs 9.87ns 38.2ns 1.21 0.015 0 7.62 KB
#2988 BodyExtractorMoreComplexBody netcoreapp3.1 11.9μs 13ns 50.2ns 0.089 0 0 6.75 KB
Benchmarks.Trace.AspNetCoreBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master SendRequest net472 0ns 0ns 0ns 0 0 0 0 b
master SendRequest netcoreapp3.1 179μs 180ns 697ns 0.269 0 0 20.37 KB
#2988 SendRequest net472 0ns 0ns 0ns 0 0 0 0 b
#2988 SendRequest netcoreapp3.1 180μs 56.9ns 213ns 0.27 0 0 20.37 KB
Benchmarks.Trace.DbCommandBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master ExecuteNonQuery net472 1.52μs 0.586ns 2.19ns 0.126 0.000766 0 794 B
master ExecuteNonQuery netcoreapp3.1 1.28μs 0.624ns 2.42ns 0.0109 0 0 824 B
#2988 ExecuteNonQuery net472 1.59μs 0.404ns 1.56ns 0.126 0.000794 0 794 B
#2988 ExecuteNonQuery netcoreapp3.1 1.26μs 0.537ns 2.01ns 0.0113 0 0 824 B
Benchmarks.Trace.ElasticsearchBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master CallElasticsearch net472 2.12μs 1.08ns 4.17ns 0.159 0 0 1 KB
master CallElasticsearch netcoreapp3.1 1.43μs 2.45ns 9.49ns 0.0134 0 0 984 B
master CallElasticsearchAsync net472 2.34μs 0.834ns 3.12ns 0.18 0 0 1.14 KB
master CallElasticsearchAsync netcoreapp3.1 1.49μs 0.656ns 2.54ns 0.0149 0 0 1.1 KB
#2988 CallElasticsearch net472 2.18μs 1.49ns 5.59ns 0.159 0 0 1 KB
#2988 CallElasticsearch netcoreapp3.1 1.38μs 0.772ns 2.78ns 0.0131 0 0 984 B
#2988 CallElasticsearchAsync net472 2.21μs 1.95ns 7.54ns 0.181 0 0 1.14 KB
#2988 CallElasticsearchAsync netcoreapp3.1 1.48μs 0.394ns 1.47ns 0.0149 0 0 1.1 KB
Benchmarks.Trace.GraphQLBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master ExecuteAsync net472 2.53μs 1.39ns 5.36ns 0.2 0 0 1.26 KB
master ExecuteAsync netcoreapp3.1 1.6μs 1.1ns 4.12ns 0.0161 0 0 1.22 KB
#2988 ExecuteAsync net472 2.42μs 5.04ns 19.5ns 0.199 0 0 1.26 KB
#2988 ExecuteAsync netcoreapp3.1 1.6μs 2.01ns 7.54ns 0.0168 0 0 1.22 KB
Benchmarks.Trace.HttpClientBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master SendAsync net472 5.11μs 2.09ns 7.81ns 0.393 0 0 2.48 KB
master SendAsync netcoreapp3.1 3.36μs 2.13ns 7.95ns 0.0318 0 0 2.4 KB
#2988 SendAsync net472 5.09μs 8.98ns 34.8ns 0.392 0 0 2.48 KB
#2988 SendAsync netcoreapp3.1 3.36μs 4.12ns 15.4ns 0.0318 0 0 2.4 KB
Benchmarks.Trace.ILoggerBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master EnrichedLog net472 2.78μs 1.8ns 6.97ns 0.263 0 0 1.66 KB
master EnrichedLog netcoreapp3.1 2.27μs 0.595ns 2.06ns 0.0239 0 0 1.73 KB
#2988 EnrichedLog net472 2.94μs 3.9ns 14.6ns 0.264 0 0 1.66 KB
#2988 EnrichedLog netcoreapp3.1 2.4μs 1.6ns 6.21ns 0.0229 0 0 1.73 KB
Benchmarks.Trace.Log4netBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master EnrichedLog net472 144μs 101ns 391ns 0.651 0.217 0 4.5 KB
master EnrichedLog netcoreapp3.1 110μs 90.6ns 351ns 0.0551 0 0 4.38 KB
#2988 EnrichedLog net472 147μs 196ns 758ns 0.659 0.22 0 4.5 KB
#2988 EnrichedLog netcoreapp3.1 113μs 72.6ns 262ns 0.057 0 0 4.38 KB
Benchmarks.Trace.NLogBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master EnrichedLog net472 5.45μs 1.63ns 6.32ns 0.545 0.00273 0 3.43 KB
master EnrichedLog netcoreapp3.1 4.23μs 7.92ns 30.7ns 0.0524 0 0 3.8 KB
#2988 EnrichedLog net472 5.41μs 9.32ns 36.1ns 0.545 0.00266 0 3.43 KB
#2988 EnrichedLog netcoreapp3.1 4.25μs 1.77ns 6.64ns 0.0509 0 0 3.8 KB
Benchmarks.Trace.RedisBenchmark - Slower ⚠️ Same allocations ✔️

Slower ⚠️ in #2988

Benchmark diff/base Base Median (ns) Diff Median (ns) Modality
Benchmarks.Trace.RedisBenchmark.SendReceive‑netcoreapp3.1 1.136 1,576.11 1,790.88

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master SendReceive net472 1.99μs 0.447ns 1.73ns 0.193 0 0 1.22 KB
master SendReceive netcoreapp3.1 1.58μs 1.15ns 4.32ns 0.0165 0 0 1.21 KB
#2988 SendReceive net472 1.99μs 1.2ns 4.65ns 0.193 0 0 1.22 KB
#2988 SendReceive netcoreapp3.1 1.79μs 0.79ns 3.06ns 0.0168 0 0 1.21 KB
Benchmarks.Trace.SerilogBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master EnrichedLog net472 4.75μs 1.56ns 5.82ns 0.33 0 0 2.08 KB
master EnrichedLog netcoreapp3.1 4.1μs 2.39ns 9.24ns 0.0226 0 0 1.69 KB
#2988 EnrichedLog net472 4.61μs 1.71ns 6.61ns 0.33 0 0 2.08 KB
#2988 EnrichedLog netcoreapp3.1 4.15μs 1.75ns 6.31ns 0.0229 0 0 1.69 KB
Benchmarks.Trace.SpanBenchmark - Faster 🎉 Same allocations ✔️

Faster 🎉 in #2988

Benchmark base/diff Base Median (ns) Diff Median (ns) Modality
Benchmarks.Trace.SpanBenchmark.StartFinishSpan‑netcoreapp3.1 1.153 821.56 712.30

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master StartFinishSpan net472 870ns 0.129ns 0.464ns 0.104 0 0 658 B
master StartFinishSpan netcoreapp3.1 820ns 0.891ns 3.33ns 0.00864 0 0 648 B
master StartFinishScope net472 1.02μs 0.411ns 1.59ns 0.117 0 0 738 B
master StartFinishScope netcoreapp3.1 902ns 0.35ns 1.26ns 0.0104 0 0 768 B
#2988 StartFinishSpan net472 802ns 0.137ns 0.495ns 0.104 0 0 658 B
#2988 StartFinishSpan netcoreapp3.1 712ns 0.244ns 0.911ns 0.0089 0 0 648 B
#2988 StartFinishScope net472 1.04μs 0.359ns 1.34ns 0.117 0 0 738 B
#2988 StartFinishScope netcoreapp3.1 960ns 0.25ns 0.934ns 0.0105 0 0 768 B
Benchmarks.Trace.TraceAnnotationsBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master RunOnMethodBegin net472 1.23μs 0.493ns 1.91ns 0.117 0 0 738 B
master RunOnMethodBegin netcoreapp3.1 1.04μs 0.449ns 1.74ns 0.0104 0 0 768 B
#2988 RunOnMethodBegin net472 1.21μs 0.385ns 1.49ns 0.117 0 0 738 B
#2988 RunOnMethodBegin netcoreapp3.1 1.07μs 0.379ns 1.47ns 0.0102 0 0 768 B

var rawHttpStatusCode = span.GetTag(Tags.HttpStatusCode);
try
{
var isDiscoverySuccessful = await _discoveryService.DiscoverAsync().ConfigureAwait(false);

@OmerRaviv OmerRaviv Aug 3, 2022

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The way we're currently using DiscoveryService where we're only querying it on initialization means that if the customer enables stats in datadog-agent config (or upgrades datadog-agent to a version that has these endpoints) after the monitored processes have started, they'd need to restart all the monitored processes for the change to take effect.

This is not an ideal user experience, so I think we should definitely consider changing things such that DiscoveryService will be continuously polling the /info endpoint and triggering some sort of event / notification to all of its consumers when things change.

This is just a general comment - not related to your PR really, just wanted to bring it to your attention.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This continuous check would benefit this feature as well, because if the agent has been upgraded/downgrade while the application is alive, we should adjust in real-time

@zacharycmontoya

Copy link
Copy Markdown
Contributor Author

Closing this PR in favor of #3047, #3048, and #3049 which break down the PR into smaller reviewable units

zacharycmontoya added a commit that referenced this pull request Aug 23, 2022
Adds testing for various requirements of the stats computation feature, as agreed upon by other tracers.

This is PR 1/3 in an attempt to break down the massive PR #2988
@zacharycmontoya
zacharycmontoya deleted the zach/stats-followup branch September 9, 2022 16:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:tests unit tests, integration tests area:tracer The core tracer library (Datadog.Trace, does not include OpenTracing, native code, or integrations)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants