Thursday, July 14, 2016

GWT RPC's future

This post is about why I do not think that GWT RPC is something valuable to be carried forward.
I am also being slightly harsh with GWT RPC which has served many GWT applications as a good RPC mechanism. Please keep in mind that this post is not about assigning blame, I do think that the engineers who designed GWT RPC did a great job, they just had other more important goals in mind.

When I first discovered GWT RPC in somewhat 2008 I thought it was magical. Send all my Java objects over the wire and just use them on the client side. All I need to do is define a simple interface. I loved it.

But as time went by I have started to dislike GWT RPC more and more. This blog post is about the bad choices that GWT RPC has made and why I do not think its a good choice to be carried forward for applications that transpile Java to JavaScript. However this does not mean that its not possible to port it forward (with some changes), but I think its not worth it there are simply better alternatives.

Bad choices


GWT.create call type inheritance broken

AsyncChronousVersion instance = GWT.create(SynchronousVersion.class);
Why do I need to pass in the synchronous version? From a type perspective this does not make any sense what so ever.
The synchronos version extend the RemoveService interface, the asynchronos version did extend nothing. Why not simply use the Asynchronous version all the way?
If you know what a GWT.create call looks like in the compiler you realize that this is really broken.

Exploding code size

Let's take a look at the second problem that GWT RPC has that is way more severe.
Assume we have this simple RPC interface:

public interface MyServiceAsync {
  void doSomething(List strings, AsyncCallBack callback);
}

The interface defines a simple method that takes a list of strings. The problem with this code becomes apparent when we take a look at the serializers and deserializers that have to be generated for this class. Since we need to have a serializer for every concrete type that we have in the program, you end up with potentially hundereds of serializers just for this one method.
The GWT team always recommended to be as specific as possible to get rid of this flaw, but this is against a core design principle in Java. We want List on the interface, not ArrayList.

Doing a simple search for subtypes of ArrayList in a hello world GWT applications returns 16, this is obviously a bad choice.

Version skew during deployment

Because of the need for serializers that were specific to your current applications, GWT RPC had a serious issue with version skew. Make a slight changes to your problem and you might have ended up causing GWT RPC to fail between these version of your app. When you have multiple thousand servers running you will always have an old client talking to a new server or an old client talking to a new server. Not dealing with this is unacceptable for a RPC system and has cost many teams at Google headaches.

Slow compiles due to global analysis

If you want to generate all the serializers of a type you need to have global knowledge of your program. You need to be able to answer questins like:

  • "Who is implementing this interface?"
  • "Who is subclassing this class"
This means that you can not incrementally compile GWT RPC code since you would not be able to answer these questions correctly. The only reasons that super dev mode works with GWT RPC is that we do the initial slow full world compile and then keep track of these types as you update your code.If you want really fast compiles, which we want for GWT 3.0, you really do not want any global knowledge.

GWT RPC can not be done with a Java annotation processor

All other GWT code generators can be changed to be Java annotation processors, since they do not require global knowledge. Since GWT RPC requires global analysis it can not be easily ported.

But I really like GWT RPC, what can I do?

Well as I said earlier in this post, you can port it to an APT, but you need to make changes to it:

  - Change the interface to list all the types it needs so you do not require global knowledge
  - Remove the synchronous or asynchronous interface and generate one from the other
  - Replace the GWT.create call
  - Make the serialization format compatible with different versions of your app, by the way this is what we do with GWT proto RPC, which unfortunately is not open source.

This could potentially look like this:

public interface MyServiceAsync {
  void doSomething(@Serializer(accept = {ArrayList.class}) List strings, ...)
}

// MyServiceAsync_Factory is generated by an APT
MyServiceAsync service = MyServiceAsync_Factory.create();


I hope this blog post helps people that really like GWT RPC to understand why there are better choices one can make for a RPC mechanism and why we should be striving to have something better.

12 comments:

  1. There are multiple ways to fix the async interface:

    1. Rewrite it around a promises paradigm.
    2. Get rid of GWT.create mechanism and use an annotation processor to generate the Async proxy. IE something along the lines of @GWTRemoteService annotation on the interface instead.

    The other 3 issues are less about GWT RPC and more about SerializationPolicy implementation details and the fact that it is buried so deeply in the process. It is hard to dig through and replace the SerializationPolicy generation and even if you do, you don't bypass the old one. So I think a simple solution there would be to make the SerializationPolicy and its generator easily replaceable.

    If we could just expose the hooks that are needed into the system to create the pieces of the underlying RPC mechanism, then we could have different RPC libraries with a common interface. That should be the ultimate goal, imo. At the end of the day, GWT RPC is a set of interfaces from a consumers standpoint, those interfaces are not necessarily inherently flawed and there is no reason we couldn't have drop in replacements for different flavors of RPC.

    ReplyDelete
  2. Hi Jonathon,

    how would to implement serialization for List<> for any generic implementor of the interface in JavaScript without a significant code size hit?
    The fundamental problem here is that one can choose arbitrary Java objects on the interface and thus will always end up with that kind of bloat.





    ReplyDelete
    Replies
    1. I would imagine a similar case to the current code pruning to determine actual types returned. The actual call tree for a particular RPC service is generally relatively small. IE a service calls a database or another service and does some conversion of the data to a model that is returned to the UI. The implementations of the RPC interface and its call tree could be analyzed to determine what type is actually returned using some form of static analysis and mark that serializer as used. This could also be assisted, as you recommended, with an annotation. I would like the annotation to be on the implementation instead of the interface though. There could possibly be multiple implementations of said interface, yes, but either of those approaches would still be better. Finally, another option, if I return a generic interface, especially a List or Map or Set, I think there would be an option to ignore the actual implementation that is returned and use a generic serializer that creates a particular implementation of the object. This could also be assisted on client side with some configuration in the module file.

      Another, current way, to limit the code bloat is to increase the RPC blacklist. I have done so on the majority of my own projects. I have, across all my projects, blacklisted everything that exists in a *.client.* package, with some tuning for particular gwt things. So I could see a scenario where you scan the rpc interfaces and implementations, then scan the generated AST for the client side, look for actual concretely used implementations during the RPC calls and make a decision as to whether or not the actual implementation is needed or a designated common generic serializer could be used.

      Another kind of similar practical approach would be to incorporate a bit more hierarchy in the serialization generators. Most of the collection/map custom field serializers are based upon two custom field serializers in GWT core. Those could theoretically be leveraged to generate a field serializer for all collections. So all you would need for the majority of collection types would be to generate a base custom field serializer, designate that, and do a pregeneration of serializers, then utilizing code pruning, eliminate those that are never used.

      Finally, instead of using a regular annotation processor, one could utilize a java agent, moving the serialization out of post processing to compile time processing.

      I think, some in part or all of the above proposals, along with modularizing the SerializationPolicy generator interface and the underlying GWT RPC architecture would make "GWT RPC" be less about the current implementation and more about the manner in which RPC is consumed.

      Delete
  3. Which are the "better alternatives"? You mention them at the beginning of the post but they are not named. We would like to know what should we use in the future if GWT RPC is going to be deprecated.

    ReplyDelete
  4. Some question from my side. Is Errai or RestyGWT good alternative? We are in the process of replacing GWT-RPC in our ERP app. Should we jump on some of this alternative or better to wait?

    ReplyDelete
  5. With GWT 3.0 most will go with REST or protocol buffers which are much lighter...

    ReplyDelete
  6. I purposefully did not mention any replacements for GWT RPC. I strongly believe that people should take the time to understand how a certain mechanism works and then decide if it fits their purpose well.
    If I recommend something here it looks like a straight recommendation from the GWT team which I do not want. I'd rather see different RPC solutions evolve and become better and convince users by having a solid technical approach.

    ReplyDelete
  7. OK, thanks. It would be very useful for us to have a list of the alternatives to RPC which are (or will be) included in the GWT library. Is this possible? Thanks!

    ReplyDelete
  8. You do point some issues with that but all of them I can match with numerous issues in ANY other approach. We use GWT RPC to a very large extent and have solved / worked around some of the issues you mention. Specifically, we use GWT RPC mainly for serialization. No other known framework offers anything close to the functionality that GWT serialization has, such as, for example, maintaining proper object graphs without duplicating instances that have multiple references to them (that is just one, there are others).

    We route *everything* through essentially one single service and one single method in that service - we pass expression trees to this method that are, then, evaluated on the server. The code backing these expression trees is partly generated and it looks as if the client can make the calls to the server just by annotating a server-side method. The only difference is that all calls are asynchronous so, *if* you want the return value you need to pass an AsyncCallback.

    Slow compiles are a problem especially when interfaces are used for field declarations, which is why we avoid those. But this *can* be addressed in the GWT compiler easily with more efficient searching. It appears as if it is searching over and over again for those instead of storing the class hierarchy somewhere and using it as an index, but this may not really be the case.

    In any case, GWT RPC can be improved, it does not and should NOT be dropped.



    ReplyDelete
  9. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. I just wrote a post about alernatives to RPC in GWT: http://goo.gl/0jPF5p 
      Hope it helps.

      Delete
  10. Online business is viewed as a standout amongst the most critical elements of the Internet. All sorts of items and administrations can be seen accessible on the web. With a huge number of web clients consistently, you quickly have a great many conceivable clients for deals transformation.easy proven way to start a blog

    ReplyDelete