Context Processing Mechanism

In Spring MVC, using ThreadLocal for thread-safe. But Spring Webflux, using Context. Project Reactor introduced a mechanism that is well aligned with functional programming to provide means to transport contextual metadata. It is simply called Context.

ContextView is read-only versions.

Context Propagation with Project Reactor

Mono<Void> handleRequest() {
  long correlationId = correlationId();
  log("Assembling the chain", correlationId);

  Mono.just("test-product")
    .delayElement(Duration.ofMillis(1))
    .flatMap(product ->
      Flux.concat(addProduct(product), notifyShop(product))
          .then())
    .contextWrite(Context.of("CORRELATION_ID", correlationId));

When subscribed to, the output is as expected:

[      main][ 6328001264807824115] Assembling the chain
[parallel-1][ 6328001264807824115] Adding product: test-product
[parallel-1][ 6328001264807824115] Notifying shop about: test-product

The information flows from downstream operators to the upstream operators to initiate the processing.

Show this Simple Context Examples.

Context Storage Mechanism

Past five user key/value pair, the Context will use a copy-on-write implementation backed by a new Map on each put.

final class ContextN extends LinkedHashMap<Object, Object> {
  // ...
}

Like functional programming, Reactor copies and uses existing values whenever adding values to maintain invariant values, but until five data are stored in a context, it deceives them as if they were copied through the Copy-On-Write strategy and does not actually copy the values.

If there are more than five pieces of data stored in Context, it is stored through the LinkedHashMap, so if too much data is stored in the Map and used, performance issues may arise.

Distributed Tracing with MDC

Adding a Context to a Reactive Sequence

  • As a result, libraries that rely on ThreadLocal at least introduce new challenges when used with Reactor. At worst, they work badly or even fail. Using the MDC of Logback to store and log correlation IDs is a prime example of such a situation.

Kotlin version - How can the MDC context be used in the reactive Spring applications

In order to continue using the MDC feature in the reactive Spring application, we need to make sure that whenever a thread starts processing a request it has to update the state of the MDC context. This can be done by doing two things:

  1. All the values that we previously added directly to the MDC context should now be added to Reactor context. The framework will ensure that the reactive context is passed along the reactive execution and it will not be bounded to any specific thread.

  2. Ensure that values residing in the reactive context are copied to the MDC context whenever there is a possibility that a thread that processes the request has changed. For this, we will implement the CoreSubscriber that will be hooked into the Reactor using Hooks.

Introduce interop between CoroutineContext and Reactor Context