Skip to content

Conversation

@zhangkun83
Copy link
Contributor

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.

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
Copy link
Member

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.

Copy link
Contributor Author

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
Copy link
Member

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/

Copy link
Contributor Author

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) {
Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Copy link
Contributor

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)

Copy link
Contributor Author

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
Copy link
Member

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.

Copy link
Contributor Author

@zhangkun83 zhangkun83 left a 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
Copy link
Contributor Author

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
Copy link
Contributor Author

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) {
Copy link
Contributor Author

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.

@codefromthecrypt
Copy link
Contributor

codefromthecrypt commented Dec 4, 2016 via email

@lock lock bot locked as resolved and limited conversation to collaborators Jan 21, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants