concurrent case question: object creation

51 views
Skip to first unread message

Dmitry

unread,
Apr 22, 2014, 3:24:13 PM4/22/14
to sha...@googlegroups.com
Hi,

I'm wondering how the following case can be correctly solved in terms of ShareJS:

  1. let's say 2 users (A and B) start with the same empty document. 
  2. user A sees that document is empty and need to start with a new object with a known name 'foo', so he do an object insert command oi: { x: 0 }, then increments it by 1. Submitted operation looks like [{p:[], oi: {x:0}}, {p:['x'], na: 1}]
  3. user B at the same time do the same. Operation looks the same way [{p:[], oi: {x:0}}, {p:['x'], na: 1}]
  4. Both operations land to the server in order opA, opB. => opB gets transformed by opA
expected result: application intention is to register both increments from both users.
actual result: opB' becomes [ { p: [], od: { x: 1 }, oi: { a: 0 } },  { p: [ 'x' ], na: 1 } ] which actually removes previous result and restarts from scratch.

The question may arise in case both clients start offline and want to start working with a document with known ID. 



----------
As a separate question to @Joseph:
I've noticed you're doing operation append to the last component only, thus with lots of edits and very slow sync things can go out of control pretty quickly. Have you thought about reordering and merging components if their paths are not intersecting? Is there any consequences I might not see, or can I probably start thinking about implementation? 

Joseph Gentle

unread,
Apr 23, 2014, 9:40:26 PM4/23/14
to sha...@googlegroups.com
On Tue, Apr 22, 2014 at 12:24 PM, Dmitry <uvar...@gmail.com> wrote:
> Hi,
>
> I'm wondering how the following case can be correctly solved in terms of
> ShareJS:
>
> let's say 2 users (A and B) start with the same empty document.
> user A sees that document is empty and need to start with a new object with
> a known name 'foo', so he do an object insert command oi: { x: 0 }, then
> increments it by 1. Submitted operation looks like [{p:[], oi: {x:0}},
> {p:['x'], na: 1}]
> user B at the same time do the same. Operation looks the same way [{p:[],
> oi: {x:0}}, {p:['x'], na: 1}]
> Both operations land to the server in order opA, opB. => opB gets
> transformed by opA
>
> expected result: application intention is to register both increments from
> both users.
> actual result: opB' becomes [ { p: [], od: { x: 1 }, oi: { a: 0 } }, { p: [
> 'x' ], na: 1 } ] which actually removes previous result and restarts from
> scratch.
>
> The question may arise in case both clients start offline and want to start
> working with a document with known ID.

Yes, this is a problem. I don't have a good answer unfortunately.
We've talked about adding a setNull variant of oi: which would solve
this problem. When the operation is applied, if the property already
exists the operation component has no effect.

I haven't added it to the JSON type because I've been busy. If you
want to try to add it yourself to fix this problem, I'd welcome the
patch.

> ----------
> As a separate question to @Joseph:
> I've noticed you're doing operation append to the last component only, thus
> with lots of edits and very slow sync things can go out of control pretty
> quickly. Have you thought about reordering and merging components if their
> paths are not intersecting? Is there any consequences I might not see, or
> can I probably start thinking about implementation?

Does it? I thought about it, but haven't had this problem show up in
real life so I'm not too worried about it.

Merging operation components elsewhere in the op would be fine, except:
- json.compose would have O(n) time instead of O(1) time (n= number of
components). If your ops aren't big anyway, it won't matter - but if
your ops were small you wouldn't need to combine the components
together. This should only happen on the client side though, so maybe
it doesn't matter.
- It needs to recognise when paths earlier in the op component change
as a result of things later in the op. For example:
Op: [{p:[10], li:"x"}, {p:[1], li:"y"}] + [{p:[11,1], si:"z"}] needs
to become [{p:[10], li:"xz"}, {p:[1], li:"y"}]
... ie, the item at p:[10] will have index 11 when the string insert
was applied (because an item was inserted before it in the list).


> --
> You received this message because you are subscribed to the Google Groups
> "ShareJS" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to sharejs+u...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Dmitry

unread,
Apr 24, 2014, 4:35:37 AM4/24/14
to sha...@googlegroups.com


On Thursday, April 24, 2014 5:40:26 AM UTC+4, Joseph Gentle wrote:


Yes, this is a problem. I don't have a good answer unfortunately.
We've talked about adding a setNull variant of oi: which would solve
this problem. When the operation is applied, if the property already
exists the operation component has no effect.

I've also thought about this solution, just wasn't sure if it's a clever "hack". Since it's essential for my project, I'm on it.


 

Does it? I thought about it, but haven't had this problem show up in
real life so I'm not too worried about it.

Merging operation components elsewhere in the op would be fine, except:
- json.compose would have O(n) time instead of O(1) time (n= number of
components). If your ops aren't big anyway, it won't matter - but if
your ops were small you wouldn't need to combine the components
together. This should only happen on the client side though, so maybe
it doesn't matter.
- It needs to recognise when paths earlier in the op component change
as a result of things later in the op. For example:
Op: [{p:[10], li:"x"}, {p:[1], li:"y"}] + [{p:[11,1], si:"z"}] needs
to become [{p:[10], li:"xz"}, {p:[1], li:"y"}]
... ie, the item at p:[10] will have index 11 when the string insert
was applied (because an item was inserted before it in the list).

Hm, guess you're right, I was kinda trapped with my problem where I could have a lots of benefits (I have a several root subdocuments that can be modified in parallel), so I guess it's better for me to implement it above OT logic just for my case.
 

Dmitry

unread,
Apr 25, 2014, 12:25:26 PM4/25/14
to sha...@googlegroups.com
Here it goes: https://github.com/share/ottypes/pull/15
I'd be happy if you'll have time to review this PR.
Reply all
Reply to author
Forward
0 new messages