Hey folks, welcome to the tenth issue of Browsertech Digest.
Apps are distributed systems now
Something that’s been fascinating me lately is how distributed systems theory has quietly snuck into the application layer of software.
Web apps have long been built on top of a distributed data layer — a replicated RDBMS, say, or a web-scale document store. But the complexity was handled by database engineers, not application engineers. Client-side code could reason about “the server” as an abstract, singular entity, blissfully unconcerned by its distributed nature.
As browser-based applications become richer and strive for desktop-caliber performance, this is changing. Front-ends are shifting from being dumb consumers of a REST API to acting more like nodes in a distributed system.
Two examples:
This shift happened gradually and has kind of flown under the radar. As a result, frontend engineers haven’t fully realized how relevant some old ideas from distributed systems engineering now are to their work.
Synchronize state with this one weird trick
One of my favorite of those old ideas is state machine synchronization. This Signals & Thread episode is a great intro (I also touched on it in You might not need a CRDT).
The idea is that with two simple ingredients, you can synchronize the state of any program. Those ingredients are:
By totally-ordered, I mean that every machine in the system sees the messages in the same order. By fully-deterministic, I mean that given the same inputs always result in the same outputs, i.e. that no external state can influence the computation.
Once you have those two things, synchronizing state is straightforward: just put all of the inputs to the program into the distributed stream, and ensure that the only inputs that the deterministic compute sees are ones that came out of the stream.
As long as every node starts with the same initial state and consumes the stream from the beginning, any two nodes that have consumed the stream entirely are guaranteed to have the same state and sequence of outputs.
Determinism in WebAssembly
Making a system deterministic is difficult if it wasn’t designed to be – just look at the trouble nix has to go through to make builds deterministic.
As it happens, WebAssembly lists determinism as a design goal. This stems in part from its isolation model — you can’t even ask for today’s date in WebAssembly unless the runtime explicitly provides a way to do so.
A result of this is that if you can compile code to WebAssembly, you’re already half way to having synchronized state in a distributed system.
My own dabbling with this started when I built a word game in WebAssembly, where the entire game works as a replicated state machine. Even seemingly non-deterministic things like randomizing tiles are done by distributing a seeded pseudo-random number generator. So the only thing that goes over the wire is events, not state. (The game is now hosted at word.red, powered by Jamsocket.)
Ian Kettlewell has recently demonstrated some work on this that’s even more real-time, synchronizing a live 2D physics simulation across the network. Rather than doing synchronization from within WebAssembly as word.red does, Ian’s work synchronizes at the host/module boundary, which is cool because it provides stronger guarantees of determinism.
WebAssembly’s killer feature
A somewhat related idea is durable actors, where you can write a stateful actor that appears to run forever, but is actually being cryogenically frozen to disk when idle. Bernard Kolobara of the Lunatic WebAssembly runtime believes this will be a killer use case for WebAssembly, and I tend to agree.
Browsertech SF is Thursday!
Registration is still open if you’re in the area. It’s going to be a good one!
Until next time,
Paul