WIP: Adds listener method to register callback on context change#1961
WIP: Adds listener method to register callback on context change#1961HaloFour wants to merge 7 commits intoopen-telemetry:masterfrom
Conversation
|
|
Codecov Report
@@ Coverage Diff @@
## master #1961 +/- ##
============================================
- Coverage 86.63% 86.52% -0.11%
Complexity 1494 1494
============================================
Files 184 184
Lines 5655 5664 +9
Branches 584 586 +2
============================================
+ Hits 4899 4901 +2
- Misses 551 555 +4
- Partials 205 208 +3
Continue to review full report at Codecov.
|
| private static final ThreadLocal<Context> THREAD_LOCAL_STORAGE = new ThreadLocal<>(); | ||
|
|
||
| static { | ||
| THREAD_LOCAL_STORAGE.set(Context.root()); |
There was a problem hiding this comment.
I removed this since it only sets the value on whichever thread happens to run the static constructor. Every other thread would remain uninitialized and will return null. Overriding initialValue() or calling the static withInitialValue method can provide a default value but the latter seemingly doesn't exist in Android and it's not really necessary since read access to the ThreadLocal is always through current() anyway.
| */ | ||
| Context current(); | ||
|
|
||
| void onAttach(Consumer<Context> contextConsumer); |
There was a problem hiding this comment.
I think I would take a approach that allows registering wrappers - as long as they are registered early i the app before storage is initialized, it allows customizing without thread sync issues.
public interface ContextStorage {
static void wrap(Function<ContextStorage, ContextStorage> wrapper) {
ContextStorageWrappers.add(wrapper);
}
}
LazyStorage {
ContextStorage contextStorage = createStorage();
for (Function wrapper : ContextStorageWrappers.get()) {
contextStorage = wrapper.apply(contextStorage);
}
}What do you think?
There was a problem hiding this comment.
Registering decorators for the ContextStorage would be an interesting approach. How would you get to the ContextStorage to call wrap without triggering LazyStorage to create the storage, though?
Did you intend ContextStorage#wrap to be static instead of default?
There was a problem hiding this comment.
LazyStorage will only be loaded when get is called so I think it should be ok :-D
Edit: yeah meant static thanks
There was a problem hiding this comment.
Got it, that makes sense now. My only concern with this approach is that you only have that window of time during which you can register these decorators (which I get is intentional). Perhaps ContextStorage#wrap should warn or throw if the storage has already been created? Should there also be an SPI for ContextStorageWrappers?
There was a problem hiding this comment.
Maybe it's sufficient to just make it easy to wrap ThreadLocalContextStorage via SPI.
The Javadocs on ContextStorage imply that you can access the instance of ThreadLocalContextStorage via Context#threadLocalStorage but that method doesn't exist and I don't see another way to access that instance. Without the ability to reference that instance rolling your own ContextStorage would involve rewriting thread-local storage which is where I think the opportunities for error mostly lie.
There was a problem hiding this comment.
Exposing ThreadLocalContextStorage was definitely required (it was javadoc'd, just the method got lost during some refactoring 😅 ).
I think wrap can help where SPI is too tedious, or to allow wrapping the non-threadlocal storage, but if our current code satisfies your use case well, we can consider that later. I think we could add it now though if it helps at all (it was the very first ask from users when we implemented storage SPI in Armeria so I do expect it to happen eventually)
There was a problem hiding this comment.
The SPI approach scratches my itch for now, and now that ThreadLocalContextStorage is exposed again it makes my implementation that much simpler. As for establishing an API here I think it worthwhile. I'd be curious what the other use cases might be.
There was a problem hiding this comment.
Here's some reference of what led to the API in Armeria
In particular it seems, from my interpretation, to be about having a similar API for hooking as reactor for users of both.
There was a problem hiding this comment.
Looks great, thanks!
|
Closing in favor of #2044 |

Prototype API for registering a callback which is triggered when the current Context for the current thread is changing.
Exposes the current
ContextStoragefrom the methodContext#storage.Adds a new method
ContextStorage#onAttach(Consumer<Context>)to register a callback interface which is invoked when the current context is going to be changed.I've not touched Javadocs or tests yet, wanted to first get feedback on the API surface.
Example of usage: