Skip to content
Son's blog
All writing

Monix Async, MDC, Thread Local, Correlation ID in Scala

TLDR

Problem

Context

We observed:

Assumptions

Solution

1-Base your computation on monix Task, avoid converting it to cats IO

What did not work when converting Task to IO at HTTP layer

val httpApp: HttpApp[IO] = ???
val resource = BlazeServerBuilder[IO](scheduler)
  .bindHttp(80, "0.0.0.0")
  .withHttpApp(httpApp)
  .resource
val (serverIO: Server[IO], resourceReleaser: IO[Unit]) = resource.allocated.unsafeRunSync()

What works

var httpApp: HttpApp[Task] = ???
val resource = BlazeServerBuilder[Task](scheduler)
  .bindHttp(80, "0.0.0.0")
  .withHttpApp(httpApp)
  .resource
val (server: Server[Task], resourceReleaser: Task[Unit]) = resource.allocated.runSyncUnsafe()

2-Put MDC before the async boundary Task->Future

If you have to use Future together with monix Task or Observable (e.g. using a library that returns Future), add another Future before your Future to add MDC in Thread Local to the executing Future.

What does not work

// here we use Observable, same thing applies for Task
Observable.fromFuture {
  Future {
    // thread executing here won't have the Thread Local of the previous Task
    Thread.sleep(1000) // sleeping to simulate a database call
    "result"
  }
}

What works

Observable.fromFuture {
  Thread.sleep(1000)
  for {
    // adding this line helps correlationId properly propagated to sangria Execution thread
    _ <- Future(MDC.put(correlationId, requestContext.correlationId)) 
    result <- Future {
      Thread.sleep(1000) // sleeping to simulate a database call
      "result"
    }
  } yield result
}

Reference