grrowl / redux-scuttlebutt

Distributed replicated redux store
170 stars 14 forks source link

[Discussion] Why syncing actions instead of data model? #1

Closed coodoo closed 8 years ago

coodoo commented 8 years ago

I have significant experience with scuttlebutt and related data types (crdt in particular), and redux-scuttlebutt looks like a really interesting way to approach syncing among users, just wondering why you choose to sync the actions, instead of the underlying data structures, say the store or state itself? Wouldn't it be simpler to keep one single source of truth and sync it across users?

grrowl commented 8 years ago

Great question! For background, I have significant experience with React and Redux and am only getting into scuttlebutt and CRDTs in general so I greatly appreciate feedback about this.

To me it was by default — researching how CRDTs (the Commutative kind) work I noticed that CmRDT "operations" are kinda like "actions", they're applied to the state. Redux time travel allows updates to be commutative since we can apply the update at any point in time. The underlying data structure is a pure result of the actions ( and a lot smaller/cheaper to sync over a network edit: not strictly true)

The "Conflict-free" part is up to your reducer. If you have conflict-free actions and reducers, you have a conflict-free app. This is not always the case — two peers might dispatch PICK_UP_ITEM, which [precondition] requires an item to exist and then [result] removes it from the world. One of these peers/players will conflict and their action will be un-done. Conversely, INSERT_TEXT_AT_POSITION is a conflict free action: you can always insert text at a string position, if the string exists.

coodoo commented 8 years ago

Thanks for the explanation, mostly make sense to me 👍

As a side note, earlier this year I built a prototype along this train of thought but synced the underlying data model, scuttlebutt/Model in that case, between clients, on top of that it updates the redux store, and in turn refreshes the app, the idea was taking data model as the single source of truth and let it handles conflict resolution and stuff, hence making redux store a passive actor.

Goal of that POC was offline-ready, when clients went offline, changes they made could be persisted locally and guaranteed to sync and converge once online, scuttlebutt and crdt makes this really easy to implement.

I would assume in your approach you will persist multiple actions while offline and batch apply those once online and synced, wondering how's it working so far (performance, correctness, scalability)?

grrowl commented 8 years ago

Actions dispatched while offline are stored in the history all the same, and upon new connection are synced. This is all scuttlebutt, we just return all "new" updates through the history(sources) implementation.

We currently persist all actions forever, and a snapshot of the state after each action was applied. This makes it more efficient to replay actions since we walk back through history until thisTimestamp > historyItemTimestamp, reset the state to the snapshot, and replay all actions including the new one.

Otherwise: it's as fast as Redux/JS (very fast), actions/updates are emitted to peers at dispatch time (the scuttlebutt protocol is designed to be low latency), and there's very little "heavy lifting" going on, so it's efficient*

* With the exception of having to sync all actions on application start — It calls reduxStore.subscribe handlers for every update. I'm fixing this up today.

coodoo commented 8 years ago

Sounds good, is that project up somewhere to see?

grrowl commented 8 years ago

We currently ensure ordering of actions in the reducer by adding a meta.@@scuttlebutt/TIMESTAMP key to the action when it first enters the Dispatcher, and referring to this at reduce time. The reducer is here

grrowl commented 8 years ago

Closing this as I think we've addressed the questions :)