redux-scuttlebutt; eventually consistent shared state among peers.
4nd Apr 2016
React enables us to render from any data state in an efficient way. Flux is a structure of modifying that state, and Redux is the library we use to do so.
Currently, fetching and saving data to remote servers is usually achieved with RESTful calls to an API. Syncing between peers is usually achieved with direct WebSockets connections which stream all actions as they happen.
There’s actually a lot of great research around reading and writing to a common data store among peers, using CRDTs. The C stands for Conflict-free, Commutative, or Convergent. The rest, Replicated Data Type.
“Conflict-free” means the data type restricts operations to those which can happen separately and be resolved later without disagreeing. For example, if you can only add and remove elements from an array, it doesn’t matter if you add an element which already exists, just as if you remove an element which doesn’t exist. Otherwise, adds and removes just happen. Implementation of conflict-free actions is outside the scope of this article.
“Commutative” means updates can be received in any order and yield the same result, and “convergent” means all peers will eventually end up with all updates and yield same result.
Convergent Replicated Data Types
In Redux, you dispatch actions which are applied to a state, resulting in a new state. Actions are always applied in order of their timestamp. Redux, thanks to its purity, lends itself very nicely to CRDT implementations — as long as actions are ordered (this is commutative), and all the actions are applied (this is convergent), any peer’s state will match any other peer’s state.
Scuttlebutt is a gossip protocol which facilitates flow control, operation reconciliation, and is designed to not be too chatty over the network. It’s designed as a base class which CRDTs can be implemented on top of.
redux-scuttlebutt is a Redux store enhancer which extends Scuttlebutt. As updates come in, we ensure they are inserted in time order, and validate their application.
No need to dispatch API-aware actions. No side effects in your app. Offline by default. Actions happen instantly and your app reflects the changes instantly.
Use cases include real-time apps like chat, games, and apps with streams of user-generated data; as well as real-ish-time apps which are less action-heavy but sync when the network is available.
The library is here: https://github.com/grrowl/redux-scuttlebutt. You can plug it into any pure, deterministic Redux application and it “just works”.
The best showcase of the strong capabilities of such an enhancement is a multiplayer-enabled Conway’s Game of Life. Clients can toggle squares and step the simulation. Any updates which are received from the past are inserted into the past. Therefore, the “present” is always the most up-to-date version of changes you’re aware of.
A visual of real-world result
These are screenshots from the Game of Life example. The peers are delayed by 2000ms.
The right peer draws a line on its canvas. These actions have not yet reached the left peer.
The left receives the line and advances the simulation two “steps”. The “steps” have not reached the right client
The left peer is stable. The right peer receives the first step.
The second step makes it to the right peer. The states have converged.
As this example demonstrates, even when realities diverge they are easily converged due to Redux’s immutable actions and state, and the time-travel capabilities of redux-scuttlebutt.
If you’re interested in this, please check out the repo or drop me a line.
- redux-scuttlebutt: https://github.com/grrowl/redux-scuttlebutt (includes counter and chat examples)
- redux-game-of-life-scuttlebutt: https://github.com/grrowl/redux-game-of-life-scuttlebutt
- Contact me on twitter or by email.
(this post originally appeared on Medium)