ScopedValue.runWhere not returning scope

Marcin Grzejszczak marcin.grzejszczak at gmail.com
Wed Jun 5 21:31:05 UTC 2024


Hi,

I apologize if this is not a proper place to ask this question, in which
case please point me where to the place where I should ask it. Also I
apologize if I sent this email twice cause I had some bizarre issues with
my mailbox.

I'm Marcin and I'm co-maintaining the Micrometer project [1] part of which
is the Micrometer Observation API with which we are instrumenting libraries
also in the domain of distributed tracing. What happens there is that when
a scope is opened for a span [2], thread local entries are set, e.g. MDC
has a correlation identifier attached (called trace id) and once the scope
is closed, the MDC gets cleared [3]. The span API allows you to achieve
this by opening a scope (e.g. pseudocode: `Scope scope = span.openScope()`)
and later on you close the scope with `scope.close()`. This kind of code is
often called within frameworks that give you some sort of a handler /
listener mechanism [4] that is being called around actual user code
invocation (you have methods like e.g. `before(...)`, `after(...)`). The
current ScopedValue API doesn't allow that. Let me show that using the
following example:

Let's say that we have an interface called a Handler

```
interface Handler {

  /**
   * Called before actual method execution
   * @param map mutable map to pass between methods
   */
  void before(Map<Object, Object> map);

  /**
   * Called after actual method execution
   * @param exception potential exception - can be null
   * @param map mutable map to pass between methods
   */
  void after(Exception exception, Map<Object, Object> map) throws Exception;

}
```

Then let's say that we have a "Proxy" class that wraps the user code
execution with the Handler

```
class Proxy {

  private final Handler handler;

  Proxy(Handler handler) {
    this.handler = handler;
  }

  void run(Runnable runnable)  throws Exception {
    Map<Object, Object> map = new HashMap<>();
    Exception exception = null;
    handler.before(map);
    try {
      runnable.run();
    } catch (Exception ex) {
      exception = ex;
      throw ex;
    } finally {
      handler.after(exception, map);
    }
  }
}
```

Then we have an implementation that uses ThreadLocal and a "scope" in a
form of Closeable that will be opened in "before" and closed in "after".
This is a simplification of the distributed tracing case.

```
class ThreadLocalValueHandler implements Handler {

  static final ThreadLocal<String> threadLocal = new ThreadLocal<>();

  @Override
  public void before(Map<Object, Object> map) {
    threadLocal.set("foo"); // simulates setting span in scope
    Closeable scope = threadLocal::remove; // simulates setting of span in
scope
    map.put("scope", scope);
  }

  @Override
  public void after(Exception exception, Map<Object, Object> map) throws
Exception {
    Closeable scope = (Closeable) map.get("scope");
    scope.close(); // simulates closing the span scope
  }
}
```

Now when we run it together we could get sth like this

```
public static void main(String[] args) throws Exception {
    new Proxy(new ThreadLocalValueHandler()).run(() -> {
      System.out.println("HELLO [" + ThreadLocalValueHandler.threadLocal.get()
+ "]");
    });
    System.out.println("Goodbye [" + ThreadLocalValueHandler.threadLocal.get()
+ "]");
  }
```

And the output would be

```
HELLO [foo]
Goodbye [null]
```

I am wondering what if I don't want to use a ThreadLocal but ScopeValue? Is
it the tool to solve this problem? Current API allows me to run a lambda
within a scope through `ScopedValue.runWhere(scopedValue, "foo", () ->
{})`. But what if I wanted to separate this into 2 steps, the scope opening
and closing? There is the `StructuredTaskScope` but I have trouble
understanding how that could be used for that matter.

Thank you.

[1] - https://micrometer.io/
[2] -
https://github.com/openzipkin/brave/blob/6.0.3/brave/src/main/java/brave/Tracer.java#L395-L403
[3] -
https://github.com/openzipkin/brave/blob/6.0.3/context/slf4j/src/main/java/brave/context/slf4j/MDCScopeDecorator.java#L61-L74
[4] -
https://github.com/micrometer-metrics/micrometer/blob/v1.13.0/micrometer-observation/src/main/java/io/micrometer/observation/ObservationHandler.java#L59-L71
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/loom-dev/attachments/20240605/3c686943/attachment-0001.htm>


More information about the loom-dev mailing list