Skip to content

Latest commit

 

History

History
136 lines (99 loc) · 6.78 KB

File metadata and controls

136 lines (99 loc) · 6.78 KB

MicroProfile Concurrency Specification

Introduction

MicroProfile Concurrency introduces APIs for propagating contexts across units of work that are thread-agnostic. It makes it possible to propagate context that was traditionally associated to the current thread across various types of units of work such as CompletionStage, CompletableFuture, Function, Runnable regardless of which particular thread ends up executing them.

Motivation

When using a reactive model, execution is cut into small units of work that are chained together to assemble a reactive pipeline. The context under which each unit of work executes is often unpredictable and depends on the particular reactive engine used. Some units might run with the context of a thread that awaits completion, or the context of a previous unit that completed and triggered the dependent unit, or with no/undefined context at all. Existing solutions for transferring thread context, such as the EE Concurrency Utilities ContextService, are tied to a specific asynchrony model, promotes usage of thread pools, is difficult to use and require a lot of boilerplate code. This specification makes it possible for thread context propagation to easily be done in a type-safe way, keeping boilerplate code to a minimum, as well as allowing for thread context propagation to be done automatically for many types of reactive models.

We distinguish two main use-cases for propagating contexts to reactive pipelines:

  • Splitting units of work into a sequential pipeline where each unit will be executed after the other. Turning an existing blocking request into an async request would produce such pipelines.

  • Fanning out units of work to be executed in parallel on a managed thread pool. Launching an asynchronous job from a request without waiting for its termination would produce such pipelines.

Goals

  • Pluggable context propagation to the most common unit of work types.

  • Mechanism for thread context propagation to CompletableFuture and CompletionStage units of work that reduces the need for boilerplate code.

  • Full compatibility with EE Concurrency spec, such that proposed interfaces can seamlessly work alongside EE Concurrency, without depending on it.

Solution

This specification introduces two interfaces that contain methods that can work alongside EE Concurrency, if available.

The interface, org.eclipse.microprofile.concurrent.ManagedExecutor, provides methods for obtaining managed instances of CompletableFuture which are backed by the managed executor as the default asynchronous execution facility and the default mechanism of defining thread context propagation. Similar to EE Concurrency’s ManagedExecutorService, the MicroProfile ManagedExecutor also implements the Java SE java.util.concurrent.ExecutorService interface, using managed threads when asynchronous invocation is required. It is possible for a single implementation to be capable of simultaneously implementing both ManagedExecutor and ManagedExecutorService interfaces.

A second interface, org.eclipse.microprofile.concurrent.ThreadContext, provides methods for individually contextualizing units of work such as CompletionStage, CompletionFuture, Runnable, Function, Supplier and more, without tying them to a particular thread execution model. This gives the user finer-grained control over the capture and propagation of thread context by remaining thread execution agnostic. It is possible for a single implementation to be capable of simultaneously implementing both ThreadContext and ContextService interfaces.

Builders

Instances of ManagedExecutor and ThreadContext can be constructed via builders with fluent API, for example,

    ManagedExecutor executor = ManagedExecutor.builder()
        .propagated(ThreadContext.APPLICATION)
        .cleared(ThreadContext.ALL_REMAINING)
        .maxAsync(5)
        .build();

    ThreadContext threadContext = ThreadContext.builder()
        .propagated(ThreadContext.APPLICATION, ThreadContext.CDI)
        .unchanged()
        .cleared(ThreadContext.ALL_REMAINING)
        .build();

Applications should shut down instances of ManagedExecutor that they build after they are no longer needed. The shutdown request serves as a signal notifying the container that resources can be safely cleaned up.

Example usage

For managed executor,

    CompletableFuture<Long> stage = executor.newIncompleteFuture()
        .thenApply(function)
        .thenAccept(consumer);
    stage.completeAsync(supplier);

Or similarly for thread context,

    threadContext.withContextCapture(unmanagedCompletionStage)
        .thenApply(function)
        .thenAccept(consumer);

Sharing Instances

CDI application scope is a convenient mechanism for sharing instances across an application.

    @Produces @ApplicationScoped @SecAndAppContextQualifier
    ManagedExecutor executor1 = ManagedExecutor.builder()
        .propagated(ThreadContext.SECURITY, ThreadContext.APPLICATION)
        .build();

    ... // in some other bean
    @Inject
    void setCompletableFuture(@SecAndAppContextQualifier ManagedExecutor executor2) {
        completableFuture = executor2.newIncompleteFuture();
    }

    ... // in yet another bean
    @Inject @SecAndAppContextQualifier
    ManagedExecutor executor3;

    // example qualifier annotation
    @Qualifier
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })
    public @interface SecAndAppContextQualifier {}

Specifying Defaults via MicroProfile Config

MicroProfile Config properties can be used to specify defaults for configuration attributes that are not otherwise configured on ManagedExecutor and ThreadContext instances. Here is an example that includes all of the available attributes that can be defaulted:

ManagedExecutor/propagated=Security,Application
ManagedExecutor/cleared=Remaining
ManagedExecutor/maxAsync=10
ManagedExecutor/maxQueued=-1

ThreadContext/propagated=
ThreadContext/cleared=Security,Transaction
ThreadContext/unchanged=Remaining