Filter mechanism for server transport creation and deletion
See original GitHub issueTo support a per-connection throttling mechanism used inside google, gRPC needs to allow user to add filters to the server that:
- Gets called when a server transport is created and handshake-completed, and has access to transport-specific attributes such as client address and identity.
- Adds per-transport attributes to ServerCall attributes.
- Gets called when a server transport is terminated.
I discussed a few options with @ejona86:
ServerInterceptor
The filter returns a ServerInterceptor
when transport is created. The interceptor is transport-scoped, unlike ordinary interceptors that are service-scoped.
interface ServerTransportFilter {
ServerInterceptor transportCreated(SomePublicServerTransportType transport);
void transportTerminated(SomePublicServerTransportType transport);
}
Pros:
- Doesn’t touch transport API (ServerTransport, ServerStream) which are internal.
- Re-uses
ServerInterceptor
for purposes thatServerInterceptor
is designed for (modifying ServerCall attributes etc).
Cons:
- Changes the scope of
ServerInterceptor
. @ejona86 thinks bondingServerInterceptor
to a transport is seemingly generic but actually an ad-hoc solution for a particular problem. I don’t necessarily agree though.
Decorating transport
The filter returns a decorated ServerListener
at registration. Through a few more layers of wrapping, the filter would be able to decorate ServerListener.transportCreated(),
ServerTransportListener.transportTerminated() and ServerStream.attributes()
.
Pros:
- Minimal API change
Cons:
- Heavily coupled with transport API which is internal.
- Duplicates the existing interceptor mechanism, but on
ServerStream
instead ofServerCall
. It would be ideal to just decorateServerCall
, which is whatServerInterceptor
does.ServerInterceptor
currently is per-service, but our use case need it to be per-connection. - Even more layers of decoration than the interceptors. The filter can be annoying to write.
Ad-hoc
Just for our original requirements, the filter would return per-transport attributes that are to be merged into ServerCall attributes.
interface ServerTransportFilter {
Attributes transportCreated(SomePublicServerTransportType transport);
void transportTerminated(SomePublicServerTransportType transport);
}
It probably won’t work in this form, because the attributes such as peer security identity may not be available immediately after the transport is created, if the security handshake has not completed yet. On the other hand, the first two options can work around this situation by reading the attributes only when the first stream is created.
Issue Analytics
- State:
- Created 7 years ago
- Reactions:2
- Comments:11 (10 by maintainers)
Top GitHub Comments
@ejona86 @louiscryan and I had a further discussion about this issue. We feel that the Context-based interface earlier has two issues:
Our consensus is that we keep
ServerCall.attributes()
, use it to pass data from the filter to interceptors, and call out transport termination explicitly. We have agreed on the following interface:@lukaszx0 WRT Attributes vs. Context, we have drawn a clear line between them. I hope this addresses your confusion.
ServerInterceptor is the one that consumes Attributes and produces Context.
@zhangkun83 sorry for missing original mention.
I like the idea of using context for everything and removing
ServerCall
attributes. Even now, when I have fairly good knowledge of internals in this particular area I am sometimes confused if stuff is either in context or call attributes. Consolidating them into one place - context - would make things simpler.Looping in @jhump who might have opinions on the main issue discussed here.