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

Make SpanContext sealed #359

Merged
merged 6 commits into from
Nov 13, 2023

Conversation

iRevive
Copy link
Contributor

@iRevive iRevive commented Nov 10, 2023

Reference Link
Spec https://opentelemetry.io/docs/specs/otel/trace/api/#spancontext
Java implementation SpanContext.java

Motivation

Once the trait is sealed, we are in control of the implementations. And we can define a safe Hash instance.


otel4s-java-trace uses OpenTelemetry Java SDK under the hood.

Currently, we need to convert SpanContext to JSpanContext in 3 cases:

  1. When SpanBuilder has links: e.g. Tracer[F].spanBuilder("child").addLink(otherSpan.context)
  2. When we provide an explicit parent: Tracer[F].spanBuilder("child").withParent(otherSpan.context)
  3. When we use a child scope: Tracer[F].childScope(otherSpan.context)

I've experimented with delegated instance too, see SpanContext and WrappedSpanContext.

However, Arman raised a valid concern that this approach violates the specification:

Warning

The API MUST implement methods to create a SpanContext. These methods SHOULD be the only way to create a SpanContext. This functionality MUST be fully implemented in the API, and SHOULD NOT be overridable

Considering that we need to convert SpanContext to JSpanContext rarely, in my opinion, we don't need a delegate instance.

WDYT?

@iRevive iRevive added the tracing Improvements to tracing module label Nov 10, 2023
@iRevive iRevive force-pushed the sdk-trace/sealed-span-context branch from 9c58514 to 762a460 Compare November 10, 2023 11:50
@armanbilge
Copy link
Member

sealing it is the first breaking API change since 0.3.0 was released (🎉) so we need to bump the base version.

ThisBuild / tlBaseVersion := "0.3"

TraceFlags.fromByte(jSpanContext.getTraceFlags.asByte)

lazy val traceState: TraceState = {
def wrap(context: JSpanContext): SpanContext = {
Copy link
Member

Choose a reason for hiding this comment

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

Nit: is wrap the correct terminology anymore?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can rename WrappedSpanContextto SpanContextConversion?

object SpanContextConversion {
  def toJSpanContext(ctx: SpanContext): JSpanContext
  def toSpanContext(ctx: JSpanContext): SpanContext // or fromJSpanContext
}

context.getTraceState.forEach((k, v) => entries.addOne(k -> v))
val traceState = TraceState.fromVectorUnsafe(entries.result())

SpanContext(
Copy link
Member

Choose a reason for hiding this comment

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

This is re-validating it right? Probably not a big deal but I suppose we could use some sort of unsafe constructor to skip validation in this specific case.

Copy link
Contributor Author

@iRevive iRevive Nov 10, 2023

Choose a reason for hiding this comment

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

Yes, it will re-validate inputs. We can use SpanContext.createInternal instead.

@iRevive
Copy link
Contributor Author

iRevive commented Nov 10, 2023

sealing it is the first breaking API change since 0.3.0 was released (🎉) so we need to bump the base version.

ThisBuild / tlBaseVersion := "0.3"

I forgot. It's a breaking API change, indeed. My disappointment is immeasurable and my day is ruined.

It means once we merge the PR, we cannot release patches (e.g. bugfix) for 0.3 anymore.

Should we make two 0.3.x and 0.4.x series-branches? But I have a feeling it's kinda an overkill for our adaptation level.

@armanbilge
Copy link
Member

armanbilge commented Nov 10, 2023

If the need to publish bug fixes arises, we can cut a series/0.3 branch from the v0.3.0 tag. Meanwhile we can continue using main for breaking development.

Edit: another option if there are bug fixes to release is to just declare v0.4.0, depending on the state of main.

@iRevive iRevive force-pushed the sdk-trace/sealed-span-context branch from 770d804 to dc2f0ed Compare November 10, 2023 18:18
Copy link
Member

@armanbilge armanbilge left a comment

Choose a reason for hiding this comment

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

LGTM but I look forward to finding out what I missed when April takes a look 😅

Copy link
Contributor

@NthPortal NthPortal left a comment

Choose a reason for hiding this comment

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

no significant concerns—just a few nitpicks and some bikeshedding

import org.typelevel.otel4s.trace.TraceState
import scodec.bits.ByteVector

private[otel4s] object SpanContextConversion {
Copy link
Contributor

Choose a reason for hiding this comment

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

should probably be called SpanContextConversions (plural)


private[otel4s] object SpanContextConversion {

def fromJSpanContext(context: JSpanContext): SpanContext = {
Copy link
Contributor

Choose a reason for hiding this comment

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

thoughts on toScala for this one and toJava for the other? a bit shorter name-wise and consistent with what the stdlib does for its conversions.

how often will end users need to use these conversions? if it's not super rare, extension methods would probably be a nice touch, though I'm happy to handle that if it ends up being needed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Much better than fromJSpanContext and toJSpanContext, thanks!

Comment on lines 187 to 188
ctx.traceIdHex,
ctx.spanIdHex,
Copy link
Contributor

Choose a reason for hiding this comment

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

any particular reason to use the hex Strings for the Hash rather than the ByteVectors?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is no Hash[ByteVector] instance, unfortunately.

Copy link
Member

Choose a reason for hiding this comment

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

Oh, we can inline a quickie one with Hash.fromUniversalHashCode(). I think instances might live in scodec-cats but not worth dragging that in ...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is there any benefit of using traceIdHex vs traceId? Since we treat traceId as a 'primary' property, we should use it in hashing too, right?

Copy link
Member

Choose a reason for hiding this comment

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

Since we treat traceId as a 'primary' property, we should use it in hashing too, right?

That's my thinking as well.

isValid = isValid
)

private final case class SpanContextImpl(
Copy link
Contributor

Choose a reason for hiding this comment

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

extreme nitpick: should this just be called Impl, as it's already inside the SpanContext companion?

@NthPortal
Copy link
Contributor

I look forward to finding out what I missed when April takes a look

if it doesn't have to do with the collections or at least the stdlib, usually nothing (as demonstrated)

# Conflicts:
#	java/trace/src/main/scala/org/typelevel/otel4s/java/trace/WrappedSpanContext.scala
@iRevive iRevive force-pushed the sdk-trace/sealed-span-context branch from a2c1349 to 2a1f332 Compare November 13, 2023 18:32
@iRevive iRevive merged commit 88d43bb into typelevel:main Nov 13, 2023
10 checks passed
@iRevive iRevive deleted the sdk-trace/sealed-span-context branch November 13, 2023 19:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
tracing Improvements to tracing module
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants