This is still the header! Main site

Roundtrips, pointers and IDs

2021/11/11

... a quick piece about latency and OOP for today.

This is post no. 42 for Kev Quirk's #100DaysToOffload challenge. The point is to write many things, not to write good ones. Please adjust quality expectations accordingly :)

Remember DCOM?

It was an over-the-network version of Microsoft's COM. It is often brought up as proof that transparent RPC doesn't work: you do want to know whether a call involves a roundtrip via the network or not, otherwise you'll write code that's great when run locally but really really slow in real life.

Which sounds like a tradeoff between API niceness and performance. So, instead of doing something like


Character hero = server.fetchHero(); // here is a network roundtrip
List friends = hero.friends(); // another roundtrip
for (Character c : friends) {
  System.out.println(c.getName()); // more roundtrips!
}
          
We need to something like

CharactersFriendsUglyBlob result = server.fetchHeroAndFriends(); // only network call
List friends = result.friends(); // all these are local calls
for (CharacterWithName c : friends) {
  System.out.println(c.getName());
}
          
... where, in the last version, you only do a roundtrip once, fetch a blob, and process it locally, instead of doing many many roundtrips for each call on each object (like in the first version).

Sadly though, this results in ugly APIs ("give me this hero with their friends but nothing else") and ugly types (... well, whatever this call returns). This the problem GraphQL is supposed to solve... which is great, but... can we do something simpler?

Passing in IDs

So here is the idea. (Partially stolen from Cap'n Proto's "time traveling" RPCs.)

The typical "atomic" RPC call looks like...


Planet homeWorld = hero.homeWorld(); // these are...
String name = homeWorld.getName();   // ... all roundtrips
          

We pass in some object reference, and we query something about it. But couldn't we just instead:


Id homeWorld;
hero.loadHomeWorldIntoId(homeWorld);

Id planetName;
homeWorld.loadPlanetNameIntoId(planetName);

await(homeWorld, planetName);
          

The idea being... instead of the server returning a reference / pointer / id of some object, which we then pass back in to query further down the tree, we could just make up an id on our own, and have the server assign a value to it... but this doesn't prevent us from using the id in a subsequent query without waiting for the response from the first request!

The code looks surprisingly similar to classic async-await, with us pretending on the client side that everything is instant... except... our "futures" / "promises" are on the server side! No roundtrips anymore.

We do get the best of two worlds here. There is no longer a need for hand-optimizing query blobs so that we return only the data we actually want in one go... but we don't need a separate language (GraphQL, etc.) either.

... comments welcome, either in email or on the (eventual) Mastodon post on Fosstodon.