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

In process context propagation for Executors #145

Closed
felixbarny opened this issue Aug 1, 2018 · 0 comments · Fixed by #430
Closed

In process context propagation for Executors #145

felixbarny opened this issue Aug 1, 2018 · 0 comments · Fixed by #430
Assignees
Labels
instrumentation Instrumentation: framework support, custom plugins, ...

Comments

@felixbarny
Copy link
Member

felixbarny commented Aug 1, 2018

To propagate the trace context to different threads, we should by default instrument ExecutorServices to propagate the currently active span. The user should not have to manually wrap the Executor in order to enable the context propagation for it.

Prior art:

One design decision is whether we want to wrap Runnables and store the context as an instance variable or if we want to instrument implementations of Runnable and add an instance variable via BCI. Wrapping has the disadvantage that we are creating garbage and type casts to a specific sub-type of Runnable would fail. The disadvantage of adding the instance variable via BCI is that this approach would not work with runtime attachment of the agent. Yet another option would be to store the trace context in a map which associates Runnables with the corresponding trace context.

Another question is whether we want to generically instrument all runnables or only those submitted to an Executor. We could store the trace context when the runnable is created and restore it in the run method. The assumption here is that the Runnable is instantiated in the Thread which holds the current trace context. But this assumption is not always true, for example when the Runnable is stored in an instance variable.

Relevant interfaces are: java.util.concurrent.Executor, java.util.concurrent.ExecutorService, java.util.concurrent.ScheduledExecutorService, java.lang.Runnable and java.util.concurrent.Callable

This should fix https://discuss.elastic.co/t/java-apm-agent-sends-no-timeline-spans/141150, where Hystrix is used for fault tolerance of JDBC queries.

@felixbarny felixbarny added instrumentation Instrumentation: framework support, custom plugins, ... [zube]: Backlog labels Aug 1, 2018
@eyalkoren eyalkoren added this to the current milestone Nov 22, 2018
@felixbarny felixbarny self-assigned this Jan 11, 2019
felixbarny added a commit to felixbarny/apm-agent-java that referenced this issue Jan 11, 2019
Introducing `TraceContextHolder` in the internal API as a common
superclass of both `TraceContext` and `AbstractSpan`. Given an instance
of this class, you can create child spans, capture exceptions and manage
activations/scopes.

This abstraction reliefs clients from having to differ between the case
when the current activation is a `TraceContext` vs an `AbstractSpan`.

A `TraceContext` would be active when the current thread does not own
the lifecycle of the parent span. Otherwise an `AbstractSpan` would be
active.

There is rarely a case where a client would need to get the currently
active span to set the name or tags. This propagation is normally done
with `@Advice.Local` variables from enter to exit advices. Should the
need for that arise however, and the client can ensure that the current
activation is actually an `AbstractSpan`, all they need to do is to cast
`tracer.getActive()` from `TraceContextHolder` to `AbstractSpan`.

The public API does not differ between `Span` and `TraceContextHolder`.
`ElasticApm.currentSpan()` always returns a `Span`. In case the current
activation is a `TraceContextHolder`, methods like `Span#setTag` are
silent noops. Calling those methods on a `Span` whose lifecycle is not
owned by the caller is illegal anyway. However, calling
`ElasticApm.currentSpan().createSpan()` is always allowed.

Taking away `ElasticApm.currentSpan()` is not an option, as a common use
case is to rename the spans created by the auto instrumentation or to
add custom tags.

Also, `ElasticApm.currentSpan().createSpan()` makes for a much nicer and
more object-oriented API than starting a span on a tracer, passing in
the parent as a start option.

In preparation of elastic#145
felixbarny added a commit to felixbarny/apm-agent-java that referenced this issue Jan 11, 2019
felixbarny added a commit that referenced this issue Jan 14, 2019
Introducing `TraceContextHolder` in the internal API as a common
superclass of both `TraceContext` and `AbstractSpan`. Given an instance
of this class, you can create child spans, capture exceptions and manage
activations/scopes.

This abstraction reliefs clients from having to differ between the case
when the current activation is a `TraceContext` vs an `AbstractSpan`.

A `TraceContext` would be active when the current thread does not own
the lifecycle of the parent span. Otherwise an `AbstractSpan` would be
active.

There is rarely a case where a client would need to get the currently
active span to set the name or tags. This propagation is normally done
with `@Advice.Local` variables from enter to exit advices. Should the
need for that arise however, and the client can ensure that the current
activation is actually an `AbstractSpan`, all they need to do is to cast
`tracer.getActive()` from `TraceContextHolder` to `AbstractSpan`.

The public API does not differ between `Span` and `TraceContextHolder`.
`ElasticApm.currentSpan()` always returns a `Span`. In case the current
activation is a `TraceContextHolder`, methods like `Span#setTag` are
silent noops. Calling those methods on a `Span` whose lifecycle is not
owned by the caller is illegal anyway. However, calling
`ElasticApm.currentSpan().createSpan()` is always allowed.

Taking away `ElasticApm.currentSpan()` is not an option, as a common use
case is to rename the spans created by the auto instrumentation or to
add custom tags.

Also, `ElasticApm.currentSpan().createSpan()` makes for a much nicer and
more object-oriented API than starting a span on a tracer, passing in
the parent as a start option.

In preparation of #145
felixbarny added a commit to felixbarny/apm-agent-java that referenced this issue Jan 18, 2019
felixbarny added a commit to felixbarny/apm-agent-java that referenced this issue Jan 18, 2019
felixbarny added a commit that referenced this issue Jan 29, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
instrumentation Instrumentation: framework support, custom plugins, ...
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants