Reputation: 413
I'm investigating using ScopedValue
and StructuredTaskScope
to replace usage of Google's gRPC context.
Both systems are nicely immutable, lightweight and support nested scopes, but I think gRPC context does something I need which might not be available via ScopedValue (which would be a real shame if true).
In various frameworks, I might need to enable debugging on a per-request basis, turning on additional logging if a request matches some criteria. However my code is never in a position to wrap some function in order to nest it in a new task, it's simply code which gets called and must exit before the request is handled. In pseudo-code:
// Assume one handler per request.
class MyHandler implements SomeFrameworkCallbackApi {
MyCloseable resetDebug = null;
@Override
void beforeRequest(RequestHeaders req, ConfigInfo info) {
if (info.addLogging() && shouldDebug(req)) {
Context newGrpcContext = Context.current().withValue(DEBUG_LEVEL, Level.FINE);
Context prev = newGrpcContext.attach();
resetDebug = () -> newGrpcContext.detach(prev);
}
}
@Override
void afterRequest(...) {
if (resetDebug != null) {
// Fails if not called in strict nesting order.
resetDebug.close();
}
}
}
This lets me have scoped variables even in situations where I am not able to modify code which can wrap the task directly.
From looking at the APIs for ScopedValue
and StructuredTaskScope
it looks like the only way to run code with a ScopedValue
installed is to call a method such as run()
, which forces a nesting call to the task at that point (which is not possible in many frameworks).
I had tried:
resetDebug = ScopedValue.getWhere(DEBUG_LEVEL, Level.FINE, StructuredTaskScope::new)::close;
But this results in java.util.concurrent.StructureViolationException
(not unexpectedly).
So my question is, is there any other way to use ScopedValue
(with or without StructuredTaskScope
) which might achieve what I'm after, or do I need to stay with gRPC context?
Upvotes: 3
Views: 400
Reputation: 413
From Brian Goetz:
""This is simply not how scoped values work, by design. What you seem to want is the unrestricted mutability of ThreadLocal, but with the improved performance of ScopedValue. But ScopedValue derives its benefits from the constraints you don't seem to like.""
So the answer is simply that ScopedValue
can only be used in situations where scoping is achieved by wrapping code directly, rather than the use of some setup/teardown callbacks which occur before/after code is run.
This means that ScopedValue
cannot be general replacement for existing ThreadLocal
-based context libraries (e.g. gRPC Context
or Open Telemetry contexts
).
In my situation (setting up scoped values for a library which can be called from many flavours of framework), I simply don't have the freedom to require that users refactor their code, which sits between the framework and my library, to implement all their scoping via the direct wrapping of code.
If your need is similar to mine, then gRPC Context
provides the additional functionality to allow scoping via the use of a returned Closeable
to manage things (e.g. managing scopes via try-with-resources
).
Upvotes: 1