-
Notifications
You must be signed in to change notification settings - Fork 4k
context: pluggable Storage mechanism. #2461
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Currently Context propagate in-thread by its own ThreadLocal, and cross-thread propagation must be done with the mechanisms provied by gRPC Context. However, there are frameworks (e.g. what we are using inside Google) which have already established context-propagation mechanisms. If gRPC Context may ride on top of them, it would be propagated automatically without additional effort from the application. The Storage API allows gRPC Context to be attached to anything. The default implementation still uses the ThreadLocal. If an override implementation is present, gRPC Context will use it.
| * previously bound context. For example: | ||
| * <p>A Context object can be {@link #attach attached} to the {@link Storage}, which effectively | ||
| * forms a <b>scope</b> for the context. The scope is typically bound to the current thread, though | ||
| * alternative {@link Storage} implementations can alter this behavior. Within a scope, its Context |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think Context becomes useless if it isn't bound in some way to the current thread. The scope has no meaning then, since it is basically undefined. All the use cases we're considering now are all thread-local-like storage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. Removed the possibility of non-thread-locals.
| * <p>A Context object can be {@link #attach attached} to the {@link Storage}, which effectively | ||
| * forms a <b>scope</b> for the context. The scope is typically bound to the current thread, though | ||
| * alternative {@link Storage} implementations can alter this behavior. Within a scope, its Context | ||
| * is accessible even across API boundaries, through {@link #current}. The scope can later be |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/can later be/is later/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
|
|
||
| // For testing | ||
| static Storage storage() { | ||
| if (storage == null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a static initializer? Seems simpler.
private static Storage storage = createStorage();
I don't mind continuing to use the storage() method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In cases it would throw, using a method for lazy initialization allows each attempt to use Context would throw an informative exception. With static initializer, only the first use would throw the informative exception, and subsequent uses would throw some generic "class not found". The former is a bit more friendly to users.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note that in similar stuff (like log4j2), a fallback context is used on error. This logs at bootstrap, but doesn't render the system useless.. judgement call if this is the right choice, but anyway there's prior art here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ps something like this could be fun to test using @raphw's byte buddy https://github.com/raphw/byte-buddy
seems like you could set the current classloader to some child, and load a crappy context storage into that (which you created with byte buddy). Then set the classloader back in a finally (after you've tested your errors work as should)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note that in similar stuff (like log4j2), a fallback context is used on error. This logs at bootstrap, but doesn't render the system useless.. judgement call if this is the right choice, but anyway there's prior art here.
We (@ejona86 and I) believe it's better to fail loudly if an override class is present and it fails to load. In our internal use case, failure to load the override would break the expected automatic context propagation, in which case breaking the system early is superior than letting it run with impaired functionality.
| * alternative implementation named {@code io.grpc.ContextStorageOverride} exists in the | ||
| * classpath, it will be used instead of the default implementation. | ||
| * | ||
| * <p>This API is <a href="https://github.com/grpc/grpc-java/issues/2462">experimental</a> and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hehe. We can't use the annotation.
zhangkun83
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @ejona86 !
| * previously bound context. For example: | ||
| * <p>A Context object can be {@link #attach attached} to the {@link Storage}, which effectively | ||
| * forms a <b>scope</b> for the context. The scope is typically bound to the current thread, though | ||
| * alternative {@link Storage} implementations can alter this behavior. Within a scope, its Context |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. Removed the possibility of non-thread-locals.
| * <p>A Context object can be {@link #attach attached} to the {@link Storage}, which effectively | ||
| * forms a <b>scope</b> for the context. The scope is typically bound to the current thread, though | ||
| * alternative {@link Storage} implementations can alter this behavior. Within a scope, its Context | ||
| * is accessible even across API boundaries, through {@link #current}. The scope can later be |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
|
|
||
| // For testing | ||
| static Storage storage() { | ||
| if (storage == null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In cases it would throw, using a method for lazy initialization allows each attempt to use Context would throw an informative exception. With static initializer, only the first use would throw the informative exception, and subsequent uses would throw some generic "class not found". The former is a bit more friendly to users.
|
We ***@***.*** <https://github.com/ejona86> and I) believe it's better to
fail loudly if an override class is present and it fails to load. In our
internal use case, failure to load the override would break the expected
automatic context propagation, in which case breaking the system early is
superior than letting it run with impaired functionality.
reasonable choice. just helping smoke it out :P
|
Currently Context propagate in-thread by its own ThreadLocal, and
cross-thread propagation must be done with the mechanisms provided by
gRPC Context. However, there are frameworks (e.g. what we are using
inside Google) which have already established context-propagation
mechanisms. If gRPC Context may ride on top of them, it would be
propagated automatically without additional effort from the application.
The Storage API allows gRPC Context to be attached to anything. The
default implementation still uses the ThreadLocal. If an override
implementation is present, gRPC Context will use it.