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

BraveScopeManager not thread safe? #70

Closed
mpetazzoni opened this issue Mar 22, 2018 · 9 comments
Closed

BraveScopeManager not thread safe? #70

mpetazzoni opened this issue Mar 22, 2018 · 9 comments

Comments

@mpetazzoni
Copy link
Contributor

I'm trying to pass a Span from one thread to another, as described in the README's "Deferring asynchronous work" section. Unfortunately, I think I'm running into a thread safety bug in BraveScopeManager, in particular in getOrEstablishActiveSpan().

In the following code:

// In thread A
try (Scope s1 = tracer.buildSpan("foo").startActive(false)) {
    final Span span = tracer.activeSpan();
    executor.submit(() -> {
        // In thread B
        try (Scope s2 = tracer.scopeManager().activate(span, true)) {
            logger.info("scope: {}, active scope: {}", s2, tracer.scopeManager().active());
        }
    });
}

The active scope seen in thread B is sometimes null (while I expect it to be s2). I believe this happens if thread B activates the span before thread A closes its scope s1, leading to s2 == s1 in thread B.

This of course does not happen if I simply create a child span in thread B with tracer.buildSpan("bar").asChildOf(span), because that gets a different span ID and therefore a different entry in the activeSpans map of the BraveScopeManager:

// In thread A
try (Scope s1 = tracer.buildSpan("foo").startActive(true)) {
    final Span span = tracer.activeSpan();
    executor.submit(() -> {
        // In thread B
        try (Scope s2 = tracer.buildSpan("bar").asChildOf(span).startActive(true)) {
            logger.info("scope: {}, active scope: {}", s2, tracer.scopeManager().active());
        }
    });
}

I'm not sure what the best practice is here, or even whether this is a bug or a feature of the BraveScopeManager. But as-is, I believe the example in the README isn't a reliable way to pass tracing context to the async routine.

(Thanks @isaachier for the help looking into this!)

@codefromthecrypt
Copy link
Contributor

which readme are you referring to? Since there are no base test classes for us to inherit, I suppose making a test based on your example is one way to figure out what is going on. Regardless I'd expect that the executor would need to be instrumented to propagate the calling scope. In normal brave this is CurrentTraceContext.wrap(blah). Maybe you can ask @pavolloffay @CodingFabian or someone else from opentracing to clarify what this readme is saying and why it isn't wrapping the executor service (assuming it isn't)

@isaachier
Copy link

@adriancole I am pretty sure @mpetazzoni is referring to the OpenTracing Java project's README here.

@codefromthecrypt
Copy link
Contributor

codefromthecrypt commented Apr 3, 2018 via email

@mpetazzoni
Copy link
Contributor Author

@adriancole I was indeed talking about opentracing-java's README file, which I realize maybe doesn't mean much for Zipkin/Brave. But if brave-opentracing intends to make using OpenTracing instrumentation (and patterns) possible, it should correctly support its documented best practices.

BraveScopeManager returns an existing Scope instance from getOrEstablishActiveSpan(), which seems to be a different behavior than what CurrentTraceContext.newScope() would do (always returns a distinct Scope instance).

Feels to me like BraveScopeManager should be made a bit more thread-aware (since scopes are not supposed to be shared across threads anyway), and make BraveScopeManager.activeSpans a thread local, or something like that?

@codefromthecrypt
Copy link
Contributor

codefromthecrypt commented Apr 4, 2018 via email

@codefromthecrypt
Copy link
Contributor

codefromthecrypt commented Apr 4, 2018 via email

codefromthecrypt pushed a commit that referenced this issue Apr 8, 2018
This also unsupports setting a custom ScopeManager as the api is hairy.

Fixes #70
@codefromthecrypt
Copy link
Contributor

#71 yanks the faulty implementation

codefromthecrypt pushed a commit that referenced this issue Apr 8, 2018
This also unsupports setting a custom ScopeManager as the api is hairy.

Fixes #70
@codefromthecrypt
Copy link
Contributor

v0.30.0 on the way with this

@mpetazzoni
Copy link
Contributor Author

Awesome, thanks! I'll check it out.

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

No branches or pull requests

3 participants