-
Notifications
You must be signed in to change notification settings - Fork 107
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
Updates to the propagators interface #269
Conversation
Codecov Report
@@ Coverage Diff @@
## main #269 +/- ##
==========================================
- Coverage 36.39% 36.34% -0.06%
==========================================
Files 41 43 +2
Lines 3165 3178 +13
==========================================
+ Hits 1152 1155 +3
- Misses 2013 2023 +10
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report at Codecov.
|
d4578c8
to
15ba248
Compare
This change makes propagators use a behaviour to define the inject and extract functions instead of being anonymous functions. The latest Otel spec is also matched by adding functions for getting all fields a propagator sets and allowing the overriding of the get/set/keys functions used by inject/extract.
15ba248
to
a3a3b76
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.
Looking good to me! This will make other propagators easier to write now.
I do think there is benefit to the composite propagators requirement and that we should implement it even if it comes in a different PR.
%% @end | ||
%%%----------------------------------------------------------------------- | ||
-module(otel_propagator_http_w3c). | ||
-module(otel_propagator_trace_context). |
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.
Is there any harm in continuing to denote this as w3c like we do with b3?
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 harm. I changed it because I looked at the other implementations and they all seemed to use "trace context". I'm going to double check and look at some more of them to see if it is really all of them or just Go and Python.
Co-authored-by: Bryan Naegele <bryannaegele@users.noreply.github.com>
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.
So I'm curious about what's the exact extent of the interface change there, and to which extent it relates to the spec? You mentioned writing in the changelog (not part of the PR), but I'm wondering how this reflects in versioning since there's the spec version and the lib version.
Co-authored-by: Fred Hebert <mononcqc@ferd.ca>
Oops, was supposed to be a changelog included in this PR. I'll get that added and hopefully it answers your question. |
Added the chagnelog but realized it doesn't answer your question. The changes aren't really spec related. Everything still acts the same it is just clearer (I think). Before the propagators configuration value was a list of functions which returned a 2-tuple of The individual propagator inject/extract functions look more different than they are in this PR because the earlier version abstracted some parts around context out by providing a function in Now all that context stuff is done in each propagator, making it clearer to follow. Oh, one thing that was added to match the spec (though in the spec it is optional, so we weren't not meeting the spec requirements) is allowing a getter and setter to be passed for a carrier. |
@bryannaegele sorry, I missed your comment earlier about the composite propagator. Can you expand on why you think it is useful? My understanding is that the only effect having a composite propagator would have is configuration would have to be something like:
|
Correct. And enable users to define their own composites. It's a requirement to offer it according to the spec which is why I brought it up.
It's been marked as |
Right, it is Maybe I'm missing part of its required functionality, what do you mean by users creating their own? How would it be different than the list of propagators the user gives now? |
Users can create custom propagators. I believe in the current implementation the only way to configure which are in use is via static config, but maybe that's wrong? I guess I'm just not clear on why it's un-applicable to Erlang. I thought the only justification for skipping any requirement in the spec was for language limitations, not implementation choices. |
Being not applicable can also mean the functionality doesn't fit or isn't needed in an implementation. I don't see what it adds in our case other than making the configuration that'll be used 99% of the time confusing. I'm still open to adding it but want to make sure where either it or a non-composite propagator (as opposed to a single element list like it is now) would be used, and think we are likely talking pased each other and once we figure out the miscommunication one of us will be happy with the others solution :) |
I'm not unhappy with this solution :) I'm just asking for my own information and a broader discussion (we can take it elsewhere) where we are and are not saying non-optional spec requirements are ok to ignore if it isn't due to a language/runtime limitation. In this particular discussion about composites, having a way to create a composite propagator would simplify the interface for creating a single The other situation that is relevant - correct me if I'm wrong - is we only have a global propagator implementation afaik. There aren't any facilities to scope which propagators are in use at a call level. Calling Using w3c as an example, traceparent and baggage are the defaults as a list. For clients calling within your infra, that's probably just fine. But for external client calls to third parties, you probably don't want to be sending baggage. Or maybe your default case is the opposite, where most calls are external, so you omit baggage, then want to add it for internal calls. In that use case, you have to create your own "composite" or use a provided one which keeps the same API semantics. Maybe one solution to investigate (outside this PR - I think this PR is fine) is implementing this I hear the argument that 99% of users may not care about it. Maybe you think it's a superfluous requirement. But it's there as a We can move this to a discussion or when I get around to the AMQP contrib then maybe I can open a PR with the example implementation so we have something more concrete to discuss. :) |
Oh, this reminded me I never mentioned how I think the AMQP case may be solved in this PR. If I understood correctly the issue was the AMQP headers were a 3-tuple, the key, value and type? Now that a custom And I didn't mean 99% wouldn't care, its 100% that shouldn't care (assuming this all works the same as the spec which I think it does) but 99% that won't even notice because setting a single propagator that isn't composite isn't a concern to them. Since really we only have a composite propagator, there is no non-composite for us. The point about using a different set of propagators for a different set of calls is a good one since I don't think we have a way to do that anymore after this PR :). It needs to export the function that takes a list of propagators to the user instead of only making use of the global propagators. I want to add that and tests before merging this. Hm, I guess we'd have to go back to using a closure or carrying some state returned by a propagator "create" function if a composite propagator was added, since it'll need state. I at least now think I see where you are coming from. You are thinking, "I want to define a propagator X I use in certain places that combines Y and Z", and right now that is only done by setting a list of global propagators? Which I think is fine as a list, but I see now why I might be wrong and need to look this over some more. Am I at least heading in the right direction with where you are coming from? |
Pretty much. It's the ability to let folks even have a simple abstraction of creating and storing their own composite propagators and still having the inject/extract interface. There are shortcomings I feel in the spec around all of this, to be sure. The closest thing to flexibility is the ability to add dependency injection support so you could at least have a simple data structure and reduce a list of propagators. |
Yea, the spec I guess assumes users will call propagators directly and not use the general propagator interface that uses the global ones when they need this. Of course that could end badly if a library thinks it knows better or it simply makes a mistake and renders the user of the library incapable of overriding the propagators it uses -- but maybe not way around that. Anyway, doing so was possible before this PR and I'll be updating it to the same today. The question then becomes do we need propagators with state so must do:
Or keep them as they are in this PR but support:
And I should have mentioned that a reason for being |
4b243ec
to
9a210ef
Compare
Oh to have a struct 😆 If we added the composite create and let's say used a tuple or record for the data structure, I think we can support everything. I also don't think we have to scope the create to a type of propagator as we don't have a way to verify what it is, I believe. It would be up to the user to not mix and match because the carrier would be different? It's probably worthwhile to keep the inject interface remain the same and just add a Propagators = otel_propagators:create_composite([a, b, c]),
%% {composite_propagator, [a,b,c]}
otel_propagator_text_map:inject(Carrier, {composite_propagator, Propagators}).
otel_propagator_text_map:inject(Carrier, Propagator) when is_atom(Propagator). That maybe gives us additional flexibility to allow passing a single propagator by atom as dependency injection for a single or composite. |
Since a new option to inject/extract was needed I've added 2 new functions `inject_from` and `extract_to` that take Contexts as the first argument. Otherwise there is a lot of overloading of types of arguments required.
I've updated the PR to support passing the propagators to |
@bryannaegele there needs to be the So |
this function is no longer needed in otel_baggage because propagators are no longer anonymous functions but instead modules.
Closing after merging #273 |
Opening as a draft because I still need to add a lot of comments/docs and am not 100% on breaking the old configuration.
One thing I like about using closures for stuff like inject/extract (which this PR removes and instead just calls
inject
orextract
on a module name) is it gives the ability to add runtime state into the functions without having to pass around an options variable. But probably not worth it or needed for this case and is cleaner the way it is now.Also going to add a Changelog to better document how to upgrade.