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

Do we separate basic model sync ("delta") from updates on changes ("notification") a la CQRS? #291

Open
enikao opened this issue Jul 12, 2024 · 3 comments

Comments

@enikao
Copy link
Contributor

enikao commented Jul 12, 2024

aka separation between "intent" and "consequence"
aka separation between "commands" and "events"

Might or might not mean that clients send only commands, and repo sends only events.

World views

No delta protocol at all, just notifications on changes

Clients can update their state based on notifications, not another bulk read.

If client makes local changes and gets notifications, it's the client job to disentangle them.

Pro:

  • A lot easier to implement

Con:

  • Mostly misses central idea of LW: Synchronizing models

Small-grained change commands

Similar to above, but a client can also send small explicit updates (client sends "I move this node there", not the new partial model from which the repo has to reconstruct the change).

Repo might reject command e.g. because trying to move a node below a deleted node.
Clients cannot reject notification.

Pro:

  • Easier to specify
  • Enables stricter defined semantics/functionality on top

Con:

  • Clients might not understand each other properly

Advantage over bulk:

  • More efficient
  • Aligns with UI interactions
  • More fine-grained change commands, the less likely are collisions

OT-style state updates

Clients send their changes to repo, repo disentangles them, repo sends updates that bring all clients to same state
--> might send different updates to different clients (or some other trick that conceptually does the same thing with a client-side library)

Repo never rejects change, but might rewrite it.

Client's model is managed by a "driver" which guarantees (eventual) consistency of the model on all clients and the repo.
Client's model cannot refuse synced changes.

Pro:

  • Clients can rely on properly synced model (assuming correct implementation)

Con:

  • Fixes all clients on same sync implementation

Repo resolves all conflicts

Client sends update, server delays replies to that client until it resolves all conflicts and then sends appropriate updates to client.

Pro:

  • Concentrates complexity on repo, clients don't have to deal with it
  • Repo can chose its strategy. Example: If a client edits a feature of a deleted node, repo can either refuse that edit, or err on the side of never losing user data and reinstate that node (assuming it keeps the appropriate history).

Con:

Decision: CQRS-style, conflict resolution only by repository

@enikao
Copy link
Contributor Author

enikao commented Jul 30, 2024

Variants on Repo resolves all conflicts

Immediately apply changes to client model

Two conflicting clients

  1. Client x deletes node A, including contained node B
    A and B are gone from client x model (i.e. not part of the client's model, but still available on a graveyard)
  2. Client y changes property on B
    property has value B in client y's model
  3. Repo receives deletion from x
  4. Repo receives property change from y
  5. Repo does conflict resolution, doesn't delete A or B, changes property on B
  6. Repo rejects change from Client x
  7. Client x reinstates A and B as part of the real model
  8. Repo accepts change from Client y
  9. Repo sends update to client x: B.prop = x

Two conflicting clients, one non-conflicting client

  1. Client x deletes node A, including contained node B
    A and B are gone from client model x (i.e. not part of the client's model, but still available on a graveyard)
  2. Client z edits node C (not related to A or B)
  3. Client y changes property on B
  4. ?

Client keeps "dirty" model during change

Two conflicting clients

  1. Client x deletes node A, including contained node B
    Client x has a dirty state without A and B, but internal model still contains them
  2. Client y changes property on B
    Client y_ has dirty state with changed property B, but internal model still has old value
  3. Repo receives deletion from x
  4. Repo receives property change from y
  5. Repo does conflict resolution, doesn't delete A or B, changes property on B
  6. Repo tells client x "I processed your change unsuccessfully"
  7. Client x discards its dirty state as invalid
  8. Repo tells client y "I processed your change successfully"
  9. Client y discards its dirty state as applied
  10. Repo does NOT send update to delete A and B
  11. Repo sends update to client x and y: B.prop = x
  12. Client x updates its internal model with B = x
  13. Client y updates its internal model with B = x

Two conflicting clients, one non-conflicting client

  1. Client x deletes node A, including contained node B
    Client x has a dirty state without A and B, but internal model still contains them
  2. Client z edits node C (not related to A or B)
  3. Client y changes property on B
  4. ?

Other scenarios

Unrecoverable error

  1. Client x sends "move node" with out-of-range index
  2. Repo denies request

Questions

@enikao
Copy link
Contributor Author

enikao commented Jul 31, 2024

If we assume the repo is the only one doing conflict resolution, and the only requirement is "a client is up-to-date and consistent after applying all changes in order", the repo has a lot of freedom.

Example:
0. property time has value now.

  1. Client x changes property time to morning
  2. Client y changes property time to evening

Possible actions of repo:

a. sends update time = morning (maybe client x always has precedence, or client y does not have write privileges on that node)
b. sends update time = evening (real conflict resolution)
c. sends updates time = morning, then time = evening (command ordering)
d. sends updates time = evening, then time = morning, then time = evening (corrective update)
e. doesn't send any update, discards both changes (maybe both morning and evening are invalid)

@enikao
Copy link
Contributor Author

enikao commented Aug 2, 2024

Only accept change requests, distribute events. No guarantees on real-time collaboration.
Might extend/change protocol later to cater for rtc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant