implement new method awaitedOps in favor of awaitedDeletes/awaitedInserts. This will fix some bugs when the type gets out of sync with the state of yjs
This commit is contained in:
parent
996566419c
commit
5b835563c8
2
dist
2
dist
@ -1 +1 @@
|
|||||||
Subproject commit 3f60690880908e7f9a92642e37253f1ac84db3f6
|
Subproject commit e2f93af86e9dd207cb57d313c6ac305cd69e34d1
|
@ -413,7 +413,7 @@ module.exports = function (Y /* :any */) {
|
|||||||
|
|
||||||
// notify parent, if it was instanciated as a custom type
|
// notify parent, if it was instanciated as a custom type
|
||||||
if (t != null) {
|
if (t != null) {
|
||||||
let o = Y.utils.copyObject(op)
|
let o = Y.utils.copyOperation(op)
|
||||||
yield* t._changed(transaction, o)
|
yield* t._changed(transaction, o)
|
||||||
}
|
}
|
||||||
if (!op.deleted) {
|
if (!op.deleted) {
|
||||||
|
@ -86,10 +86,6 @@ function getRandomString () {
|
|||||||
g.getRandomString = getRandomString
|
g.getRandomString = getRandomString
|
||||||
|
|
||||||
function * applyTransactions (relAmount, numberOfTransactions, objects, users, transactions, noReconnect) {
|
function * applyTransactions (relAmount, numberOfTransactions, objects, users, transactions, noReconnect) {
|
||||||
function randomTransaction (root) {
|
|
||||||
var f = getRandom(transactions)
|
|
||||||
f(root)
|
|
||||||
}
|
|
||||||
for (var i = 0; i < numberOfTransactions * relAmount + 1; i++) {
|
for (var i = 0; i < numberOfTransactions * relAmount + 1; i++) {
|
||||||
var r = Math.random()
|
var r = Math.random()
|
||||||
if (r >= 0.5) {
|
if (r >= 0.5) {
|
||||||
@ -97,44 +93,76 @@ function * applyTransactions (relAmount, numberOfTransactions, objects, users, t
|
|||||||
yield Y.utils.globalRoom.flushOne() // flushes for some user.. (not necessarily 0)
|
yield Y.utils.globalRoom.flushOne() // flushes for some user.. (not necessarily 0)
|
||||||
} else if (noReconnect || r >= 0.05) {
|
} else if (noReconnect || r >= 0.05) {
|
||||||
// 45% chance to create operation
|
// 45% chance to create operation
|
||||||
randomTransaction(getRandom(objects))
|
var done = getRandom(transactions)(getRandom(objects))
|
||||||
|
if (done != null) {
|
||||||
|
yield done
|
||||||
|
} else {
|
||||||
|
yield wait()
|
||||||
|
}
|
||||||
yield Y.utils.globalRoom.whenTransactionsFinished()
|
yield Y.utils.globalRoom.whenTransactionsFinished()
|
||||||
} else {
|
} else {
|
||||||
// 5% chance to disconnect/reconnect
|
// 5% chance to disconnect/reconnect
|
||||||
var u = getRandom(users)
|
var u = getRandom(users)
|
||||||
|
yield Promise.all(objects.map(fixAwaitingInType))
|
||||||
if (u.connector.isDisconnected()) {
|
if (u.connector.isDisconnected()) {
|
||||||
yield u.reconnect()
|
yield u.reconnect()
|
||||||
} else {
|
} else {
|
||||||
yield u.disconnect()
|
yield u.disconnect()
|
||||||
}
|
}
|
||||||
|
yield Promise.all(objects.map(fixAwaitingInType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fixAwaitingInType (type) {
|
||||||
|
return new Promise(function (resolve) {
|
||||||
|
type.os.whenTransactionsFinished().then(function () {
|
||||||
|
// _debuggingAwaiting artificially increases the awaiting property. We need to make sure that we only do that once / reverse the effect once
|
||||||
|
type.os.requestTransaction(function * () {
|
||||||
|
if (type.eventHandler.awaiting > 0 && type.eventHandler._debuggingAwaiting === true) {
|
||||||
|
type.eventHandler._debuggingAwaiting = false
|
||||||
|
yield* type.eventHandler.awaitedOps(this, 0)
|
||||||
|
}
|
||||||
|
wait(50).then(type.os.whenTransactionsFinished()).then(wait(50)).then(resolve)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
g.fixAwaitingInType = fixAwaitingInType
|
||||||
|
|
||||||
g.applyRandomTransactionsNoGCNoDisconnect = async(function * applyRandomTransactions (users, objects, transactions, numberOfTransactions) {
|
g.applyRandomTransactionsNoGCNoDisconnect = async(function * applyRandomTransactions (users, objects, transactions, numberOfTransactions) {
|
||||||
yield* applyTransactions(1, numberOfTransactions, objects, users, transactions, true)
|
yield* applyTransactions(1, numberOfTransactions, objects, users, transactions, true)
|
||||||
yield Y.utils.globalRoom.flushAll()
|
yield Y.utils.globalRoom.flushAll()
|
||||||
|
yield Promise.all(objects.map(fixAwaitingInType))
|
||||||
})
|
})
|
||||||
|
|
||||||
g.applyRandomTransactionsAllRejoinNoGC = async(function * applyRandomTransactions (users, objects, transactions, numberOfTransactions) {
|
g.applyRandomTransactionsAllRejoinNoGC = async(function * applyRandomTransactions (users, objects, transactions, numberOfTransactions) {
|
||||||
yield* applyTransactions(1, numberOfTransactions, objects, users, transactions)
|
yield* applyTransactions(1, numberOfTransactions, objects, users, transactions)
|
||||||
|
yield Promise.all(objects.map(fixAwaitingInType))
|
||||||
yield Y.utils.globalRoom.flushAll()
|
yield Y.utils.globalRoom.flushAll()
|
||||||
|
yield Promise.all(objects.map(fixAwaitingInType))
|
||||||
for (var u in users) {
|
for (var u in users) {
|
||||||
|
yield Promise.all(objects.map(fixAwaitingInType))
|
||||||
yield users[u].reconnect()
|
yield users[u].reconnect()
|
||||||
|
yield Promise.all(objects.map(fixAwaitingInType))
|
||||||
}
|
}
|
||||||
|
yield Promise.all(objects.map(fixAwaitingInType))
|
||||||
yield Y.utils.globalRoom.flushAll()
|
yield Y.utils.globalRoom.flushAll()
|
||||||
|
yield Promise.all(objects.map(fixAwaitingInType))
|
||||||
yield g.garbageCollectAllUsers(users)
|
yield g.garbageCollectAllUsers(users)
|
||||||
})
|
})
|
||||||
|
|
||||||
g.applyRandomTransactionsWithGC = async(function * applyRandomTransactions (users, objects, transactions, numberOfTransactions) {
|
g.applyRandomTransactionsWithGC = async(function * applyRandomTransactions (users, objects, transactions, numberOfTransactions) {
|
||||||
yield* applyTransactions(1, numberOfTransactions, objects, users.slice(1), transactions)
|
yield* applyTransactions(1, numberOfTransactions, objects, users.slice(1), transactions)
|
||||||
yield Y.utils.globalRoom.flushAll()
|
yield Y.utils.globalRoom.flushAll()
|
||||||
|
yield Promise.all(objects.map(fixAwaitingInType))
|
||||||
for (var u in users) {
|
for (var u in users) {
|
||||||
// TODO: here, we enforce that two users never sync at the same time with u[0]
|
// TODO: here, we enforce that two users never sync at the same time with u[0]
|
||||||
// enforce that in the connector itself!
|
// enforce that in the connector itself!
|
||||||
yield users[u].reconnect()
|
yield users[u].reconnect()
|
||||||
}
|
}
|
||||||
yield Y.utils.globalRoom.flushAll()
|
yield Y.utils.globalRoom.flushAll()
|
||||||
|
yield Promise.all(objects.map(fixAwaitingInType))
|
||||||
yield g.garbageCollectAllUsers(users)
|
yield g.garbageCollectAllUsers(users)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -222,7 +250,6 @@ g.compareAllUsers = async(function * compareAllUsers (users) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// TODO: make requestTransaction return a promise..
|
|
||||||
u.db.requestTransaction(function * () {
|
u.db.requestTransaction(function * () {
|
||||||
yield* t2.call(this)
|
yield* t2.call(this)
|
||||||
var db2 = []
|
var db2 = []
|
||||||
|
@ -1068,7 +1068,7 @@ module.exports = function (Y/* :any */) {
|
|||||||
/* this is what we used before.. use this as a reference..
|
/* 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) -- use copyoperation instead now!
|
||||||
var o = op
|
var o = op
|
||||||
var ids = [op.id]
|
var ids = [op.id]
|
||||||
// search for the new op.right
|
// search for the new op.right
|
||||||
|
93
src/Utils.js
93
src/Utils.js
@ -102,9 +102,41 @@ module.exports = function (Y /* : any*/) {
|
|||||||
prematurely called operations are executed
|
prematurely called operations are executed
|
||||||
*/
|
*/
|
||||||
awaitAndPrematurelyCall (ops) {
|
awaitAndPrematurelyCall (ops) {
|
||||||
this.awaiting += ops.length
|
this.awaiting++
|
||||||
ops.forEach(this.onevent)
|
ops.map(Y.utils.copyOperation).forEach(this.onevent)
|
||||||
}
|
}
|
||||||
|
* awaitedOps (transaction, n) {
|
||||||
|
// remove awaited ops
|
||||||
|
this.waiting.splice(this.waiting.length - n)
|
||||||
|
// update all waiting ops
|
||||||
|
for (let i = 0; i < this.waiting.length; i++) {
|
||||||
|
var o = this.waiting[i]
|
||||||
|
if (o.struct === 'Insert') {
|
||||||
|
var _o = yield* transaction.getInsertion(o.id)
|
||||||
|
if (!Y.utils.compareIds(_o.id, o.id)) {
|
||||||
|
// o got extended
|
||||||
|
o.left = [o.id[0], o.id[1] - 1]
|
||||||
|
} else if (_o.left == null) {
|
||||||
|
o.left = null
|
||||||
|
} else {
|
||||||
|
// find next undeleted op
|
||||||
|
var left = yield* transaction.getInsertion(_o.left)
|
||||||
|
while (left.deleted != null) {
|
||||||
|
if (left.left != null) {
|
||||||
|
left = yield* transaction.getInsertion(left.left)
|
||||||
|
} else {
|
||||||
|
left = null
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
o.left = left != null ? Y.utils.getLastId(left) : null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._tryCallEvents()
|
||||||
|
}
|
||||||
|
// TODO: Remove awaitedInserts and awaitedDeletes in favor of awaitedOps, as they are deprecated and do not always work
|
||||||
|
// Do this in one of the coming releases that are breaking anyway
|
||||||
/*
|
/*
|
||||||
Call this when you successfully awaited the execution of n Insert operations
|
Call this when you successfully awaited the execution of n Insert operations
|
||||||
*/
|
*/
|
||||||
@ -162,12 +194,42 @@ module.exports = function (Y /* : any*/) {
|
|||||||
/* (private)
|
/* (private)
|
||||||
Try to execute the events for the waiting operations
|
Try to execute the events for the waiting operations
|
||||||
*/
|
*/
|
||||||
_tryCallEvents (n) {
|
_tryCallEvents () {
|
||||||
this.awaiting -= n
|
function notSoSmartSort (array) {
|
||||||
|
var result = []
|
||||||
|
while (array.length > 0) {
|
||||||
|
for (var i = 0; i < array.length; i++) {
|
||||||
|
var independent = true
|
||||||
|
for (var j = 0; j < array.length; j++) {
|
||||||
|
if (Y.utils.matchesId(array[j], array[i].left)) {
|
||||||
|
// array[i] depends on array[j]
|
||||||
|
independent = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (independent) {
|
||||||
|
result.push(array.splice(i, 1)[0])
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
if (this.awaiting > 0) this.awaiting--
|
||||||
if (this.awaiting === 0 && this.waiting.length > 0) {
|
if (this.awaiting === 0 && this.waiting.length > 0) {
|
||||||
var ops = this.waiting
|
var ins = []
|
||||||
|
var dels = []
|
||||||
|
this.waiting.forEach(function (o) {
|
||||||
|
if (o.struct === 'Delete') {
|
||||||
|
dels.push(o)
|
||||||
|
} else {
|
||||||
|
ins.push(o)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
ins = notSoSmartSort(ins)
|
||||||
|
ins.forEach(this.onevent)
|
||||||
|
dels.forEach(this.onevent)
|
||||||
this.waiting = []
|
this.waiting = []
|
||||||
ops.forEach(this.onevent)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -236,6 +298,20 @@ module.exports = function (Y /* : any*/) {
|
|||||||
}
|
}
|
||||||
Y.utils.copyObject = copyObject
|
Y.utils.copyObject = copyObject
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copy an operation, so that it can be manipulated.
|
||||||
|
Note: You must not change subproperties (except o.content)!
|
||||||
|
*/
|
||||||
|
function copyOperation (o) {
|
||||||
|
o = copyObject(o)
|
||||||
|
if (o.content != null) {
|
||||||
|
o.content = o.content.map(function (c) { return c })
|
||||||
|
}
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
Y.utils.copyOperation = copyOperation
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Defines a smaller relation on Id's
|
Defines a smaller relation on Id's
|
||||||
*/
|
*/
|
||||||
@ -244,6 +320,11 @@ module.exports = function (Y /* : any*/) {
|
|||||||
}
|
}
|
||||||
Y.utils.smaller = smaller
|
Y.utils.smaller = smaller
|
||||||
|
|
||||||
|
function inDeletionRange (del, ins) {
|
||||||
|
return del.target[0] === ins[0] && del.target[1] <= ins[1] && ins[1] < del.target[1] + (del.length || 1)
|
||||||
|
}
|
||||||
|
Y.utils.inDeletionRange = inDeletionRange
|
||||||
|
|
||||||
function compareIds (id1, id2) {
|
function compareIds (id1, id2) {
|
||||||
if (id1 == null || id2 == null) {
|
if (id1 == null || id2 == null) {
|
||||||
return id1 === id2
|
return id1 === id2
|
||||||
|
Loading…
x
Reference in New Issue
Block a user