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

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions