Skip to content
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

Prototypes proposed spec changes to allow extraction of multiple header values #5973

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

jamesmoessis
Copy link

  • Adds MultiTextMapCarrier, extending TextMapCarrier - adds GetAll(string) []string.
  • Implements MultiTextMapCarrier for HeaderCarrier
  • Change propagator.Baggage to use the GetAll() method if it's implemented.
  • Gives example tests extracting requests with multiple 'baggage' headers set.

Does not introduce any breaking changes or alter any existing tests.

Spec issue: open-telemetry/opentelemetry-specification#433
Corresponding Java prototype: open-telemetry/opentelemetry-java#6852

…, extending TextMapCarrier.

Gives example extracting requests with multiple 'baggage' headers set.
@jamesmoessis jamesmoessis changed the title Prototypes proposed spec changes to allow multiple header values Prototypes proposed spec changes to allow extraction of multiple header values Nov 14, 2024
Copy link

codecov bot commented Nov 14, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 84.6%. Comparing base (d428313) to head (530b431).

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##            main   #5973     +/-   ##
=======================================
- Coverage   84.6%   84.6%   -0.1%     
=======================================
  Files        272     272             
  Lines      22897   22918     +21     
=======================================
+ Hits       19391   19401     +10     
- Misses      3162    3173     +11     
  Partials     344     344             

see 2 files with indirect coverage changes

propagation/propagation.go Show resolved Hide resolved
propagation/propagation.go Show resolved Hide resolved
multiCarrier, isMultiCarrier := carrier.(MultiTextMapCarrier)
if isMultiCarrier {
return extractMultiBaggage(parent, multiCarrier)
}
Copy link
Member

Choose a reason for hiding this comment

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

Not as gross as I expected 👍

Copy link
Member

@pellared pellared left a comment

Choose a reason for hiding this comment

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

I think your prototype is good. For an actual PR we would need to improve the docs and call out the MultiTextMapCarrier in more places.

PS. Not feeling well today so treat my review with low confidence (like an pre-approval). I will do my best to look at it again once I recover and have a clearer mind.

@pellared
Copy link
Member

@open-telemetry/go-maintainers, PTAL at this prototype

@@ -29,6 +29,19 @@ func (b Baggage) Inject(ctx context.Context, carrier TextMapCarrier) {

// Extract returns a copy of parent with the baggage from the carrier added.
func (b Baggage) Extract(parent context.Context, carrier TextMapCarrier) context.Context {
multiCarrier, isMultiCarrier := carrier.(MultiTextMapCarrier)
Copy link
Member

@pellared pellared Nov 18, 2024

Choose a reason for hiding this comment

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

AFAIK type assertions can introduce an additional heap allocation. The extraction is on the hot path.
Can you add some benchmark for this method and add benchstat results to the PR description so that we can see what is the performance overhead?

Can you also implement the MultiTextMapCarrier for TraceContext so that we can also use the existing BenchmarkExtract to validate the performance overhead for TraceContext (which is probably more popular than Baggage)?

PS. I do not expect anyone would say that an additional heap allocation would be a blocker. Maybe at some point Go runtime would not make a heap allocation when doing a type assertion. Still, we should be aware of the performance consequences of a new functionality.

Copy link
Member

@pellared pellared Nov 26, 2024

Choose a reason for hiding this comment

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

I made a quick checkout of the PR and the gopls with gc_details enabled has not shown that there is any additional heap allocation.

func (b Baggage) Fields() []string {
return []string{baggageHeader}
func extractMultiBaggage(parent context.Context, carrier MultiTextMapCarrier) context.Context {
bVals := carrier.GetAll(baggageHeader)
Copy link
Member

Choose a reason for hiding this comment

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

We could return early here if bVals is empty.
That's a nit, but maybe that shows we would need more benchmarks, especially with @pellared's comment above.

type MultiTextMapCarrier interface {
TextMapCarrier
// GetAll returns all values associated with the passed key.
GetAll(key string) []string
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we name this Values to match the http.Header?

https://pkg.go.dev/net/http#Header.Values

Copy link
Author

@jamesmoessis jamesmoessis Nov 26, 2024

Choose a reason for hiding this comment

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

Spec is defining GetAll not Values. Is there any benefit to matching http.header? As mentioned elsewhere in this PR, http.header does not implement TextMapCarrier in the first place.

Copy link
Contributor

Choose a reason for hiding this comment

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

It matches expected naming by Go users

Copy link
Contributor

@MrAlias MrAlias Nov 27, 2024

Choose a reason for hiding this comment

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

Also, please see the other comment about interface design. Not embedding will allow greater usability, especially with the stdlib Header.

Copy link
Author

Choose a reason for hiding this comment

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

I suppose we are talking about a tradeoff between matching the otel spec (other language implementations of same spec), and matching the http.Header API. I don't really mind - what matters most to me is having correctness of propagation, which we currently don't have. So, if it's going to move the PR along then I'm happy to change the name to deviate from the name proposed in the spec.

@@ -29,6 +29,15 @@ type TextMapCarrier interface {
// must never be done outside of a new major release.
}

// MultiTextMapCarrier is a TextMapCarrier that can return multiple values for a single key.
type MultiTextMapCarrier interface {
TextMapCarrier
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this need to be embedded. It limits the composability.

Copy link
Member

Choose a reason for hiding this comment

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

I was also thinking about two interfaces; one defining the method, second embedding two interfaces. Inspired that in io package there is e.g. https://pkg.go.dev/io#ReadCloser.

Copy link
Author

@jamesmoessis jamesmoessis Nov 27, 2024

Choose a reason for hiding this comment

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

Yeah, I definitely like this suggestion of improving the composability.

Would something along these lines be what you are thinking?

type MultiGetter interface {
  GetAll(key string) []string
}

type MultiTextMapCarrier interface {
  TextMapCarrier
  MultiGetter
}

jack-berg added a commit to open-telemetry/opentelemetry-specification that referenced this pull request Nov 28, 2024
…ey (#4295)

Fixes #433

Discussed in 11/5/24 Spec SIG, and prototyped in Java and Go.

## Changes

Adds `GetAll` method to Getter, allowing for the retrieval of multiple
keys for the same value. For example, an HTTP request may have multiple
`baggage` headers.

As written in the changes, the implementation of `GetAll` is strictly
optional. This is for two reasons:
1. Backwards compatibility with existing types/interfaces
2. Carriers do not necessarily support returning multiple values for a
single key

## Links to Prototypes

This includes implementations of `GetAll()` in Java and Go, as well as
using the method in the W3C Baggage Propagators (multiple baggage
headers are allowed, [spec
reference](https://www.w3.org/TR/baggage/#baggage-http-header-format)).

- Java: open-telemetry/opentelemetry-java#6852
- Go: open-telemetry/opentelemetry-go#5973

* [x] Links to the prototypes (when adding or changing features)
* [x]
[`CHANGELOG.md`](https://github.com/open-telemetry/opentelemetry-specification/blob/main/CHANGELOG.md)
file updated for non-trivial changes

---------

Co-authored-by: Trask Stalnaker <[email protected]>
Co-authored-by: jack-berg <[email protected]>
Co-authored-by: Robert Pająk <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants