Skip to content

Instantly share code, notes, and snippets.

@tomalec
Last active August 29, 2015 14:07
Show Gist options
  • Select an option

  • Save tomalec/0e8d9f547a39a7797e45 to your computer and use it in GitHub Desktop.

Select an option

Save tomalec/0e8d9f547a39a7797e45 to your computer and use it in GitHub Desktop.
Puppets OT. Many users removes the same row

Scenario

 - Alice: add apple
 - Server: apple added
 - Alice: remove oranges
 * Alice message gets delayed *
 - Bob: remove oranges
 - Server: oranges removed
 * Alice message finally comes to the server*
 - Server: oranges (already) removed

Initial state of View-Model for Alice

{
  "_ServerVersion": 0, 
  "_ClientVersion$": 0, 
  "Ingredients": [
  	{
  		"name": "Juicy Orange",
  		"amount": 3
  	},
  	{
  		"name": "Juicy Apple",
  		"amount": 2
  	},
  	{
  		"name": "Banana",
  		"amount": 1
  	}
  	],
  "TotalCost": 60,
  ...
}

Alice is adding new Juicy Apple to the Ingredients list:

[{"op": "test", "path": "/_ServerVersion", "value": 0},
{"op": "replace", "path": "/_ClientVersion$", "value": 1},
{"op": "replace", "path": "/Ingredients/1/amount$", "value": "3"}]

Server updates the DB , and responds: [{"op": "test", "path": "/_ClientVersion", "value": 1}, {"op": "replace", "path": "/_ServerVersion$", "value": 1}, {"op": "replace", "path": "/TotalCost", "value": 70}]


Alice goes offline,

and clicks to remove Oranges from list

[{"op": "test", "path": "/_ServerVersion", "value": 1},
{"op": "replace", "path": "/_ClientVersion$", "value": 2},
{"op": "replace", "path": "/Ingredients/0/amount$", "value": "0"}] //instead of Delete = null

Meanwhile Bob comes online

Bob initial state

{
  "_ServerVersion": 0, 
  "_ClientVersion$": 0, 
  "Ingredients": [
  	{
  		"name": "Juicy Orange",
  		"amount": 3
  	},
  	{
  		"name": "Juicy Apple",
  		"amount": 3
  	},
  	{
  		"name": "Banana",
  		"amount": 1
  	}
  	],
  "TotalCost": 100,
  ...
}

and remove Oranges

[{"op": "test", "path": "/_ServerVersion", "value": 0},
{"op": "replace", "path": "/_ClientVersion$", "value": 1},
{"op": "replace", "path": "/Ingredients/0/amount$", "value": "0"}] //instead of Delete = null

Server updates the DB and responds with

[{"op": "test", "path": "/_ClientVersion", "value": 1},
{"op": "replace", "path": "/_ServerVersion$", "value": 1},
{"op": "replace", "path": "/TotalCost", "value": 40}]

Alice is back online, and server finally gets

[{"op": "test", "path": "/_ServerVersion", "value": 1},
{"op": "replace", "path": "/_ClientVersion$", "value": 2},
{"op": "replace", "path": "/Ingredients/0/amount$", "value": "0"}] //instead of Delete = null

JSON we are talking about is Alice View-Model state, stored in session, not the Database state

Therefore _ServerVersion and _clientVersion matches for both peers (Alice and Server).

Server (App code) matches /Ingredients/0 with real Object to find out that there is no such thing in DB, so it performs app specific logic as for attempt to remove non-existing entry.

It could either

  1. do nothing, but just update TotalCost that changed in meantime.
[{"op": "test", "path": "/_ClientVersion", "value": 2},
{"op": "replace", "path": "/_ServerVersion$", "value": 2},
{"op": "replace", "path": "/TotalCost", "value": 40}]
  1. return error,
  2. or just a message to be shown to the user:
[{"op": "test", "path": "/_ClientVersion", "value": 2},
{"op": "replace", "path": "/_ServerVersion$", "value": 2},
{"op": "replace", "path": "/Notification$", "value": "There is no such thing in Receipt (or if app's server-side code is sophisticated enough, tp keeps track of modifications in recipes: Bob have already removed it) "},
{"op": "replace", "path": "/TotalCost", "value": 40}]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment