Remix.run Logo
spankalee 4 days ago

Looking at this quickly, it does seem to require (or strongly encourage?) a stateful server to hold on to the import and export tables and the state of objects in each.

One thing about a traditional RPC system where every call is top-level and you pass keys and such on every call is that multiple calls in a sequence can usually land on different servers and work fine.

Is there a way to serialize and store the import/export tables to a database so you can do the same here, or do you really need something like server affinity or Durable Objects?

kentonv 4 days ago | parent | next [-]

The state only lives for a single RPC session.

When using WebSockets, that's the lifetime of the WebSocket.

But when using the HTTP batch transport, a session is a single HTTP request, that performs a batch of calls all at once.

So there's actually no need to hold state across multiple HTTP requests or connections, at least as far as Cap'n Web is concerned.

This does imply that you shouldn't design a protocol where it would be catastrophic if the session suddenly disconnected in the middle and you lost all your capabilities. It should be possible to reconnect and reconstruct them.

crabmusket 4 days ago | parent | next [-]

Hmm so websocket reconnects could break state? Important to know when building on this, to e.g. re-establish the object graph when beginning a reconnected session? Or when using the http protocol is a possibility - to e.g. always include the "get object" as the first call in the batch.

kentonv 4 days ago | parent [-]

Yes, when you reconnect the WebSocket, the client will need to call methods to get new instances of any objects it needs. The old stubs are permanently broken.

FWIW the way I've handled this in a React app is, the root stub gets passed in as a prop to the root component, and children call the appropriate methods to get whatever objects they need from it. When the connection is lost, a new one is created, and the new root stub passed into the root component, which causes everything downstream to re-run exactly as you'd want. Seems to work well.

doctorpangloss 4 days ago | parent | prev [-]

^ maybe the most knowledgable person in the world about these gritty details

RPC SDKs should have session management, otherwise you end up in this situation:

"Any sufficiently complicated gRPC or Cap'n'Proto program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Akka"

vereis 4 days ago | parent | next [-]

i think the original quote is "Any sufficiently complicated concurrent program in another language contains an ad hoc informally-specified bug-ridden slow implementation of half of Erlang" :-) but your point still stands

coryrc 4 days ago | parent [-]

Not quite :) https://en.m.wikipedia.org/wiki/Greenspun%27s_tenth_rule

paradox460 4 days ago | parent | prev [-]

Isn't akka just a poor man's erlang

cogman10 4 days ago | parent | prev | next [-]

That's what I noticed reading through this.

It looks like the server affinity is accomplished by using websockets. The http batching simply sends all the requests at once and then waits for the response.

I don't love this because it makes load balancing hard. If a bunch of chatty clients get a socket to the same server, now that server is burdened and potentially overloadable.

Further, it makes scaling in/out servers really annoying. Persistent long lived connections are beasts to deal with because now you have to handle that "what do I do if multiple requests are in flight?".

One more thing I don't really love about this, it requires a timely client. This seems like it might be trivial to DDOS as a client can simply send a stream of push events and never pull. The server would then be burdened to keep those responses around so long as the client remains connected. That seems bad.

fleventynine 4 days ago | parent [-]

Yeah, I think to make a system using this really scale you'd have to add support for this protocol in your load balancer / DDOS defenses.

dgl 4 days ago | parent [-]

This isn't really that different to GWT, which Google has been scaling for a long time. My knowledge is a little outdated, however more complex applications had a "UI" server component which talked to multiple "API" backend components, doing internal load balancing between them.

Architecturally I don't think it makes sense to support this in a load balancer, you instead want to pass back a "cost" or outright decisions to your load balancing layer.

Also note the "batch-pipelining" example is just a node.js client; this already supports not just browsers as clients, so you could always add another layer of abstraction (the "fundamental theorem of software engineering").

afiori 4 days ago | parent | prev [-]

My limited knowledge of Cap'n'Proto (read the docs once years ago) server and clients can peer stubs, so that is server C receives a stub originated from server A via client B then C can try to call A directly.