Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@ejona86
Copy link
Member

@ejona86 ejona86 commented Jul 18, 2015

@jhump, does this approach work for you? I did end up coming up with a fix for the type-safety issue by checking that the original serializer of the protobuf is the same as the deserializer.


The commits are split up for easier reviewing. The commit messages do have some useful notes. The JMH benchmark was added to be able to compare the performance to the other transports.

I'm not actually sure which project we want the transport in. I put it in core for now, but I am very okay with moving it elsewhere. I would want it reasonably accessible, as things like the stubs should probably use it for their tests.

The transport is fully-featured with full support for flow control and running multiple instances. With this transport, we could actually test client-side load balancing and other higher-level features without mocking and without bringing in large dependencies. That will lead to more reliable, maintainable, readable, and writable tests.

The DEADLINE_EXCEEDED change was necessary because the ServerImpl reliably starts its deadline timer before the ChannelImpl does (because ChannelImpl starts the timer after the transport has created the call). We needed the fix already and I had already discussed with gRPC C folks about the need for it, but it was a prerequisite to get tests passing with this transport while also making the transport act like a real HTTP/2 transport.

The benchmark tested the unloaded latency of the various transports:

Benchmark                         (direct)  (transport)    Mode    Cnt       Score      Error  Units
TransportBenchmark.unaryCall1024      true    INPROCESS  sample  97387    1609.799 ±    3.678  ns/op
TransportBenchmark.unaryCall1024      true        NETTY  sample  25006  199282.635 ± 2895.446  ns/op
TransportBenchmark.unaryCall1024      true  NETTY_LOCAL  sample  29979  166393.467 ± 1843.578  ns/op
TransportBenchmark.unaryCall1024      true       OKHTTP  sample  25210  197704.896 ± 2715.707  ns/op
TransportBenchmark.unaryCall1024     false    INPROCESS  sample  81584   30604.337 ±  228.763  ns/op
TransportBenchmark.unaryCall1024     false        NETTY  sample  20244  245859.231 ± 4941.596  ns/op
TransportBenchmark.unaryCall1024     false  NETTY_LOCAL  sample  20039  248849.163 ± 3317.106  ns/op
TransportBenchmark.unaryCall1024     false       OKHTTP  sample  23012  216423.620 ± 2862.112  ns/op

Resolves #518

@ejona86 ejona86 force-pushed the inprocess-transport branch 3 times, most recently from 8ee08e8 to e224066 Compare July 18, 2015 21:07
@jhump
Copy link
Contributor

jhump commented Jul 18, 2015

@ejona86, I think this looks pretty good. I started going through reviewing each commit, but maybe that's premature? I'm a little concerned about having to register them by name into a static set. Code that wants to start multiple servers in the same JVM would have to generate unique names.

Do we need the client and server to be so de-coupled? From my perspective, I'd prefer ditch the static registry and require callers to supply an InProcessServer when building the client, to make the link explicit.

@ejona86
Copy link
Member Author

ejona86 commented Jul 18, 2015

@jhump, I wasn't finding it necessary for you to do a full review (but if you do, I value it!). I was mainly interested in how you felt about high-level approach, performance, and construction details. Basically, does it help you and what do you dislike about it?

I think the registry-variety will be needed and useful. With it we can communicate to disparate parts of code what to connect to. All the load balancing and naming discussions have led to "us" (but not yet Java) moving to a URI-based mechanism of naming. With it, we could conceivably have a "inprocess:///registeredName" address and have a utility to auto-select the in-process transport.

I'm completely fine with having an anonymous server and passing it to the channel builder to bind. I considered doing it initially, but figured it would be easy to add. It would still need to be shutdown manually, and getting rid of that without using a finalizer is tricky. I'm working on a registry-free version now.

@ejona86
Copy link
Member Author

ejona86 commented Jul 19, 2015

Hmm... registry-free is a bit tricky since we don't have a reference to the server transport.

We could have some object that you pass to the builder that is given knowledge of the server transport and then lets you construct channels. Seems like it has all the properties that you want, but feels weak.

We could have the registry and allow auto-generated names or use objects as keys. "new Object()" is pretty easy to make sure you have a unique key. Depending on how we did the auto-generated name, it might help to implement #72.

These don't seem to be hard to implement, but it does seem hard to choose one. I'm going to continue thinking about it.

@ejona86
Copy link
Member Author

ejona86 commented Jul 19, 2015

@jhump, sorry about my initial message for this PR. I later realized why the confusion, because it seemed "The commits are split up for easier reviewing" was directed toward you. (I've since put a HR separator.) I'm glad you stopped when things seemed strange.

@jhump
Copy link
Contributor

jhump commented Jul 21, 2015

This looks like a great start, and the performance test results do look very compelling.

I'm still a little skeptical of having to serialize and deserialize every message. I bet the features of the transport could be used without needing serialization -- like if Marshaller, Transport, Stream, and StreamListener could be parameterized by the representation of a message (e.g. other than InputStream). Then you could have an "identity" marshaller, something like Marshaller<T, T> instead of Marshaller<T, InputStream>.

(Related, but not directly pertinent: I noticed that there is still no safety on the serializers -- for example, a client Marshaller generating JSON but the server expecting proto would fail in possibly surprising ways/places. The Java implementation does not support the content type suffix that is described in the protocol spec -- "+proto", "+json", or other custom value -- which is how the actual encoding is supposed to be conveyed I think.)

Regarding the registry, I'm not sure I understand why making it registry-free is tricky. If you make InProcessServer a public class, then InProcessChannelBuilder can accept it (as an alternative to accepting a name) and pass it along to the InProcessTransport (instead of requiring the transport look it up by name). I can see how the registry is still useful, but it could be an explicit separate step to register the server after creating it. Being able to pass the actual server instance in, vs. requiring a runtime lookup, provides stronger compile-time safety.

@ejona86 ejona86 force-pushed the inprocess-transport branch from 288517e to 7b7e927 Compare July 21, 2015 16:01
@ejona86
Copy link
Member Author

ejona86 commented Jul 21, 2015

@jhump, we don't actually serialize/deserialize every message unless it appears we have to. That was the point of c6e4a82. The InputStream returned by ProtoUtils was already lazy; it would only serialize the proto when read() was called. With that commit I detect if the InputStream already has a proto inside and return that proto if it is compatible. That means we do an allocation for the InputStream but no serialization, at least for protobuf which has immutable objects.

Concerning the serialization checking: I'm less concerned about proto vs JSON and more concerned of message types matching. Getting the message types wrong seems much more likely than the encoding, and there is no current defense for that. The proto vs JSON seems more like a special case. As of today, no implementation is sending the +proto or +json syntax, as they don't actually know the type and it was added to the spec without discussion. When receiving, implementations are ignoring it as well. I'd be more interested in a very descriptive "message-type" header like "application/protobuf; message=google.protobuf.Empty". It would still take plumbing to make use of, but it could solve the problem.

The suggestion about making InProcessServer public to avoid the registry sounds doable. I'll look into it. I do think there will be some interaction with AbstractChannelBuilder, but it does sound reasonable.

@nmittler
Copy link

@ejona86 Let's separate out the first 4 commits. They LGTM after a couple of comments are addressed.

@nmittler
Copy link

@ejona86 Just a few comments on the in-process transport. Looks great!

@jhump
Copy link
Contributor

jhump commented Jul 21, 2015

@ejona86, my blunder. Sorry for the noise and thanks for linking to that commit. For some reason, I stopped at the "implement InProcessTransport" commit and didn't look closely at the subsequent commit messages. The lack of serialization does explain why the JMH benchmark results are so good!

In that case, this is perfect! I still like the idea of not needing to register and lookup in-process server instances, but that's a minor point that's certainly not a blocker.

Thanks!

@ejona86
Copy link
Member Author

ejona86 commented Jul 22, 2015

@jhump, I don't really blame you, there was a lot to look through and there were multiple pieces I could have communicated better. It is actually unfortunate that the serialization circumvention is already done, as otherwise the JMH benchmark would get even better :-P

I'll be quickly trying to add the construction pattern that avoids the registry, because I would actually like that too. If it starts slowing me down too much I'll skip it for now; I do agree it doesn't seem a blocker, and seems it should be same difficulty to add now or later.

@ejona86 ejona86 force-pushed the inprocess-transport branch from 7a6c90b to bab0843 Compare July 22, 2015 16:35
@ejona86
Copy link
Member Author

ejona86 commented Jul 22, 2015

@nmittler, I've pushed the 4 commits that were just prepwork for the inprocess transport. I've rebased this PR, but left my fixups separate so that you don't have to worry about me changing commits you already reviewed.

@nmittler
Copy link

@ejona86 LGTM ... we can address further changes (e.g. no registry) with follow-on PRs as needed.

ejona86 added 3 commits July 22, 2015 15:54
In-memory transport provides back the same input stream that was
provided, so if we notice our own object simply avoid serializing.

DeferredProtoInputStream is made package-private because 1) it doesn't
seem we need it to be public and 2) the change depends on it being
package-private so the constructor can be changed.
The only benchmark method measures latency of a unary call in an
unloaded environment.
@ejona86 ejona86 force-pushed the inprocess-transport branch from 924c457 to bf53a0e Compare July 22, 2015 22:55
@ejona86 ejona86 merged commit bf53a0e into grpc:master Jul 22, 2015
@ejona86 ejona86 deleted the inprocess-transport branch July 22, 2015 22:57
@ejona86
Copy link
Member Author

ejona86 commented Jul 22, 2015

Submitted changes without "no registry" support, since @nmittler reviewed before I got them done. The will be done in a follow-up.

@lock lock bot locked as resolved and limited conversation to collaborators Jan 22, 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.

In-Process Transport

3 participants