Skip to content
This repository has been archived by the owner on May 23, 2023. It is now read-only.

WIP: Separate SpanContext from SpanBuilder #68

Closed
wants to merge 7 commits into from

Conversation

mabn
Copy link
Contributor

@mabn mabn commented Nov 15, 2016

This is an attempt to remove SpanContext interface from SpanBuilder. See #55

Apparently it has various repercussions...

There are two places where SpanContext should be created:

  1. when creating new span - based on parent span context + some span id generation strategy. Good place for that is AbstractSpanBuilder, createSpan() in particular
  2. when extracting SpanContext from a carrier. It’s based on baggage and trace state keys.

One option would be to create AbstractSpanContext and then AbstractSpanContextBuilder which would have methods like withTraceState or withBaggageItem, but to keep it simple I think it’s ok to reuse just stick to immutable AbstractSpanContext (where mutations create new instances).
Still, there is a need for some way to instantiate concrete subclass - that's why I've added createSpanContext(traceState) to AbstractTracer.

I did few other changes:

  • moved AbstractSpanBuilder.isTraceState to AbstractTracer as it was no longer needed there and there was already isTraceState in the tracer
  • removed SpanBuilder.withStateItem - no longer needed

But I've run into problems with Noop implementation.
Right now there are two - one in -impl, the other in -noop. AbstractSpanBuilder asChildOf(Span parent) - requires NoopSpanBuilder to extends AbstractSpanBuilder but on the other hand AbstractSpanBuilder asChildOf tests against NoopSpanContext from opentracing-noop
which cannot depend on AbstractSpanContext (because circular dependency).

I think that it needs to be cleaned up first, especially:

  • not returning Noop implementations from AbstractTracer because this forces:
    • either Noop implementations to extend abstract implementation
    • or the abstract implementation to declare interfaces as return types and then downcast
  • get rid of one of noop implementations :)

(Note - the this is not ready to merge)

Marcin Biegan added 5 commits November 15, 2016 02:17
SpanBuilder should not extend SpanContext - these types have different
responsibilities. In particular SpanBuilder does not need identifiers
like trace_id or span_id.
The code using SpanContext was updated to use AbstractSpan instead
(which also implements SpanContext).
In addition Extractor now extract SpanContext instead of SpanBuilder.
- AbstractTracer now creates AbstractSpanContext based on the trace state
- Introduced NoopSpanContext with the assumption that it will be
  reworked during some cleanup of opentracing-noop module
- Removed assert from AbstractSpan.setBaggageItem - a better place for
  this check will be inside AbstractSpanContext once it allows adding
  baggage items
Instead SpanContext is an attribute of AbstractSpan

Unfortunately there's a mess with NoopSpanContexts - there are two
and both are used.
@yurishkuro
Copy link
Member

(Note - the this is not ready to merge)

Tip: prepend "WIP - " to the title, and remove when ready for review/merge

return new AbstractSpanContext(traceState, baggage, tracer);
}

public AbstractSpanContext setBaggageItem(String key, String value) {
Copy link
Member

Choose a reason for hiding this comment

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

Just an FYI - in basictracer implementations in other languages there is only withBaggage(key, value) method, which creates a new immutable ctx.

@@ -35,6 +34,8 @@ protected AbstractTracer() {
}

abstract AbstractSpanBuilder createSpanBuilder(String operationName);

abstract AbstractSpanContext createSpanContext(Map<String, Object> traceState);
Copy link
Member

Choose a reason for hiding this comment

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

If I were designing this abstract implementation, I would've done it as:

abstract class AbstractTracer<SPAN extends AbstractSpan, SCTX extends AbstractSpanContext> ... {
    abstract SPAN createSpanContext(Map<String, Object> traceState);
}

The implementations then would deal with their own concrete types instead of abstract superclass, and the abstract tracer would take care of the necessary downcasting (as long as it gets a concrete Class in the constructor)

@michaelsembwever
Copy link
Contributor

But I've run into problems with Noop implementation.
Right now there are two - one in -impl, the other in -noop. AbstractSpanBuilder asChildOf(Span parent) - requires NoopSpanBuilder to extends AbstractSpanBuilder but on the other hand AbstractSpanBuilder asChildOf tests against NoopSpanContext from opentracing-noop
which cannot depend on AbstractSpanContext (because circular dependency).

This is intentional. Each implementation might want to override (re-implement) the noops.

@michaelsembwever
Copy link
Contributor

michaelsembwever commented Nov 15, 2016

I did few other changes:

  • moved AbstractSpanBuilder.isTraceState to AbstractTracer as it was no longer needed there and there was already isTraceState in the tracer
  • removed SpanBuilder.withStateItem - no longer needed

These are used, ref https://github.com/openzipkin/brave-opentracing/blob/master/src/main/java/io/opentracing/impl/BraveSpanBuilder.java#L102

I think you'll answer a lot of these questions and changes if you can work on the brave-opentracing PR in parallel.

@michaelsembwever michaelsembwever changed the title Separate SpanContext from SpanBuilder WIP: Separate SpanContext from SpanBuilder Nov 15, 2016
Marcin Biegan added 2 commits November 29, 2016 00:58
* master:
  [maven-release-plugin] prepare for next development iteration
  [maven-release-plugin] prepare release 0.20.2
  revert deletion of developer section as it broke Maven sync
  [maven-release-plugin] prepare for next development iteration
  [maven-release-plugin] prepare release 0.20.1
  Resets tag to HEAD
  [maven-release-plugin] prepare for next development iteration
  [maven-release-plugin] prepare release 0.20.0
  travis: use container infrastructure
  Propagate baggage to child spans

# Conflicts:
#	opentracing-impl/src/test/java/io/opentracing/impl/AbstractTracerTest.java
@mabn
Copy link
Contributor Author

mabn commented Nov 30, 2016

I created a PR (openzipkin-contrib/brave-opentracing#9) in brave-opentracing, but I think that the direction it's going does't look good enough.
I had to pretty much change everything and still run into issues with Noop implementation (there's like 3 NoopSpanContext classes - one interface and two implementations).

I'm starting to think that the abstract implementation does too much and it would be better if it was limited to some basic methods like the whole family of SpanBuilder.withTag or Span.log - without methods related to actual tracing (e.g. AbstractSpanBuilder.createSpan).

@sjoerdtalsma
Copy link
Contributor

But I've run into problems with Noop implementation.
Right now there are two - one in -impl, the other in -noop. AbstractSpanBuilder asChildOf(Span parent) - requires NoopSpanBuilder to extends AbstractSpanBuilder but on the other hand AbstractSpanBuilder asChildOf tests against NoopSpanContext from opentracing-noop
which cannot depend on AbstractSpanContext (because circular dependency).

Seems solvable to me, e.g.:

  • asChildOf(Span) for the abstract base-implementation shouldn't assume any typing (i.m.o.) and could just forward to asChildOf(span.context())?
  • Make the built-in NoopSpanContext not rely on the base-implementation (e.g. it is trivial and doesn't need common functionality). Then put the 'smartness' into the abstract base-implementation by inspecting instanceof NoopSpanContext if you must.

To me that feels more pure. I'm curious why this would not work?

@mabn
Copy link
Contributor Author

mabn commented Feb 22, 2017

It wouldn't work because:

  1. AbstractSpanBuilder.asChildOf() returns AbstractSpanBuilder.
  2. when called with NoopSpanContext argument it has to return NoopSpanBuilder
  3. which means that NoopSpanBuilder has to extend AbstractSpanBuilder

2 is based on what @michaelsembwever wrote earlier, but I don't really like this approach - it's a very surprising behaviour. It's one thing to have a noop tracer which does nothing, but in this case receiving request without trace context extracts a NoopSpanContext which in turns causes all its children to be NoopSpans. Jaeger client for example does not handle it this way.

All in all I wouldn't use the abstract implementation anymore, it introduces more problems than it solves. Little changes in the abstract implementation led to lots of changes e.g. in the mentioned brave-opentracing. IMO it would be more useful if it provided implementation of all simple "non-factory" methods - setTag, log, etc., but not createSpan or childOf.

@bhs
Copy link
Contributor

bhs commented Feb 23, 2017

@mabn

IMO it would be more useful if it provided implementation of all simple "non-factory" methods - setTag, log, etc., but not createSpan or childOf.

+1000 to this. I could not agree more. I also think it "would be nice" to move Abstract* out of this repo entirely, as it sends a weird signal that it's actually part of the API when it's just a convenience class for implementations.

@bhs
Copy link
Contributor

bhs commented Apr 30, 2017

(I'm going to close this since #115 deals with the underlying problem and this has fallen out of date)

@bhs bhs closed this Apr 30, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants