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

Implement InvocationSpanContext for user tracing / streaming tail workers #3028

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

jasnell
Copy link
Member

@jasnell jasnell commented Oct 30, 2024

This implements the InvocationSpanContext class. This type holds a tuple of trace ID, invocation ID, and span ID and will be used in support for the updated streaming tail workers implementation. The basic idea is that every top-level request receives a trace ID which is shared across every invocation the occurs within that request. Every individual invocation has a unique invocation ID, and every span within that invocation receives a monotonically increasing span ID.

While this PR does not currently introduce uses of this type, the idea is for all invocations to always have a root InvocationSpanContext and for every child user span created to branch off this root. The current user span then would always have a current InvocationSpanContext that would be propagated outward for all traceable sub requests (generally any subrequest that is not an public internet fetch). A follow up PR will start to introduce the uses and propagation of the span context. This PR only introduces the class impl an the basic test for it. (I'm intentionally breaking this work into smaller incremental PRs to make it easier to review individual chunks).

@fhanau
Copy link
Collaborator

fhanau commented Oct 30, 2024

Don't merge just yet – will review today

@jasnell
Copy link
Member Author

jasnell commented Oct 30, 2024

Don't merge just yet – will review today

Yep, figured as much. Want to make sure this is going to be good with the work you're doing

src/workerd/io/trace.c++ Outdated Show resolved Hide resolved
Comment on lines +209 to +210
// If the traceId or invocationId are invalid, then we'll ignore them.
if (!sc->getTraceId() || !sc->getInvocationId()) return kj::none;
Copy link
Member

Choose a reason for hiding this comment

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

At this point, maybe we should just have a helper method to check validness.

Suggested change
// If the traceId or invocationId are invalid, then we'll ignore them.
if (!sc->getTraceId() || !sc->getInvocationId()) return kj::none;
// If the traceId or invocationId are invalid, then we'll ignore them.
if (!sc->hasTraceId() || !sc->hasInvocationId()) return kj::none;

Copy link
Member Author

Choose a reason for hiding this comment

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

Are you suggesting renaming these because there is no hasTraceId() or hasInvocationId(). These both return const TraceId& which implements an operator bool(), which means these work as is.

// monotically increasing number. Every InvocationSpanContext has a root span
// whose ID is zero. Every child span context created within that context will
// have a span id that is one greater than the previously created one.
class SpanIdCounter final: public kj::Refcounted {
Copy link
Collaborator

Choose a reason for hiding this comment

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

SpanIdCounter felt overengineered at a first glance – using an increasing unsigned counter value correctly should not be difficult as long as it only needs to be done within the span implementation itself. I take that this is being used for counter memory management so that it is easier to acquire increasing, unique span IDs across several "branches" of the trace?

Copy link
Member Author

@jasnell jasnell Nov 3, 2024

Choose a reason for hiding this comment

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

Yes, that's exactly it. I didn't put much effort into designing this bit and just Did Something That Works. If you have an alternative suggested approach here I'm happy to change this part. The key requirement is that every descendent span should be assigned the next monotonically increasing value without those having to be aware of each other in any other way.

e.g.

root span (0)

create child span off root (1)

create child span off root (2)

create child span off 1 (3)

create child span off 3 (4)

create child span off 2 (5)

....


inline kj::uint next() {
static constexpr kj::uint kMax = kj::maxValue;
KJ_ASSERT(id < kMax, "max number of spans exceeded");
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not objecting to this assert, but if 2^32 spans are reached we have bigger problems 😄

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh, I meant for this to be a KJ_DASSERT but this is just me being way over paranoid lol.

traceId.toCapnp(writer.initTraceId());
invocationId.toCapnp(writer.initInvocationId());
writer.setSpanId(spanId);
kj::mv(getParent());
Copy link
Collaborator

Choose a reason for hiding this comment

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

Where does the parent context move, or is this just used to invalidate it?

Copy link
Member Author

Choose a reason for hiding this comment

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

Just invalidates.

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.

3 participants