[AppSec] Api to allow user data to be associated with the local root span#2546
Conversation
andrewlock
left a comment
There was a problem hiding this comment.
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
Tagsobject, and shouldn't consist of 5 nullable args
This comment has been minimized.
This comment has been minimized.
d819e44 to
9789ffb
Compare
This comment has been minimized.
This comment has been minimized.
b23c172 to
a087a33
Compare
a087a33 to
e8001e4
Compare
This comment has been minimized.
This comment has been minimized.
Test setting on the local root directly and on a child span.
andrewlock
left a comment
There was a problem hiding this comment.
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
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
029546d to
9d11fc6
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Code Coverage Report 📊✔️ Merging #2546 into master will not change line coverage
View the full report for further details: Datadog.Trace Breakdown ✔️
The following classes have significant coverage changes.
The following classes were added in #2546:
View the full reports for further details: |
Benchmarks Report 🐌Benchmarks for #2546 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.AppSecBodyBenchmark - 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 - Same speed ✔️ Same allocations ✔️Raw results
Benchmarks.Trace.GraphQLBenchmark - Same speed ✔️ Same allocations ✔️Raw results
Benchmarks.Trace.HttpClientBenchmark - Same speed ✔️ Same allocations ✔️Raw results
Benchmarks.Trace.ILoggerBenchmark - Same speed ✔️ Same allocations ✔️Raw results
Benchmarks.Trace.Log4netBenchmark - Same speed ✔️ Same allocations ✔️Raw results
Benchmarks.Trace.NLogBenchmark - Same speed ✔️ Same allocations ✔️Raw results
Benchmarks.Trace.RedisBenchmark - Same speed ✔️ Same allocations ✔️Raw results
Benchmarks.Trace.SerilogBenchmark - Same speed ✔️ Same allocations ✔️Raw results
Benchmarks.Trace.SpanBenchmark - Same speed ✔️ Same allocations ✔️Raw results
Benchmarks.Trace.TraceAnnotationsBenchmark - Same speed ✔️ Same allocations ✔️Raw results
|
| /// <summary> | ||
| /// A data container class for the users details | ||
| /// </summary> | ||
| public class UserDetails |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
I mentioned readonly struct but I wasn't sure if we should enforce it. Thank you both for keeping me honest 🎉
There was a problem hiding this comment.
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 🤷♂️
There was a problem hiding this comment.
No better than just having parameters passed to the method.
I forgot this is how UserDetails came to be in the first place 🤦🏽♂️
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