example-add-request-id

GT-Sandbox-Snapshot: Example adding Request ID to Co-Routine Context.

Code

package com.glassthought.sandbox

import com.glassthought.sandbox.util.out.impl.out
import kotlinx.coroutines.*
import kotlin.coroutines.AbstractCoroutineContextElement
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.coroutineContext


// Define a context element to hold request ID
data class RequestId(val id: String) : AbstractCoroutineContextElement(RequestId) {
  companion object Key : CoroutineContext.Key<RequestId>
}

// Any suspend function can access the request ID from context
suspend fun doWork(about: String) {
  val requestId = currentCoroutineContext()[RequestId]?.id
  out.info("[${about}]: Working on request_id=[$requestId]")
  delay(50)
}

fun main(): kotlin.Unit = runBlocking {
  println("--------------------------------------------------------------------------------")
  println("MAIN started")

  // Add request ID to coroutine context
  withContext(RequestId("req-12345")) {
    out.info("Request ID added to context")

    // All suspend calls in this scope can access it
    doWork("direct-call-within-context")

    // Child inherits parent context (RequestId + CoroutineName)
    launch { doWork("in-launch") }
  }
}

Command to reproduce:

gt.sandbox.checkout.commit 86fa348a8e09123d80d8 \
&& cd "${GT_SANDBOX_REPO}" \
&& cmd.run.announce "./gradlew run --quiet"

Recorded output of command:

Picked up JAVA_TOOL_OPTIONS: -Dkotlinx.coroutines.debug
Picked up JAVA_TOOL_OPTIONS: -Dkotlinx.coroutines.debug
--------------------------------------------------------------------------------
MAIN started
[INFO][elapsed:   15ms][①][coroutname:@coroutine#1] Request ID added to context
[INFO][elapsed:   27ms][①][coroutname:@coroutine#1] [direct-call-within-context]: Working on request_id=[req-12345]
[INFO][elapsed:   81ms][⓶][coroutname:@coroutine#2] [in-launch]: Working on request_id=[req-12345]

NOTE: if you launch on another scope as expected the fields will NOT transfer over UNLESS you pass through your coroutine context.

GT-Sandbox-Snapshot: Example passing through custom element to a task spawned on background scope.

Code

package com.glassthought.sandbox

import kotlinx.coroutines.*
import kotlin.coroutines.AbstractCoroutineContextElement
import kotlin.coroutines.CoroutineContext

data class RequestId(val id: String) : AbstractCoroutineContextElement(RequestId) {
  companion object Key : CoroutineContext.Key<RequestId>
}

suspend fun doWork(about: String) {
  val requestId = currentCoroutineContext()[RequestId]?.id
  println("[$about]: request_id=[$requestId]")
  delay(50)
}

// Different scope (like a singleton or background processor)
val backgroundScope = CoroutineScope(Dispatchers.Default)

fun main() = runBlocking {
  println("=== Context Inheritance ===\n")

  withContext(RequestId("req-12345")) {
    println("Request context set to: req-12345\n")

    // ✅ INHERITS - uses current scope
    launch { doWork("launch") }

    // ❌ DOES NOT INHERIT - uses backgroundScope instead
    backgroundScope.launch { doWork("backgroundScope.launch") }

    // ✅ FIX - explicitly pass context to different scope
    backgroundScope.launch(currentCoroutineContext()) {
      doWork("backgroundScope.launch + context")
    }

    delay(200)
  }

  backgroundScope.cancel()
}

Command to reproduce:

gt.sandbox.checkout.commit 052b2be0dc8e73ae2259 \
&& cd "${GT_SANDBOX_REPO}" \
&& cmd.run.announce "./gradlew run --quiet"

Recorded output of command:

Picked up JAVA_TOOL_OPTIONS: -Dkotlinx.coroutines.debug
Picked up JAVA_TOOL_OPTIONS: -Dkotlinx.coroutines.debug
=== Context Inheritance ===

Request context set to: req-12345

[backgroundScope.launch]: request_id=[null]
[launch]: request_id=[req-12345]
[backgroundScope.launch + context]: request_id=[req-12345]

Backlinks