Kamon icon indicating copy to clipboard operation
Kamon copied to clipboard

Context propagation is broken for Promises

Open qwe2 opened this issue 6 months ago • 1 comments

The following code prints null on Kamon v2.7.3 / Kanela v1.0.18 (scala 2.13.14):

package foo

import kamon.Kamon
import kamon.tag.{Lookups, TagSet}

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.DurationInt
import scala.concurrent.{Await, Promise}
import scala.util.Success

object Foo {
  def main(args: Array[String]): Unit = {
    Kamon.init()

    val prom = Promise[Int]()

    val context = Kamon.currentContext()
    val ts = TagSet.builder()
    ts.add("foo", "bar")

    val newCtx = context.withTags(ts.build())

    Kamon.runWithContext(newCtx) {
      prom.complete(Success(123))
    }

    val res = prom.future.map { _ =>
      val context = Kamon.currentContext()
      println(context.getTag(Lookups.plain("foo"))) // <---- should print "bar"
    }

    Await.result(res, 10.seconds)
  }
}

This should print bar, which it does on older versions. Tested on 2.1.3 where it was still working. I suspect the issue is that FutureChainingInstrumentation has been deprecated and disabled by default, and the replacement does not work on Promise. The above code works correctly on Kamon v2.7.3 with the below config:

kanela {
    modules {
        executor-service.enabled = false
        scala-future.enabled = true
    }
}

Note that unless executor-service is disabled, enabling scala-future results in

[info] Initializing Kamon Telemetry v2.7.3 / Kanela v1.0.18
[error] Exception in thread "main" java.lang.ClassFormatError: Duplicate interface name "kamon/instrumentation/context/HasContext" in class file scala/concurrent/impl/CallbackRunnable
[error]         at java.base/java.lang.ClassLoader.defineClass1(Native Method)
[error]         at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1022)
[error]         at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:174)
[error]         at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:800)
[error]         at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:698)
[error]         at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:621)
[error]         at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:579)
[error]         at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
[error]         at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:527)
[error]         at scala.concurrent.impl.Promise$DefaultPromise.onComplete(Promise.scala:317)
[error]         at scala.concurrent.impl.Promise.transform(Promise.scala:41)
[error]         at scala.concurrent.impl.Promise.transform$(Promise.scala:39)
[error]         at scala.concurrent.impl.Promise$DefaultPromise.transform(Promise.scala:197)
[error]         at scala.concurrent.Future.map(Future.scala:292)
[error]         at scala.concurrent.Future.map$(Future.scala:292)
[error]         at scala.concurrent.impl.Promise$DefaultPromise.map(Promise.scala:197)
[error]         at foo.Foo$.main(Foo.scala:27)
[error]         at foo.Foo.main(Foo.scala)

~Additionally, the scala-future module is completely broken when used together with sbt-kanela-runner 2.1.0. For some reason, scala.util.Success does never get instrumented (interestingly, Failure does) when running on this version. It is working properly on sbt-kanela-runner 2.0.14 however. I have not investigated this any further.~ This was an issue with javaagent missing from javaOptions (https://github.com/kamon-io/sbt-kanela-runner/issues/30).

qwe2 avatar Aug 06 '24 16:08 qwe2