ContinuationInterceptor composability


#1

What is the best way to handle this situation: I’m launching a coroutine using some CoroutineDispatcher (let’s say CommonPool) which “takes” the ContinuationInterceptor key. Now I also have some context information, let’s say a request id, which I want to set on the SLF4j Mapped Diagnostic Context (MDC) when the continuation resumes. MDC is thread bound and I was thinking of using a ContinuationInterceptor context, but I now realize that it won’t compose with the CoroutineDispatcher (since the key is already taken).

Should I create my own CoroutineDispatcher that implements both behaviors?

Just to give an idea this is an example of what I came up with

class RequestContext(
  val reqId: String
) : AbstractCoroutineContextElement(RequestContext) {
  companion object Key: CoroutineContext.Key<RequestContext>
}

object MyPool : CoroutineDispatcher() {
  private val pool = ForkJoinPool((Runtime.getRuntime().availableProcessors() - 1).coerceAtLeast(1))

  override fun dispatch(context: CoroutineContext, block: Runnable) {
    pool.execute {
      val reqCtx = context[RequestContext.Key]
      if (reqCtx != null) {
        MDC.put("reqId", reqCtx.reqId)
      }
      block.run()
    }
  }
}

fun main(args: Array<String>) {
  val log = LoggerFactory.getLogger("test")
  runBlocking {
    val ja = launch(MyPool + RequestContext("A")) {
      log.info("A1")
      delay(10)
      log.info("A2")
      delay(10)
      log.info("A3")
      delay(10)
      log.info("A4")
    }

    val jb = launch(MyPool + RequestContext("B")) {
      log.info("B1")
      delay(10)
      log.info("B2")
      delay(10)
      log.info("B3")
      delay(10)
      log.info("B4")
    }

    ja.join()
    jb.join()
  }
}

Wondering if there is a way to avoid having to define my own CoroutineDispatcher