-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
New immutable type for Flags structs #5972
Conversation
b1cd7af
to
5d5f70f
Compare
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 think this nicely encapsulates the types and follows the separation of concerns between OTLP and W3C, which is great.
However, I am not sure the ergonomics of using the resulting API are very good. So for instance to set the sampled flag on an existing LogRecord you need to do:
logRecord.SetFlagsStruct(logRecord.FlagsStruct().WithTraceFlags(logRecord.FlagsStruct().TraceFlags().WithIsSampled(true)))
This seems a bit too much to flip a single bit.
What do other @open-telemetry/collector-approvers think?
(I know I asked for this, but I am happy to admit my proposal was wrong if that's how people feel about the API).
@tigrannajaryan this is a draft, so indeed some comments are incorrect, but will fix them Regarding the example, indeed is too much if you do it in 1 line with no local vars. No local vars: logRecord.SetFlagsStruct(logRecord.FlagsStruct().WithTraceFlags(logRecord.FlagsStruct().TraceFlags().WithIsSampled(true))) TraceFlags local var: tf := logRecord.FlagsStruct().TraceFlags()
logRecord.SetFlagsStruct(logRecord.FlagsStruct().WithTraceFlags(tf.WithIsSampled(true)) LogRecordFlags local var: lf := logRecord.FlagsStruct()
logRecord.SetFlagsStruct(lf.WithTraceFlags(lf.TraceFlags().WithIsSampled(true)) LogRecordFlags and TraceFlags local vars: lf := logRecord.FlagsStruct()
tf := tf.TraceFlags()
logRecord.SetFlagsStruct(lf.WithTraceFlags(tf.WithIsSampled(true)) /cc @TylerHelmuth please comment since you also worked on this. Update: The |
79a7b94
to
ee2a1e8
Compare
ee2a1e8
to
5e8efd1
Compare
Codecov ReportBase: 92.17% // Head: 92.16% // Decreases project coverage by
Additional details and impacted files@@ Coverage Diff @@
## main #5972 +/- ##
==========================================
- Coverage 92.17% 92.16% -0.01%
==========================================
Files 210 212 +2
Lines 13272 13259 -13
==========================================
- Hits 12233 12220 -13
Misses 822 822
Partials 217 217
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. ☔ View full report at Codecov. |
5e8efd1
to
b141278
Compare
b141278
to
35a4629
Compare
@bogdandrutu @tigrannajaryan reading through this it definitely feels like a deviation away from the simplicity of Even with local vars, I am not as knowledgeable on OTLP/WC3/pdata as you two, so I am not immediately seeing the need for this change. My initial opinion is I don't love the complexity/ergonomics this adds. |
@TylerHelmuth for the context, I think this was triggered by my comment here #5959 (comment) |
TL;DR: Based on the current investigation I am proposing to go with an immutable API for all flags implementations to avoid AsRaw/FromRaw APIs, and if possible to also go with the separation of concerns that @tigrannajaryan mentioned. There are other examples that need to be considered as well (not only the most unfavorable case), will try to compare both APIs, while doing so will use same name
Immutable vs Mutable Also a questions from previous comments There is no downside on retrieving/checking the flag components. So we need to make a tradeoff between having these AsRaw/FromRaw on a mutable Flags implementation which does not require the "Set" after change, or have immutable Flags which require set after changes, but conversion to/from raw value is implicit The immutable pattern in golang is not new, since types like isSampled := logRecord.Flags().IsSampled()
// OR
isSampled := logRecord.Flags().IsSampled() Updating a value: isSampled := logRecord.Flags().SetIsSampled(true)
// OR
isSampled := logRecord.SetFlags(logRecord.Flags().WithIsSampled(true)) Accessing raw: var rawFlags uint32
rawFlags = logRecord.Flags().AsRaw()
// OR
rawFlags = logRecord.Flags() Updating from raw: logRecord.Flags().FromRaw(rawFlags)
// OR
logRecord.SetFlags(rawFlags) After crafting this PR I kind of like this, the downside of setting the value is less important in practice than the conversion to/from raw (don't show me the contrib usages of SetIsSampled, because I know all of them, since I added all of them, they are there just because before in tests users set a random value for the flags which was not possible with the current API without FromFlags which PR generated this discussion). This does not imply that we need a Separation of Concerns between W3C and OTLP In other words, the OTLP defines the least significant byte as the This has also a benefit (not only downsides), because this avoids duplicate code, in an imminent event of adding TraceFlags in other places, we will need to duplicate these funcs ( Retrieving a value: isSampled := logRecord.Flags().IsSampled()
// OR
isSampled := logRecord.Flags().TraceFlags().IsSampled() Updating a value: isSampled := logRecord.Flags().SetIsSampled(true)
// OR
isSampled := logRecord.Flags().TraceFlags().SetIsSampled(true) Accessing raw trace context only part: logRecord.Flags().AsRaw()&0xFF
// OR
logRecord.Flags().TraceContext().AsRaw(rawFlags) Updating from raw trace context only part: logRecord.Flags().FromRaw((logRecord.Flags().AsRaw & ^0xFF) | rawFlags)
// OR
logRecord.Flags().TraceContext().FromRaw(rawFlags) After crafting this PR I kind of like this, since will avoid any duplicate code. Bogdan's current proposal This is the current PR, which combines the immutable and separation of concerns (just for your information the mutable and separation of concerns is impossible to build with the current pdata framework without an extra allocation OR usage of Retrieving a value: isSampled := logRecord.Flags().TraceFlags().IsSampled() Updating a value inside TraceFlags part: lf := logRecord.Flags()
logRecord.SetFlags(lf.WithTraceFlags(lf.TraceFlags().WithIsSampled(true)) Updating a value outside TraceFlags part: logRecord.SetFlags(logRecord.Flags().WithFoo(true)) Accessing raw: rawFlags = logRecord.Flags() Updating from raw: logRecord.SetFlags(rawFlags) Accessing raw trace context only part: rawTraceFlags = logRecord.Flags().TraceFlags() Updating from raw trace context only part: logRecord.SetFlags(logRecord.Flags().WithTraceFlags(rawTraceFlags) |
I assume this is an example of possible such addition, right?
This proposal aligns with what I was suggesting. I think it is the strongest from the perspective of safety and separation of concerns. I am only reluctant because the ergonomics are not very good.
Where do we need these raw functions? |
Yes
I believed so, since was inspired by your comments.
Next section has a summary of current usages of the funcs: Where do we need these getters?
Where do we need these setters?
Where do we need these raw functions?
In general, I don't see a use-case for setters except conversions between formats, of something like the "transform" processor. In the type conversions we have lots of "exporters" so the getters are more important there. |
@bogdandrutu thank you for the in-depth analysis. Understanding the situation better, I can see why we'd do something like isSampled := logRecord.SetFlags(logRecord.Flags().WithIsSampled(true)) to achieve immutability. I think my dislike is stemming from the ergonomics introduced by lf := logRecord.Flags()
logRecord.SetFlags(lf.WithTraceFlags(lf.TraceFlags().WithIsSampled(true)) Personally, I'd rather duplicate code than introduce that API, especially if the duplicated code could get autogenerated by pdata. Also, I anticipate there being questions raised as to why the API for updating the flags is different for flags inside the trace part vs outside the trace part. Not saying that is isn't correct, only that I think it will raise questions our community will have to answer. |
Yes. I think there is value in separation of concerns, but it is clear that it results in less ergonomic API. I don't think it is possible to make this choice objectively, so let's make a subjective call ("I like this and not that"). I am fine with whatever we choose approach, no strong preference.
I support this. It is a small amount of duplication. The TraceFlags are unlikely to become something complicated. It is a small surface area and it is not a big deal to duplicate the API where it is needed. |
Signed-off-by: Bogdan <[email protected]>
35a4629
to
f59888f
Compare
This is not a breaking change, since the LogRecordFlags and the getter/setter to LogRecords were never released.
Even though the Contrib tests are failing, this is expected because contrib was updated to an unrelease version to simplify the next release.
Signed-off-by: Bogdan [email protected]