Skip to content

[AppSec] Api to allow user data to be associated with the local root span#2546

Merged
robertpi merged 10 commits into
masterfrom
appsec/robert/user-details-api
Apr 11, 2022
Merged

[AppSec] Api to allow user data to be associated with the local root span#2546
robertpi merged 10 commits into
masterfrom
appsec/robert/user-details-api

Conversation

@robertpi

@robertpi robertpi commented Mar 8, 2022

Copy link
Copy Markdown
Member

Summary of changes

Api to allow user data to be associated with the local root span

Reason for change

This is useful to allow the backend to detect bad actors

Implementation details

Just an extra message on the tracer, that tags the local root span.

Test coverage

Unit tests

@robertpi
robertpi requested a review from a team as a code owner March 8, 2022 18:24

@andrewlock andrewlock left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I agree with the principal, allowing users to set tags on the root span but I think

  • The API should be about exposing the root span, not this single use case. We've talked about this for a long time though, and I can't remember right now what the down sides are for doing this
  • We shouldn't have a "Set User" API at all IMO, or if we do, it should be on the Tags object, and shouldn't consist of 5 nullable args

Comment thread tracer/src/Datadog.Trace/ITracer.cs Outdated
Comment thread tracer/src/Datadog.Trace/Tracer.cs Outdated
@andrewlock

This comment has been minimized.

@anna-git anna-git changed the title Api to allow user data to be associated with the local root span [AppSec] Api to allow user data to be associated with the local root span Mar 9, 2022
Comment thread tracer/src/Datadog.Trace/Tags.cs Outdated
Comment thread tracer/src/Datadog.Trace/ITracer.cs Outdated
Comment thread tracer/src/Datadog.Trace/ITracer.cs Outdated
Comment thread tracer/src/Datadog.Trace/Tracer.cs Outdated
@robertpi
robertpi force-pushed the appsec/robert/user-details-api branch 2 times, most recently from d819e44 to 9789ffb Compare April 1, 2022 14:44
@andrewlock

This comment has been minimized.

@robertpi
robertpi force-pushed the appsec/robert/user-details-api branch 2 times, most recently from b23c172 to a087a33 Compare April 5, 2022 09:10
@robertpi
robertpi force-pushed the appsec/robert/user-details-api branch from a087a33 to e8001e4 Compare April 5, 2022 11:26
@andrewlock

This comment has been minimized.

Comment thread tracer/src/Datadog.Trace/Tracer.cs Outdated
Comment thread tracer/src/Datadog.Trace/Tracer.cs Outdated
Comment thread tracer/test/Datadog.Trace.Tests/TracerTests.cs Outdated
@lucaspimentel lucaspimentel added the area:tracer The core tracer library (Datadog.Trace, does not include OpenTracing, native code, or integrations) label Apr 6, 2022
robertpi added 2 commits April 8, 2022 10:19
Test setting on the local root directly and on a child span.

@andrewlock andrewlock left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good, just a few suggestions.

Noting explicitly that this will only set the tags on the root span in non-version-mismatch scenarios. When there's a version mismatch, the tags could essentially be set on any span in the trace

Comment thread tracer/src/Datadog.Trace/Tracer.cs Outdated
Comment thread tracer/src/Datadog.Trace/Tracer.cs Outdated
Comment thread tracer/src/Datadog.Trace/UserDetails.cs Outdated
Comment thread tracer/test/Datadog.Trace.Tests/TracerTests.cs
Comment thread tracer/src/Datadog.Trace/SpanExtensions.cs
@andrewlock

This comment has been minimized.

@andrewlock

This comment has been minimized.

@andrewlock

This comment has been minimized.

@robertpi
robertpi force-pushed the appsec/robert/user-details-api branch from 029546d to 9d11fc6 Compare April 8, 2022 15:28
@andrewlock

This comment has been minimized.

@andrewlock

This comment has been minimized.

@andrewlock

Copy link
Copy Markdown
Member

Code Coverage Report 📊

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

master #2546 Change
Lines 13548 / 18553 13515 / 18573
Lines % 73% 73% 0% ✔️
Branches 7830 / 11073 7825 / 11095
Branches % 71% 71% 0% ✔️
Complexity 12372 12394 22

View the full report for further details:

Datadog.Trace Breakdown ✔️

master #2546 Change
Lines % 73% 73% 0% ✔️
Branches % 71% 71% 0% ✔️
Complexity 12372 12394 22

The following classes have significant coverage changes.

File Line coverage change Branch coverage change Complexity change
Datadog.Trace.Telemetry.JsonHttpClientTelemetryTransport -26% -38% 0 ✔️
Datadog.Trace.Ci.GitInfo -17% -11% 0 ✔️
Datadog.Trace.Ci.CIVisibility 7% ✔️ 7% ✔️ 0 ✔️

The following classes were added in #2546:

File Line coverage Branch coverage Complexity
Datadog.Trace.SpanExtensions 90% 82% 22

View the full reports for further details:

@robertpi
robertpi merged commit b292273 into master Apr 11, 2022
@robertpi
robertpi deleted the appsec/robert/user-details-api branch April 11, 2022 15:51
@github-actions github-actions Bot added this to the vNext milestone Apr 11, 2022
@andrewlock

Copy link
Copy Markdown
Member

Benchmarks Report 🐌

Benchmarks for #2546 compared to master:

  • All benchmarks have the same speed
  • All benchmarks have the same 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 - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master WriteAndFlushEnrichedTraces net472 960μs 5.44μs 36.5μs 0 0 0 3.16 KB
master WriteAndFlushEnrichedTraces netcoreapp3.1 733μs 3.98μs 23.9μs 0 0 0 2.57 KB
#2546 WriteAndFlushEnrichedTraces net472 966μs 5.56μs 43.7μs 0 0 0 3.16 KB
#2546 WriteAndFlushEnrichedTraces netcoreapp3.1 732μs 4.13μs 28μs 0 0 0 2.57 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 298ns 1.47ns 6.42ns 0.052 0 0 329 B
master AllCycleSimpleBody netcoreapp3.1 387ns 2.2ns 15.2ns 0.00459 0 0 328 B
master AllCycleMoreComplexBody net472 295ns 1.59ns 8.7ns 0.0482 0 0 305 B
master AllCycleMoreComplexBody netcoreapp3.1 391ns 2.11ns 12.3ns 0.00412 0 0 304 B
master BodyExtractorSimpleBody net472 433ns 2.44ns 15.4ns 0.057 0 0 361 B
master BodyExtractorSimpleBody netcoreapp3.1 460ns 2.48ns 15.1ns 0.0037 0 0 272 B
master BodyExtractorMoreComplexBody net472 24.7μs 141ns 1.05μs 1.19 0.0122 0 7.62 KB
master BodyExtractorMoreComplexBody netcoreapp3.1 22.3μs 127ns 969ns 0.0873 0 0 6.75 KB
#2546 AllCycleSimpleBody net472 293ns 1.55ns 7.57ns 0.052 0 0 329 B
#2546 AllCycleSimpleBody netcoreapp3.1 386ns 2.13ns 12.6ns 0.00453 0 0 328 B
#2546 AllCycleMoreComplexBody net472 309ns 1.65ns 8.07ns 0.0482 0 0 305 B
#2546 AllCycleMoreComplexBody netcoreapp3.1 367ns 2.05ns 12.5ns 0.00424 0 0 304 B
#2546 BodyExtractorSimpleBody net472 433ns 2.16ns 15.1ns 0.0571 0 0 361 B
#2546 BodyExtractorSimpleBody netcoreapp3.1 463ns 2.71ns 23.1ns 0.00372 0 0 272 B
#2546 BodyExtractorMoreComplexBody net472 24.7μs 143ns 1.19μs 1.19 0.0117 0 7.62 KB
#2546 BodyExtractorMoreComplexBody netcoreapp3.1 21.8μs 120ns 699ns 0.0914 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 302μs 1.49μs 6.13μs 0.15 0 0 19.74 KB
#2546 SendRequest net472 0ns 0ns 0ns 0 0 0 0 b
#2546 SendRequest netcoreapp3.1 310μs 1.76μs 12.3μs 0.151 0 0 19.74 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.81μs 10.4ns 83.6ns 0.0942 0.000923 0 594 B
master ExecuteNonQuery netcoreapp3.1 1.52μs 8.54ns 59.2ns 0.00886 0 0 632 B
#2546 ExecuteNonQuery net472 1.84μs 10.6ns 77.8ns 0.0937 0.000852 0 594 B
#2546 ExecuteNonQuery netcoreapp3.1 1.51μs 8.68ns 64.9ns 0.00922 0 0 632 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.83μs 16.2ns 122ns 0.125 0 0 802 B
master CallElasticsearch netcoreapp3.1 1.68μs 8.37ns 35.5ns 0.0112 0 0 792 B
master CallElasticsearchAsync net472 2.95μs 16.6ns 113ns 0.145 0 0 939 B
master CallElasticsearchAsync netcoreapp3.1 1.92μs 10.2ns 49.7ns 0.0127 0 0 912 B
#2546 CallElasticsearch net472 2.87μs 16.4ns 124ns 0.124 0 0 802 B
#2546 CallElasticsearch netcoreapp3.1 1.71μs 9.89ns 74.7ns 0.0104 0 0 792 B
#2546 CallElasticsearchAsync net472 3.03μs 17.6ns 149ns 0.146 0 0 939 B
#2546 CallElasticsearchAsync netcoreapp3.1 1.9μs 10.2ns 55.8ns 0.0123 0 0 912 B
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 3.28μs 17.9ns 110ns 0.164 0 0 1.06 KB
master ExecuteAsync netcoreapp3.1 2.23μs 10.5ns 42.1ns 0.0147 0 0 1.03 KB
#2546 ExecuteAsync net472 3.45μs 19.7ns 138ns 0.164 0 0 1.06 KB
#2546 ExecuteAsync netcoreapp3.1 2.07μs 11ns 56.9ns 0.0138 0 0 1.03 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 6.99μs 40.3ns 332ns 0.356 0 0 2.28 KB
master SendAsync netcoreapp3.1 5.3μs 29.4ns 179ns 0.0299 0 0 2.21 KB
#2546 SendAsync net472 7.21μs 34.3ns 175ns 0.359 0 0 2.28 KB
#2546 SendAsync netcoreapp3.1 5.33μs 30.3ns 221ns 0.0308 0 0 2.21 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 3.6μs 19.8ns 151ns 0.227 0 0 1.45 KB
master EnrichedLog netcoreapp3.1 3.35μs 18.7ns 120ns 0.0203 0 0 1.53 KB
#2546 EnrichedLog net472 3.57μs 20.9ns 185ns 0.227 0 0 1.45 KB
#2546 EnrichedLog netcoreapp3.1 3.07μs 14.8ns 64.7ns 0.0212 0 0 1.53 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 307μs 1.7μs 10.4μs 0.309 0.155 0 4.33 KB
master EnrichedLog netcoreapp3.1 239μs 1.29μs 7.2μs 0 0 0 4.21 KB
#2546 EnrichedLog net472 298μs 1.5μs 6.35μs 0.437 0.146 0 4.33 KB
#2546 EnrichedLog netcoreapp3.1 235μs 1.08μs 4.06μs 0 0 0 4.21 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 8.24μs 45.2ns 275ns 0.506 0 0 3.23 KB
master EnrichedLog netcoreapp3.1 6.76μs 35.7ns 186ns 0.0484 0 0 3.6 KB
#2546 EnrichedLog net472 8.22μs 46.2ns 306ns 0.503 0 0 3.23 KB
#2546 EnrichedLog netcoreapp3.1 6.65μs 33.5ns 157ns 0.0503 0 0 3.6 KB
Benchmarks.Trace.RedisBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master SendReceive net472 2.41μs 12.8ns 66.3ns 0.16 0 0 1.01 KB
master SendReceive netcoreapp3.1 2.17μs 11.6ns 63.7ns 0.0138 0 0 1.01 KB
#2546 SendReceive net472 2.53μs 14.1ns 93.5ns 0.158 0 0 1.01 KB
#2546 SendReceive netcoreapp3.1 2.15μs 12ns 72.9ns 0.0136 0 0 1.01 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 5.99μs 33.4ns 224ns 0.292 0 0 1.87 KB
master EnrichedLog netcoreapp3.1 5.41μs 30.7ns 232ns 0.0206 0 0 1.49 KB
#2546 EnrichedLog net472 6.23μs 34.7ns 250ns 0.292 0 0 1.87 KB
#2546 EnrichedLog netcoreapp3.1 5.4μs 29.7ns 190ns 0.0191 0 0 1.49 KB
Benchmarks.Trace.SpanBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master StartFinishSpan net472 1.01μs 5.35ns 29.8ns 0.0714 0 0 457 B
master StartFinishSpan netcoreapp3.1 886ns 4.79ns 24.9ns 0.00634 0 0 456 B
master StartFinishScope net472 1.2μs 6.99ns 64.1ns 0.0843 0 0 538 B
master StartFinishScope netcoreapp3.1 1.07μs 5.55ns 38ns 0.00771 0 0 576 B
#2546 StartFinishSpan net472 898ns 4.65ns 23.2ns 0.072 0 0 457 B
#2546 StartFinishSpan netcoreapp3.1 885ns 4.79ns 25.3ns 0.00615 0 0 456 B
#2546 StartFinishScope net472 1.12μs 6.26ns 38.6ns 0.0844 0 0 538 B
#2546 StartFinishScope netcoreapp3.1 1.05μs 5.78ns 37.9ns 0.00783 0 0 576 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.35μs 7.71ns 60.2ns 0.0842 0 0 538 B
master RunOnMethodBegin netcoreapp3.1 1.2μs 6.46ns 34.2ns 0.00754 0 0 576 B
#2546 RunOnMethodBegin net472 1.3μs 6.72ns 32.9ns 0.0842 0 0 538 B
#2546 RunOnMethodBegin netcoreapp3.1 1.22μs 7.13ns 60.1ns 0.00762 0 0 576 B

/// <summary>
/// A data container class for the users details
/// </summary>
public class UserDetails

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If users are expected to create UserDetails on every request/trace, we should consider making this a struct (maybe an immutable readonly struct?) to avoid the heap allocation. Since this would not be a tiny struct, we could also make the UserDetails parameter in the extension method an in parameter, so it's passed by reference.

If users decide to cache the UserDetails in the user session, it will end up in the heap, but that's still only one instance per user instead of one per request.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

+1

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I agree with the struct part, but I don't think we should use in when it's not a readonly struct.

From the docs:

Pass a struct as the argument for an in parameter only if it's declared with the readonly modifier or the method accesses only readonly members of the struct. Otherwise, the compiler must create defensive copies in many situations to ensure that arguments are not mutated. Consider the following example that calculates the distance of a 3D point from the origin:

Without some language guarantee, the compiler must create a temporary copy of the argument before calling any member not marked with the readonly modifier. The temporary storage is created on the stack, the values of the argument are copied to the temporary storage, and the value is copied to the stack for each member access as the this argument. In many situations, these copies harm performance enough that pass-by-value is faster than pass-by-read-only-reference when the argument type isn't a readonly struct and the method calls members that aren't marked readonly.

@tonyredondo tonyredondo Apr 11, 2022

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think the idea is to use readonly struct and have best of both worlds, an struct that can be cached if the user wants (allocated in the heap) or avoid the heap allocation and do stack allocation if they want to use new in each request.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I mentioned readonly struct but I wasn't sure if we should enforce it. Thank you both for keeping me honest 🎉

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The trouble with readonly struct here is that it makes the API worse IMO. No better than just having parameters passed to the method. Either way would be a breaking change if we want to add an extra parameter.

Struct is still an improvement, we just have to accept the copy I think 🤷‍♂️

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No better than just having parameters passed to the method.

I forgot this is how UserDetails came to be in the first place 🤦🏽‍♂️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:asm 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.

6 participants