Implemente a new sync procedure that is optimal with respect to big O notation (there is probably a way to reduce it by a factor of 1/2)
This commit is contained in:
parent
a33d0bf7bc
commit
51a834d6c9
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "yjs",
|
"name": "yjs",
|
||||||
"version": "0.7.7",
|
"version": "0.8.0",
|
||||||
"description": "A framework for real-time p2p shared editing on arbitrary complex data types",
|
"description": "A framework for real-time p2p shared editing on arbitrary complex data types",
|
||||||
"main": "./src/y.js",
|
"main": "./src/y.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -626,34 +626,8 @@ module.exports = function (Y/* :any */) {
|
|||||||
})
|
})
|
||||||
return ss
|
return ss
|
||||||
}
|
}
|
||||||
* getOperations (startSS) {
|
|
||||||
// TODO: use bounds here!
|
|
||||||
if (startSS == null) {
|
|
||||||
startSS = {}
|
|
||||||
}
|
|
||||||
var ops = []
|
|
||||||
|
|
||||||
var endSV = yield* this.getStateVector()
|
|
||||||
for (var endState of endSV) {
|
|
||||||
var user = endState.user
|
|
||||||
if (user === '_') {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var startPos = startSS[user] || 0
|
|
||||||
|
|
||||||
yield* this.os.iterate(this, [user, startPos], [user, Number.MAX_VALUE], function * (op) {
|
|
||||||
ops.push(op)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
var res = []
|
|
||||||
for (var op of ops) {
|
|
||||||
var o = yield* this.makeOperationReady(startSS, op)
|
|
||||||
res.push(o)
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
Here, we make op executable for the receiving user.
|
Here, we make all missing operations executable for the receiving user.
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
startSS: denotes to the SV that the remote user sent
|
startSS: denotes to the SV that the remote user sent
|
||||||
@ -688,7 +662,92 @@ module.exports = function (Y/* :any */) {
|
|||||||
(startSS or currSS.. ?)
|
(startSS or currSS.. ?)
|
||||||
-> Could be necessary when I turn GC again.
|
-> Could be necessary when I turn GC again.
|
||||||
-> Is a bad(ish) idea because it requires more computation
|
-> Is a bad(ish) idea because it requires more computation
|
||||||
|
|
||||||
|
What we do:
|
||||||
|
* Iterate over all missing operations.
|
||||||
|
* When there is an operation, where the right op is known, send this op all missing ops to the left to the user
|
||||||
|
* I explained above what we have to do with each operation. Here is how we do it efficiently:
|
||||||
|
1. Go to the left until you find either op.origin, or a known operation (let o denote current operation in the iteration)
|
||||||
|
2. Found a known operation -> set op.left = o, and send it to the user. stop
|
||||||
|
3. Found o = op.origin -> set op.left = op.origin, and send it to the user. start again from 1. (set op = o)
|
||||||
|
4. Found some o -> set o.right = op, o.left = o.origin, send it to the user, continue
|
||||||
*/
|
*/
|
||||||
|
* getOperations (startSS) {
|
||||||
|
// TODO: use bounds here!
|
||||||
|
if (startSS == null) {
|
||||||
|
startSS = {}
|
||||||
|
}
|
||||||
|
var send = []
|
||||||
|
|
||||||
|
var endSV = yield* this.getStateVector()
|
||||||
|
for (var endState of endSV) {
|
||||||
|
var user = endState.user
|
||||||
|
if (user === '_') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var startPos = startSS[user] || 0
|
||||||
|
|
||||||
|
yield* this.os.iterate(this, [user, startPos], [user, Number.MAX_VALUE], function * (op) {
|
||||||
|
op = Y.Struct[op.struct].encode(op)
|
||||||
|
if (op.struct !== 'Insert') {
|
||||||
|
send.push(op)
|
||||||
|
} else if (op.right == null || op.right[1] < (startSS[op.right[0]] || 0)) {
|
||||||
|
// case 1. op.right is known
|
||||||
|
var o = op
|
||||||
|
// Remember: ?
|
||||||
|
// -> set op.right
|
||||||
|
// 1. to the first operation that is known (according to startSS)
|
||||||
|
// 2. or to the first operation that has an origin that is not to the
|
||||||
|
// right of op.
|
||||||
|
// For this we maintain a list of ops which origins are not found yet.
|
||||||
|
var missing_origins = [op]
|
||||||
|
var newright = op.right
|
||||||
|
while (true) {
|
||||||
|
if (o.left == null) {
|
||||||
|
op.left = null
|
||||||
|
send.push(op)
|
||||||
|
if (!Y.utils.compareIds(o.id, op.id)) {
|
||||||
|
o = Y.Struct[op.struct].encode(o)
|
||||||
|
o.right = missing_origins[missing_origins.length - 1].id
|
||||||
|
send.push(o)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
o = yield* this.getOperation(o.left)
|
||||||
|
// we set another o, check if we can reduce $missing_origins
|
||||||
|
while (missing_origins.length > 0 && Y.utils.compareIds(missing_origins[missing_origins.length - 1].origin, o.id)) {
|
||||||
|
missing_origins.pop()
|
||||||
|
}
|
||||||
|
if (o.id[1] < (startSS[o.id[0]] || 0)) {
|
||||||
|
// case 2. o is known
|
||||||
|
op.left = o.id
|
||||||
|
send.push(op)
|
||||||
|
break
|
||||||
|
} else if (Y.utils.compareIds(o.id, op.origin)) {
|
||||||
|
// case 3. o is op.origin
|
||||||
|
op.left = op.origin
|
||||||
|
send.push(op)
|
||||||
|
op = Y.Struct[op.struct].encode(o)
|
||||||
|
op.right = newright
|
||||||
|
if (missing_origins.length > 0) {
|
||||||
|
console.log('This should not happen .. :( please report this')
|
||||||
|
}
|
||||||
|
missing_origins = [op]
|
||||||
|
} else {
|
||||||
|
// case 4. send o, continue to find op.origin
|
||||||
|
var s = Y.Struct[op.struct].encode(o)
|
||||||
|
s.right = missing_origins[missing_origins.length - 1].id
|
||||||
|
s.left = s.origin
|
||||||
|
send.push(s)
|
||||||
|
missing_origins.push(o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return send
|
||||||
|
}
|
||||||
|
/* this is what we used before.. use this as a reference..
|
||||||
* makeOperationReady (startSS, op) {
|
* makeOperationReady (startSS, op) {
|
||||||
op = Y.Struct[op.struct].encode(op)
|
op = Y.Struct[op.struct].encode(op)
|
||||||
op = Y.utils.copyObject(op)
|
op = Y.utils.copyObject(op)
|
||||||
@ -712,6 +771,7 @@ module.exports = function (Y/* :any */) {
|
|||||||
op.left = op.origin
|
op.left = op.origin
|
||||||
return op
|
return op
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
Y.Transaction = TransactionInterface
|
Y.Transaction = TransactionInterface
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user