Skip to content

[Feature] Add tracing support for ThreadPoolExecutor.invokeAll and invokeAny in the JDK thread pool plugin #13945

Description

@xuzhiguang

Search before asking

  • I had searched in the issues and found no similar feature requirement.

Description

Add tracing support for ThreadPoolExecutor.invokeAll and ThreadPoolExecutor.invokeAny in the JDK thread pool plugin.

The plugin currently propagates tracing context for tasks submitted through ThreadPoolExecutor.execute and ThreadPoolExecutor.submit, but the following inherited batch task submission methods are not covered:

<T> List<Future<T>> invokeAll(
    Collection<? extends Callable<T>> tasks
)

<T> List<Future<T>> invokeAll(
    Collection<? extends Callable<T>> tasks,
    long timeout,
    TimeUnit unit
)

<T> T invokeAny(
    Collection<? extends Callable<T>> tasks
)

<T> T invokeAny(
    Collection<? extends Callable<T>> tasks,
    long timeout,
    TimeUnit unit
)

ThreadPoolExecutor inherits these methods from AbstractExecutorService.

Internally, invokeAll and invokeAny convert or wrap the original Callable tasks as RunnableFuture-based tasks before passing them to execute. The current execute interceptor skips RunnableFuture instances, while the original Callable tasks have not captured the active tracing context.

As a result, tasks executed through ThreadPoolExecutor.invokeAll or ThreadPoolExecutor.invokeAny do not continue the caller's trace and have no CrossThread reference to the parent trace segment.

Supporting these methods would make tracing context propagation consistent across the individual and batch task submission APIs of ThreadPoolExecutor.

Use case

ThreadPoolExecutor executor = new ThreadPoolExecutor(
    2,
    2,
    0L,
    TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<Runnable>()
);

List<Callable<String>> tasks = Arrays.asList(
    () -> callDownstreamService("task-1"),
    () -> callDownstreamService("task-2")
);

List<Future<String>> results = executor.invokeAll(tasks);

String firstResult = executor.invokeAny(tasks);

When these methods are called within an active SkyWalking trace, every Callable that starts execution should continue the caller's tracing context. Downstream HTTP, database, or RPC operations should be connected to the same trace through CrossThread references.

Currently, work submitted through ThreadPoolExecutor.invokeAll and ThreadPoolExecutor.invokeAny may appear as disconnected traces or may be missing from the parent trace.

The expected behavior is:

  1. Support both overloads of ThreadPoolExecutor.invokeAll.
  2. Support both overloads of ThreadPoolExecutor.invokeAny.
  3. Each Callable that starts execution continues the active tracing context.
  4. Each worker-thread trace segment contains a CrossThread reference to the caller.
  5. Existing timeout, cancellation, exception, and result-returning semantics remain unchanged.
  6. Existing behavior for execute, submit, and already wrapped tasks remains unchanged.
  7. Plugin scenario tests verify tracing context propagation for all four overloads.

I implemented a local prototype for ThreadPoolExecutor.invokeAll to validate the feasibility of this feature.

After adding tracing context propagation, the Callable tasks submitted through invokeAll are connected to the caller's trace through CrossThread references, as shown below:

Image

Related issues

No response

Are you willing to submit a pull request to implement this on your own?

  • Yes I am willing to submit a pull request on my own!

Code of Conduct

Metadata

Metadata

Assignees

Labels

featureNew featurejavaJava agent related

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions