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

187 B3 Headers Support #188

Merged
merged 1 commit into from
Aug 7, 2018
Merged

187 B3 Headers Support #188

merged 1 commit into from
Aug 7, 2018

Conversation

cshoe
Copy link
Contributor

@cshoe cshoe commented Jun 29, 2018

Add support for B3 trace headers whil continuing to support the old
trace headers. This change aims to allow both both B3 and the old
headers to be used as long as they contain identical values. For
example:

X-Trace: 1234567890
X-B3-TraceId: 1234567890

Fixes #187

@cshoe
Copy link
Contributor Author

cshoe commented Jun 29, 2018

👓 @spladug @pacejackson

Copy link
Contributor

@spladug spladug left a comment

Choose a reason for hiding this comment

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

cool! I hadn't seen that they'd standardized the B3 headers. very neat.

LOWER_HEX_16 = re.compile(r"\A[0-9a-f]{16}\Z")
LOWER_HEX_32 = re.compile(r"\A[0-9a-f]{32}\Z")

LOWER_HEX_16_BYTES = re.compile(b"\A[0-9a-f]{16}\Z")
Copy link
Contributor

Choose a reason for hiding this comment

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

did you mean to do b""?

@@ -0,0 +1,122 @@
import re
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: this file is missing the from __future__ stuff which should be standard in all baseplate files.

@@ -80,7 +82,8 @@ def getHandlerContext(self, fn_name, server_context):
# just attach the raw context so it gets passed on
# downstream even if we don't know how to handle it.
context.raw_request_context = edge_payload
except (KeyError, ValueError):
except (KeyError, ValueError) as exc:
logger.debug(exc)
Copy link
Contributor

Choose a reason for hiding this comment

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

?

LOWER_HEX_32_BYTES = re.compile(b"\A[0-9a-f]{32}\Z")


class TraceHTTPHeaders(object):
Copy link
Contributor

Choose a reason for hiding this comment

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

is there any reason for this to be a class? rather than doing inheritance shenanigans it could just be a function that returns a namedtuple (which also saves you from using ["dict lookup"] style stuff) and we can have two variants for thrift and http.

Copy link
Contributor

Choose a reason for hiding this comment

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

in fact, it feels a little like we're building an object (TraceHTTPHeaders) just to turn it into another object (TraceInfo). can we just build the TraceInfo directly?

if header_name in headers:
values.append(headers[header_name])

if len(values) == 0:
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: if not values: is more pythonic

continue
# make sure all found values are equal
# http://stackoverflow.com/q/3844948/
elif values.count(values[0]) != len(values):
Copy link
Contributor

Choose a reason for hiding this comment

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

might be a little easier to understand as elif not all(value == values[0] for value in values):

# make sure all found values are equal
# http://stackoverflow.com/q/3844948/
elif values.count(values[0]) != len(values):
raise ValueError("Conflicting values found for %s header(s)", header_names)
Copy link
Contributor

Choose a reason for hiding this comment

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

i don't think valueerror does any string formatting does it?

@@ -108,31 +108,13 @@ def new(cls):
def from_upstream(cls, trace_id, parent_id, span_id, sampled, flags):
"""Build a TraceInfo from individual headers.

:param int trace_id: The ID of the trace.
Copy link
Contributor

Choose a reason for hiding this comment

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

what's rollout for this look like if something suddenly starts sending long stringy IDs to downstream services that haven't been upgraded yet? are all the baseplate observers we have OK with this changing from int to str?


HEX_16_RE = LOWER_HEX_16
HEX_32_RE = LOWER_HEX_32
VALID_SAMPLED_VALUES = ("0", "1")
Copy link
Contributor

Choose a reason for hiding this comment

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

the fact that they're strings on the wire feels like something the app shouldn't have to care about. can we decode the trace IDs into consistent integers within the application always? and the "sampled" flag into a boolean? it feels painful to make the app have to check against == "1" rather than just testing truthiness.


"""
HEADER_NAMES = {
"trace_id": ["X-Trace", "X-B3-TraceId"],
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we use tuples rather than lists for these? Tuples are a little smaller and immutable.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, the way these are ordered will favor the old header format over the new, maybe we should put the new one first?



class TraceHTTPHeaders(object):
"""Helper class for trace headers in an HTTP call.
Copy link
Contributor

Choose a reason for hiding this comment

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

If this is it for the docstring, I think you can fit the closing """ on the same line.

@cshoe
Copy link
Contributor Author

cshoe commented Aug 3, 2018

💇

@spladug @pacejackson Thanks for the feedback. My first approach was way too heavy handed and over engineered. I tried to greatly simplify things and focus solely on getting Baseplate to look at B3 headers in addition to those it already supported.

Copy link
Contributor

@spladug spladug left a comment

Choose a reason for hiding this comment

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

Awesome, that's much more surgical. Exciting to move towards standardized header details. Only concern is exposing the TraceInfo class to the per-protocol concerns rather than keeping it idealized.

flags=flags,
)

trace_info = TraceInfo.from_http_headers(request.headers)
Copy link
Contributor

Choose a reason for hiding this comment

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

It's kinda a bummer to make TraceInfo have to be aware of the different protocols we're using; if we added more down the line that'd mean more direct modifications in that class rather than just adding the new protocol definitions. The core stuff is meant to be the idealized form of the tracing and then these integrations are where that meets the actual wire. The centralized extraction logic could potentially be exposed as a helper that these integrations use though?

@cshoe
Copy link
Contributor Author

cshoe commented Aug 7, 2018

💇

Copy link
Contributor

@spladug spladug left a comment

Choose a reason for hiding this comment

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

awesome! a couple of comments that are non-blocking

@@ -135,6 +136,62 @@ def from_upstream(cls, trace_id, parent_id, span_id, sampled, flags):

return cls(trace_id, parent_id, span_id, sampled, flags)

@classmethod
def extract_upstream_header_values(cls, upstream_header_names, headers):
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it make sense just to merge this into from_upstream ? I don't think we'd ever use one without the other?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah... I wanted to do that but there are subtle differences in how the two protocols handle the header values due to Thrift operating in bytes and HTTP using strings.

https://github.com/reddit/baseplate/pull/188/files#diff-db4cf04c08224f72cb9627e1f9866bbcR121
https://github.com/reddit/baseplate/pull/188/files#diff-d1382533c32ca95376190e31679acc05R162

Some type checking could be done. Or possibly a generic

True if extraced_values["sampled"] in ("1", b"1")

but this started to feel protocol specific again. If others are added, other protocol specific edge cases could arise making the from_upstream method kind of gnarly. I'm happy to take a stab at it though, if you'd like.

Copy link
Contributor

Choose a reason for hiding this comment

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

👍 alrighty, makes sense. thanks

if not values:
continue
elif not all(value == values[0] for value in values):
raise ValueError("Conflicting values found for %s header(s)".format(header_names))
Copy link
Contributor

Choose a reason for hiding this comment

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

do we want to crash the whole request if the headers are iffy? (i could see either answer being reasonable)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I raised this to follow what is being done in new. Where this is called is wrapped so the request doesn't get nuked but rather the trace data is not propagated.

Copy link
Contributor

Choose a reason for hiding this comment

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

ah, perfect. thanks!

Add support for B3 trace headers whil continuing to support the old
trace headers. This change aims to allow both both B3 and the old
headers to be used as long as they contain identical values. For
example:

```
X-Trace: 1234567890
X-B3-TraceId: 1234567890
```

Fixes reddit#187
@cshoe cshoe merged commit 46db4cd into reddit:master Aug 7, 2018
@cshoe cshoe deleted the 187-b3-headers branch August 7, 2018 17:51
Kaitaan pushed a commit that referenced this pull request Nov 1, 2018
Add support for B3 trace headers whil continuing to support the old
trace headers. This change aims to allow both both B3 and the old
headers to be used as long as they contain identical values. For
example:

```
X-Trace: 1234567890
X-B3-TraceId: 1234567890
```

Fixes #187
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants