Further reduced number of db requests
This commit is contained in:
parent
6b1cf18822
commit
31d2a231e3
@ -54,8 +54,9 @@ require('./gulpfile.helper.js')(gulp, {
|
||||
moduleName: 'yjs',
|
||||
includeRuntime: true,
|
||||
specs: [
|
||||
'./src/Database.spec.js',
|
||||
'../y-array/src/Array.spec.js',
|
||||
'./src/Database.spec.js'
|
||||
'../y-map/src/Map.spec.js'
|
||||
]
|
||||
})
|
||||
|
||||
|
@ -75,7 +75,7 @@ module.exports = function (Y /* :any */) {
|
||||
}
|
||||
this.gc1 = [] // first stage
|
||||
this.gc2 = [] // second stage -> after that, remove the op
|
||||
this.gcTimeout = opts.gcTimeout || 5000
|
||||
this.gcTimeout = opts.gcTimeout || 50000
|
||||
function garbageCollect () {
|
||||
return os.whenTransactionsFinished().then(function () {
|
||||
if (os.gc1.length > 0 || os.gc2.length > 0) {
|
||||
|
@ -54,10 +54,10 @@ for (let database of databases) {
|
||||
yield* this.markDeleted(['166', 2])
|
||||
yield* this.markDeleted(['166', 0])
|
||||
yield* this.markDeleted(['166', 2])
|
||||
yield* this.markGarbageCollected(['166', 2])
|
||||
yield* this.markGarbageCollected(['166', 2], 1)
|
||||
yield* this.markDeleted(['166', 1])
|
||||
yield* this.markDeleted(['166', 3])
|
||||
yield* this.markGarbageCollected(['166', 3])
|
||||
yield* this.markGarbageCollected(['166', 3], 1)
|
||||
yield* this.markDeleted(['166', 0])
|
||||
expect(yield* this.getDeleteSet()).toEqual({'166': [[0, 2, false], [2, 2, true]]})
|
||||
done()
|
||||
@ -68,9 +68,9 @@ for (let database of databases) {
|
||||
yield* this.markDeleted(['293', 0])
|
||||
yield* this.markDeleted(['291', 2])
|
||||
yield* this.markDeleted(['291', 2])
|
||||
yield* this.markGarbageCollected(['293', 0])
|
||||
yield* this.markGarbageCollected(['293', 0], 1)
|
||||
yield* this.markDeleted(['293', 1])
|
||||
yield* this.markGarbageCollected(['291', 2])
|
||||
yield* this.markGarbageCollected(['291', 2], 1)
|
||||
expect(yield* this.getDeleteSet()).toEqual({'291': [[2, 1, true]], '293': [[0, 1, true], [1, 1, false]]})
|
||||
done()
|
||||
})
|
||||
@ -81,15 +81,15 @@ for (let database of databases) {
|
||||
yield* this.markDeleted(['581', 1])
|
||||
yield* this.markDeleted(['580', 0])
|
||||
yield* this.markDeleted(['580', 0])
|
||||
yield* this.markGarbageCollected(['581', 0])
|
||||
yield* this.markGarbageCollected(['581', 0], 1)
|
||||
yield* this.markDeleted(['581', 2])
|
||||
yield* this.markDeleted(['580', 1])
|
||||
yield* this.markDeleted(['580', 2])
|
||||
yield* this.markDeleted(['580', 1])
|
||||
yield* this.markDeleted(['580', 2])
|
||||
yield* this.markGarbageCollected(['581', 2])
|
||||
yield* this.markGarbageCollected(['581', 1])
|
||||
yield* this.markGarbageCollected(['580', 1])
|
||||
yield* this.markGarbageCollected(['581', 2], 1)
|
||||
yield* this.markGarbageCollected(['581', 1], 1)
|
||||
yield* this.markGarbageCollected(['580', 1], 1)
|
||||
expect(yield* this.getDeleteSet()).toEqual({'580': [[0, 1, false], [1, 1, true], [2, 1, false]], '581': [[0, 3, true]]})
|
||||
done()
|
||||
})
|
||||
@ -100,7 +100,7 @@ for (let database of databases) {
|
||||
yield* this.markDeleted(['543', 2])
|
||||
yield* this.markDeleted(['544', 0])
|
||||
yield* this.markDeleted(['543', 2])
|
||||
yield* this.markGarbageCollected(['544', 0])
|
||||
yield* this.markGarbageCollected(['544', 0], 1)
|
||||
yield* this.markDeleted(['545', 1])
|
||||
yield* this.markDeleted(['543', 4])
|
||||
yield* this.markDeleted(['543', 3])
|
||||
@ -108,10 +108,10 @@ for (let database of databases) {
|
||||
yield* this.markDeleted(['544', 2])
|
||||
yield* this.markDeleted(['544', 1])
|
||||
yield* this.markDeleted(['544', 2])
|
||||
yield* this.markGarbageCollected(['543', 2])
|
||||
yield* this.markGarbageCollected(['543', 4])
|
||||
yield* this.markGarbageCollected(['544', 2])
|
||||
yield* this.markGarbageCollected(['543', 3])
|
||||
yield* this.markGarbageCollected(['543', 2], 1)
|
||||
yield* this.markGarbageCollected(['543', 4], 1)
|
||||
yield* this.markGarbageCollected(['544', 2], 1)
|
||||
yield* this.markGarbageCollected(['543', 3], 1)
|
||||
expect(yield* this.getDeleteSet()).toEqual({'543': [[2, 3, true]], '544': [[0, 1, true], [1, 1, false], [2, 1, true]], '545': [[1, 1, false]]})
|
||||
done()
|
||||
})
|
||||
@ -142,14 +142,14 @@ for (let database of databases) {
|
||||
yield* this.markDeleted(['11', 1])
|
||||
yield* this.markDeleted(['9', 4])
|
||||
yield* this.markDeleted(['10', 0])
|
||||
yield* this.markGarbageCollected(['11', 2])
|
||||
yield* this.markGarbageCollected(['11', 2], 1)
|
||||
yield* this.markDeleted(['11', 2])
|
||||
yield* this.markGarbageCollected(['11', 3])
|
||||
yield* this.markGarbageCollected(['11', 3], 1)
|
||||
yield* this.markDeleted(['11', 3])
|
||||
yield* this.markDeleted(['11', 3])
|
||||
yield* this.markDeleted(['9', 4])
|
||||
yield* this.markDeleted(['10', 0])
|
||||
yield* this.markGarbageCollected(['11', 1])
|
||||
yield* this.markGarbageCollected(['11', 1], 1)
|
||||
yield* this.markDeleted(['11', 1])
|
||||
expect(yield* this.getDeleteSet()).toEqual({'9': [[2, 1, false], [4, 1, false]], '10': [[0, 1, false]], '11': [[1, 3, true], [4, 1, false]]})
|
||||
done()
|
||||
|
@ -222,86 +222,125 @@ module.exports = function (Y/* :any */) {
|
||||
/*
|
||||
Mark an operation as deleted&gc'd
|
||||
*/
|
||||
* markGarbageCollected (id) {
|
||||
* markGarbageCollected (id, len) {
|
||||
// this.mem.push(["gc", id]);
|
||||
var n = yield* this.markDeleted(id)
|
||||
if (!n.gc) {
|
||||
if (n.id[1] < id[1]) {
|
||||
// un-extend left
|
||||
var newlen = n.len - (id[1] - n.id[1])
|
||||
n.len -= newlen
|
||||
yield* this.ds.put(n)
|
||||
n = {id: id, len: newlen, gc: false}
|
||||
yield* this.ds.put(n)
|
||||
}
|
||||
// get prev&next before adding a new operation
|
||||
var prev = yield* this.ds.findPrev(id)
|
||||
var next = yield* this.ds.findNext(id)
|
||||
|
||||
if (id[1] < n.id[1] + n.len - 1) {
|
||||
// un-extend right
|
||||
yield* this.ds.put({id: [id[0], id[1] + 1], len: n.len - 1, gc: false})
|
||||
n.len = 1
|
||||
}
|
||||
// set gc'd
|
||||
n.gc = true
|
||||
// can extend left?
|
||||
if (
|
||||
prev != null &&
|
||||
prev.gc &&
|
||||
Y.utils.compareIds([prev.id[0], prev.id[1] + prev.len], n.id)
|
||||
) {
|
||||
prev.len += n.len
|
||||
yield* this.ds.delete(n.id)
|
||||
n = prev
|
||||
// ds.put n here?
|
||||
}
|
||||
// can extend right?
|
||||
if (
|
||||
next != null &&
|
||||
next.gc &&
|
||||
Y.utils.compareIds([n.id[0], n.id[1] + n.len], next.id)
|
||||
) {
|
||||
n.len += next.len
|
||||
yield* this.ds.delete(next.id)
|
||||
}
|
||||
var n = yield* this.markDeleted(id, len)
|
||||
if (n.id[1] < id[1] && !n.gc) {
|
||||
// un-extend left
|
||||
var newlen = n.len - (id[1] - n.id[1])
|
||||
n.len -= newlen
|
||||
yield* this.ds.put(n)
|
||||
n = {id: id, len: newlen, gc: false}
|
||||
yield* this.ds.put(n)
|
||||
}
|
||||
// get prev&next before adding a new operation
|
||||
var prev = yield* this.ds.findPrev(id)
|
||||
var next = yield* this.ds.findNext(id)
|
||||
|
||||
if (id[1] < n.id[1] + n.len - len && !n.gc) {
|
||||
// un-extend right
|
||||
yield* this.ds.put({id: [id[0], id[1] + 1], len: n.len - 1, gc: false})
|
||||
n.len = 1
|
||||
}
|
||||
// set gc'd
|
||||
n.gc = true
|
||||
// can extend left?
|
||||
if (
|
||||
prev != null &&
|
||||
prev.gc &&
|
||||
Y.utils.compareIds([prev.id[0], prev.id[1] + prev.len], n.id)
|
||||
) {
|
||||
prev.len += n.len
|
||||
yield* this.ds.delete(n.id)
|
||||
n = prev
|
||||
// ds.put n here?
|
||||
}
|
||||
// can extend right?
|
||||
if (
|
||||
next != null &&
|
||||
next.gc &&
|
||||
Y.utils.compareIds([n.id[0], n.id[1] + n.len], next.id)
|
||||
) {
|
||||
n.len += next.len
|
||||
yield* this.ds.delete(next.id)
|
||||
}
|
||||
yield* this.ds.put(n)
|
||||
}
|
||||
/*
|
||||
Mark an operation as deleted.
|
||||
|
||||
returns the delete node
|
||||
*/
|
||||
* markDeleted (id) {
|
||||
* markDeleted (id, length) {
|
||||
if (length == null) {
|
||||
length = 1
|
||||
// debugger // TODO!!
|
||||
}
|
||||
// this.mem.push(["del", id]);
|
||||
var n = yield* this.ds.findWithUpperBound(id)
|
||||
if (n != null && n.id[0] === id[0]) {
|
||||
if (n.id[1] <= id[1] && id[1] < n.id[1] + n.len) {
|
||||
// already deleted
|
||||
return n
|
||||
} else if (n.id[1] + n.len === id[1] && !n.gc) {
|
||||
// can extend existing deletion
|
||||
n.len++
|
||||
if (n.id[1] <= id[1] && id[1] <= n.id[1] + n.len) {
|
||||
// id is in n's range
|
||||
var diff = id[1] + length - (n.id[1] + n.len) // overlapping right
|
||||
if (diff > 0) {
|
||||
// id+length overlaps n
|
||||
if (!n.gc) {
|
||||
n.len += diff
|
||||
} else {
|
||||
diff = n.id[1] + n.len - id[1] // overlapping left (id till n.end)
|
||||
if (diff < length) {
|
||||
// a partial deletion
|
||||
n = {id: [id[0], id[1] + diff], len: length - diff, gc: false}
|
||||
yield* this.ds.put(n)
|
||||
} else {
|
||||
// already gc'd
|
||||
throw new Error('Cannot happen! (it dit though.. :()')
|
||||
// return n
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// no overlapping, already deleted
|
||||
return n
|
||||
}
|
||||
} else {
|
||||
// cannot extend left
|
||||
n = {id: id, len: 1, gc: false}
|
||||
yield* this.ds.put(n)
|
||||
// cannot extend left (there is no left!)
|
||||
n = {id: id, len: length, gc: false}
|
||||
yield* this.ds.put(n) // TODO: you double-put !!
|
||||
}
|
||||
} else {
|
||||
// cannot extend left
|
||||
n = {id: id, len: 1, gc: false}
|
||||
n = {id: id, len: length, gc: false}
|
||||
yield* this.ds.put(n)
|
||||
}
|
||||
// can extend right?
|
||||
var next = yield* this.ds.findNext(n.id)
|
||||
if (
|
||||
next != null &&
|
||||
Y.utils.compareIds([n.id[0], n.id[1] + n.len], next.id) &&
|
||||
!next.gc
|
||||
n.id[0] === next.id[0] &&
|
||||
n.id[1] + n.len >= next.id[1]
|
||||
) {
|
||||
n.len = n.len + next.len
|
||||
yield* this.ds.delete(next.id)
|
||||
diff = n.id[1] + n.len - next.id[1] // from next.start to n.end
|
||||
if (next.gc) {
|
||||
if (diff >= 0) {
|
||||
n.len -= diff
|
||||
if (diff > next.len) {
|
||||
// need to create another deletion after $next
|
||||
// TODO: (may not be necessary, because this case shouldn't happen!)
|
||||
// also this is supposed to return a deletion range. which one to choose? n or the new created deletion?
|
||||
throw new Error('This case is not handled (on purpose!)')
|
||||
}
|
||||
} // else: everything is fine :)
|
||||
} else {
|
||||
if (diff >= 0) {
|
||||
if (diff > next.len) {
|
||||
// may be neccessary to extend next.next!
|
||||
// TODO: (may not be necessary, because this case shouldn't happen!)
|
||||
throw new Error('This case is not handled (on purpose!)')
|
||||
}
|
||||
n.len += next.len - diff
|
||||
yield* this.ds.delete(next.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
yield* this.ds.put(n)
|
||||
return n
|
||||
@ -330,19 +369,8 @@ module.exports = function (Y/* :any */) {
|
||||
*/
|
||||
* garbageCollectOperation (id) {
|
||||
this.store.addToDebug('yield* this.garbageCollectOperation(', id, ')')
|
||||
// check to increase the state of the respective user
|
||||
var o = null
|
||||
var state = yield* this.getState(id[0])
|
||||
if (state.clock === id[1]) {
|
||||
state.clock++
|
||||
// also check if more expected operations were gc'd
|
||||
yield* this.checkDeleteStoreForState(state)
|
||||
// then set the state
|
||||
yield* this.setState(state)
|
||||
} else if (state.clock > id[1]) {
|
||||
o = yield* this.getOperation(id)
|
||||
} // else state.clock < id[1], don't clean up
|
||||
yield* this.markGarbageCollected(id) // always mark gc'd
|
||||
var o = yield* this.getOperation(id)
|
||||
yield* this.markGarbageCollected(id, 1) // always mark gc'd
|
||||
// if op exists, then clean that mess up..
|
||||
if (o != null) {
|
||||
/*
|
||||
@ -487,9 +515,7 @@ module.exports = function (Y/* :any */) {
|
||||
* applyDeleteSet (ds) {
|
||||
var deletions = []
|
||||
function createDeletions (user, start, len, gc) {
|
||||
for (var c = start; c < start + len; c++) {
|
||||
deletions.push([user, c, gc])
|
||||
}
|
||||
deletions.push([user, start, len, gc])
|
||||
}
|
||||
|
||||
for (var user in ds) {
|
||||
@ -544,28 +570,46 @@ module.exports = function (Y/* :any */) {
|
||||
}
|
||||
for (var i = 0; i < deletions.length; i++) {
|
||||
var del = deletions[i]
|
||||
var id = [del[0], del[1]]
|
||||
// always try to delete..
|
||||
var state = yield* this.getState(id[0])
|
||||
if (id[1] < state.clock) {
|
||||
var addOperation = yield* this.deleteOperation(id)
|
||||
if (addOperation) {
|
||||
// TODO:.. really .. here? You could prevent calling all these functions in operationAdded
|
||||
yield* this.store.operationAdded(this, {struct: 'Delete', target: id})
|
||||
var state = yield* this.getState(del[0])
|
||||
if (del[1] < state.clock) {
|
||||
for (let c = del[1]; c < del[1] + del[2]; c++) {
|
||||
var id = [del[0], c]
|
||||
var addOperation = yield* this.deleteOperation(id)
|
||||
if (addOperation) {
|
||||
// TODO:.. really .. here? You could prevent calling all these functions in operationAdded
|
||||
yield* this.store.operationAdded(this, {struct: 'Delete', target: id})
|
||||
}
|
||||
if (del[3]) {
|
||||
// gc
|
||||
yield* this.garbageCollectOperation(id)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
yield* this.markDeleted(id)
|
||||
if (del[3]) {
|
||||
yield* this.markGarbageCollected([del[0], del[1]], del[2])
|
||||
} else {
|
||||
yield* this.markDeleted([del[0], del[1]], del[2])
|
||||
}
|
||||
}
|
||||
if (del[2]) {
|
||||
// gc
|
||||
yield* this.garbageCollectOperation(id)
|
||||
if (del[3]) {
|
||||
// check to increase the state of the respective user
|
||||
if (state.clock >= del[1] && state.clock < del[1] + del[2]) {
|
||||
state.clock = del[1] + del[2]
|
||||
// also check if more expected operations were gc'd
|
||||
yield* this.checkDeleteStoreForState(state) // TODO: unneccessary?
|
||||
// then set the state
|
||||
yield* this.setState(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.store.forwardAppliedOperations) {
|
||||
var ops = deletions.map(function (d) {
|
||||
return {struct: 'Delete', target: [d[0], d[1]]}
|
||||
})
|
||||
this.store.y.connector.broadcastOps(ops)
|
||||
for (let c = del[1]; c < del[1] + del[2]; c++) {
|
||||
var ops = deletions.map(function (d) {
|
||||
return {struct: 'Delete', target: [d[0], c]} // TODO: implement Delete with deletion length!
|
||||
})
|
||||
this.store.y.connector.broadcastOps(ops)
|
||||
}
|
||||
}
|
||||
}
|
||||
* isGarbageCollected (id) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user