Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Synchronization on the state diff level #10

Open
xaviervia opened this issue Dec 3, 2016 · 1 comment
Open

Synchronization on the state diff level #10

xaviervia opened this issue Dec 3, 2016 · 1 comment
Labels

Comments

@xaviervia
Copy link

Hi,

Thanks for publishing this.

I’m very interested in the topic of peer-to-peer store synchronization and I was planning on building a library for that on top of hyperlog or more naively on top of webrtc-signalhub (that is, without the log reconciliation). I found this project very interesting.

I was thinking however that synchronizing the store is something that can be done on state update rather than on action dispatch. I am not sure about this and I'm still trying to find out reasons for one approach or the other, so take my rationale with a grain of salt, I would like to know what you think about it:

  • While actions are the specific way that Redux manages store updates, every application built with a single central state architecture will have an object structure. If the synchronization targets the state instead of the action log, it could be a generic solution.

  • Similarly, application updates that are rolled out first on one client and only afterward on others will work gracefully with state-level sync, but will break for action-level sync. Say a new action is introduced: the old clients will not know what to do with it. If the update log contains only say, jsonpatch diffs, the older clients will be able to have their state sync without conflict until they receive the new app version.

  • In many real life applications, some actions and some parts of the state will have to be ignored by the synchronization. In my mind is more straightforward to specify this by setting a namespace of the state to be synchronized (for example, state.shared) rather than a blacklist or whitelist of action types.

  • The tools for diffing JSON are already there, so it would be simple to have a generic interface for state synchronization in which every JSON diff is a "commit", even tagged with a hash and author if so required.

An underlying assumption of these point is that store sync makes the application and the state become entities with separated lifecycles. The app might change while the state lives on, and the state might change while the app lives on in an older version.

As a way of a metaphor, synchronizing the state is closer to doing git. Synchronizing the action log is more like synchonizing the log of the keystrokes that the programmer made.

On the other hand the action log will probably be slimmer and easier to debug. Each might be a separate use case worth of different libraries.

@grrowl
Copy link
Owner

grrowl commented Dec 5, 2016

Thanks for checking it out and the feedback!

Synchronising the actions themselves has a number of benefits, including

  • Unrecognized actions are still compatible with outdated reducers, they simply won't have an effect on state — the application View/Renderables can't become out of sync with the state shape, since the application is responsible for them both. Local state might be different to remote state, but the app will render truthfully. We can also update the app and simply replay all known actions to update.
  • Clients can always emit actions, and can always replay actions emitted by other clients, even if they happened before local client's actions. There's no way to conflict, like what might happen if two clients overwrite a common value or key in the shared store.
  • With synced actions logs, shared and local actions can mutate local and shared states. Compared to store sync: Shared state changes would only be caused by shared actions, local state changes would only be caused by local actions.
    • This means you don't have to split into a local "UI" reducer and shared "posts" reducer, but can decide within a reducer which keys a public or private action affects. A relative value user.isFriend is possible, next to user.name and user.avatar.
    • Further, with redux-scuttlebutt your locally emitted actions will maintain their "correct" position in time relative to when shared actions occurred, whereas with synced stores they mustn't ever touch.

and, crucially,

  • Syncing actions means your clients can be offline for seconds, or minutes, or days, and they'll still sync. Diffing store changes opens the door to conflicts, or situations where we're not sure "who came first" or "who won" when working on the same piece of data.
  • Syncing actions leaves the decision of dealing with these conflicts up to the reducer, and therefore the application itself, so you can resolve conflicts any way which is appropriate.
  • Also (and this is the most bizarre to explain) when multiple distributed clients are connected, your local client can exist without the latest actions from one or many remote clients having been applied. When they are eventually applied, it's as if they already happened in the past. We only have to write the one set of logic (as we do with renderable components for the views) which gives us flexibility and predictability.

I agree that the two approaches serve different use cases. State-based syncing is simpler and easier to reckon about, but hopefully bringing scuttlebutt's strengths (distributed, resilient logs) to Redux's natural structure (time-travel, statelessness and integration with React) we'll be able to raise the bar for real-time client apps.

@grrowl grrowl added the question label Dec 5, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants