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