Further reduced number of db requests

This commit is contained in:
Kevin Jahns 2016-01-26 15:30:19 +01:00
parent 6b1cf18822
commit 31d2a231e3
4 changed files with 151 additions and 106 deletions

View File

@ -54,8 +54,9 @@ require('./gulpfile.helper.js')(gulp, {
moduleName: 'yjs', moduleName: 'yjs',
includeRuntime: true, includeRuntime: true,
specs: [ specs: [
'./src/Database.spec.js',
'../y-array/src/Array.spec.js', '../y-array/src/Array.spec.js',
'./src/Database.spec.js' '../y-map/src/Map.spec.js'
] ]
}) })

View File

@ -75,7 +75,7 @@ module.exports = function (Y /* :any */) {
} }
this.gc1 = [] // first stage this.gc1 = [] // first stage
this.gc2 = [] // second stage -> after that, remove the op this.gc2 = [] // second stage -> after that, remove the op
this.gcTimeout = opts.gcTimeout || 5000 this.gcTimeout = opts.gcTimeout || 50000
function garbageCollect () { function garbageCollect () {
return os.whenTransactionsFinished().then(function () { return os.whenTransactionsFinished().then(function () {
if (os.gc1.length > 0 || os.gc2.length > 0) { if (os.gc1.length > 0 || os.gc2.length > 0) {

View File

@ -54,10 +54,10 @@ for (let database of databases) {
yield* this.markDeleted(['166', 2]) yield* this.markDeleted(['166', 2])
yield* this.markDeleted(['166', 0]) yield* this.markDeleted(['166', 0])
yield* this.markDeleted(['166', 2]) 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', 1])
yield* this.markDeleted(['166', 3]) yield* this.markDeleted(['166', 3])
yield* this.markGarbageCollected(['166', 3]) yield* this.markGarbageCollected(['166', 3], 1)
yield* this.markDeleted(['166', 0]) yield* this.markDeleted(['166', 0])
expect(yield* this.getDeleteSet()).toEqual({'166': [[0, 2, false], [2, 2, true]]}) expect(yield* this.getDeleteSet()).toEqual({'166': [[0, 2, false], [2, 2, true]]})
done() done()
@ -68,9 +68,9 @@ for (let database of databases) {
yield* this.markDeleted(['293', 0]) yield* this.markDeleted(['293', 0])
yield* this.markDeleted(['291', 2]) yield* this.markDeleted(['291', 2])
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.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]]}) expect(yield* this.getDeleteSet()).toEqual({'291': [[2, 1, true]], '293': [[0, 1, true], [1, 1, false]]})
done() done()
}) })
@ -81,15 +81,15 @@ for (let database of databases) {
yield* this.markDeleted(['581', 1]) yield* this.markDeleted(['581', 1])
yield* this.markDeleted(['580', 0]) yield* this.markDeleted(['580', 0])
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(['581', 2])
yield* this.markDeleted(['580', 1]) yield* this.markDeleted(['580', 1])
yield* this.markDeleted(['580', 2]) yield* this.markDeleted(['580', 2])
yield* this.markDeleted(['580', 1]) yield* this.markDeleted(['580', 1])
yield* this.markDeleted(['580', 2]) yield* this.markDeleted(['580', 2])
yield* this.markGarbageCollected(['581', 2]) yield* this.markGarbageCollected(['581', 2], 1)
yield* this.markGarbageCollected(['581', 1]) yield* this.markGarbageCollected(['581', 1], 1)
yield* this.markGarbageCollected(['580', 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]]}) expect(yield* this.getDeleteSet()).toEqual({'580': [[0, 1, false], [1, 1, true], [2, 1, false]], '581': [[0, 3, true]]})
done() done()
}) })
@ -100,7 +100,7 @@ for (let database of databases) {
yield* this.markDeleted(['543', 2]) yield* this.markDeleted(['543', 2])
yield* this.markDeleted(['544', 0]) yield* this.markDeleted(['544', 0])
yield* this.markDeleted(['543', 2]) yield* this.markDeleted(['543', 2])
yield* this.markGarbageCollected(['544', 0]) yield* this.markGarbageCollected(['544', 0], 1)
yield* this.markDeleted(['545', 1]) yield* this.markDeleted(['545', 1])
yield* this.markDeleted(['543', 4]) yield* this.markDeleted(['543', 4])
yield* this.markDeleted(['543', 3]) yield* this.markDeleted(['543', 3])
@ -108,10 +108,10 @@ for (let database of databases) {
yield* this.markDeleted(['544', 2]) yield* this.markDeleted(['544', 2])
yield* this.markDeleted(['544', 1]) yield* this.markDeleted(['544', 1])
yield* this.markDeleted(['544', 2]) yield* this.markDeleted(['544', 2])
yield* this.markGarbageCollected(['543', 2]) yield* this.markGarbageCollected(['543', 2], 1)
yield* this.markGarbageCollected(['543', 4]) yield* this.markGarbageCollected(['543', 4], 1)
yield* this.markGarbageCollected(['544', 2]) yield* this.markGarbageCollected(['544', 2], 1)
yield* this.markGarbageCollected(['543', 3]) 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]]}) expect(yield* this.getDeleteSet()).toEqual({'543': [[2, 3, true]], '544': [[0, 1, true], [1, 1, false], [2, 1, true]], '545': [[1, 1, false]]})
done() done()
}) })
@ -142,14 +142,14 @@ for (let database of databases) {
yield* this.markDeleted(['11', 1]) yield* this.markDeleted(['11', 1])
yield* this.markDeleted(['9', 4]) yield* this.markDeleted(['9', 4])
yield* this.markDeleted(['10', 0]) yield* this.markDeleted(['10', 0])
yield* this.markGarbageCollected(['11', 2]) yield* this.markGarbageCollected(['11', 2], 1)
yield* this.markDeleted(['11', 2]) 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(['11', 3]) yield* this.markDeleted(['11', 3])
yield* this.markDeleted(['9', 4]) yield* this.markDeleted(['9', 4])
yield* this.markDeleted(['10', 0]) yield* this.markDeleted(['10', 0])
yield* this.markGarbageCollected(['11', 1]) yield* this.markGarbageCollected(['11', 1], 1)
yield* this.markDeleted(['11', 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]]}) expect(yield* this.getDeleteSet()).toEqual({'9': [[2, 1, false], [4, 1, false]], '10': [[0, 1, false]], '11': [[1, 3, true], [4, 1, false]]})
done() done()

View File

@ -222,11 +222,10 @@ module.exports = function (Y/* :any */) {
/* /*
Mark an operation as deleted&gc'd Mark an operation as deleted&gc'd
*/ */
* markGarbageCollected (id) { * markGarbageCollected (id, len) {
// this.mem.push(["gc", id]); // this.mem.push(["gc", id]);
var n = yield* this.markDeleted(id) var n = yield* this.markDeleted(id, len)
if (!n.gc) { if (n.id[1] < id[1] && !n.gc) {
if (n.id[1] < id[1]) {
// un-extend left // un-extend left
var newlen = n.len - (id[1] - n.id[1]) var newlen = n.len - (id[1] - n.id[1])
n.len -= newlen n.len -= newlen
@ -238,7 +237,7 @@ module.exports = function (Y/* :any */) {
var prev = yield* this.ds.findPrev(id) var prev = yield* this.ds.findPrev(id)
var next = yield* this.ds.findNext(id) var next = yield* this.ds.findNext(id)
if (id[1] < n.id[1] + n.len - 1) { if (id[1] < n.id[1] + n.len - len && !n.gc) {
// un-extend right // un-extend right
yield* this.ds.put({id: [id[0], id[1] + 1], len: n.len - 1, gc: false}) yield* this.ds.put({id: [id[0], id[1] + 1], len: n.len - 1, gc: false})
n.len = 1 n.len = 1
@ -267,42 +266,82 @@ module.exports = function (Y/* :any */) {
} }
yield* this.ds.put(n) yield* this.ds.put(n)
} }
}
/* /*
Mark an operation as deleted. Mark an operation as deleted.
returns the delete node returns the delete node
*/ */
* markDeleted (id) { * markDeleted (id, length) {
if (length == null) {
length = 1
// debugger // TODO!!
}
// this.mem.push(["del", id]); // this.mem.push(["del", id]);
var n = yield* this.ds.findWithUpperBound(id) var n = yield* this.ds.findWithUpperBound(id)
if (n != null && n.id[0] === id[0]) { if (n != null && n.id[0] === id[0]) {
if (n.id[1] <= id[1] && id[1] < n.id[1] + n.len) { if (n.id[1] <= id[1] && id[1] <= n.id[1] + n.len) {
// already deleted // id is in n's range
return n var diff = id[1] + length - (n.id[1] + n.len) // overlapping right
} else if (n.id[1] + n.len === id[1] && !n.gc) { if (diff > 0) {
// can extend existing deletion // id+length overlaps n
n.len++ if (!n.gc) {
n.len += diff
} else { } else {
// cannot extend left diff = n.id[1] + n.len - id[1] // overlapping left (id till n.end)
n = {id: id, len: 1, gc: false} if (diff < length) {
// a partial deletion
n = {id: [id[0], id[1] + diff], len: length - diff, gc: false}
yield* this.ds.put(n) 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 (there is no left!)
n = {id: id, len: length, gc: false}
yield* this.ds.put(n) // TODO: you double-put !!
} }
} else { } else {
// cannot extend left // cannot extend left
n = {id: id, len: 1, gc: false} n = {id: id, len: length, gc: false}
yield* this.ds.put(n) yield* this.ds.put(n)
} }
// can extend right? // can extend right?
var next = yield* this.ds.findNext(n.id) var next = yield* this.ds.findNext(n.id)
if ( if (
next != null && next != null &&
Y.utils.compareIds([n.id[0], n.id[1] + n.len], next.id) && n.id[0] === next.id[0] &&
!next.gc n.id[1] + n.len >= next.id[1]
) { ) {
n.len = n.len + next.len 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.delete(next.id)
} }
}
}
yield* this.ds.put(n) yield* this.ds.put(n)
return n return n
} }
@ -330,19 +369,8 @@ module.exports = function (Y/* :any */) {
*/ */
* garbageCollectOperation (id) { * garbageCollectOperation (id) {
this.store.addToDebug('yield* this.garbageCollectOperation(', id, ')') this.store.addToDebug('yield* this.garbageCollectOperation(', id, ')')
// check to increase the state of the respective user var o = yield* this.getOperation(id)
var o = null yield* this.markGarbageCollected(id, 1) // always mark gc'd
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
// if op exists, then clean that mess up.. // if op exists, then clean that mess up..
if (o != null) { if (o != null) {
/* /*
@ -487,9 +515,7 @@ module.exports = function (Y/* :any */) {
* applyDeleteSet (ds) { * applyDeleteSet (ds) {
var deletions = [] var deletions = []
function createDeletions (user, start, len, gc) { function createDeletions (user, start, len, gc) {
for (var c = start; c < start + len; c++) { deletions.push([user, start, len, gc])
deletions.push([user, c, gc])
}
} }
for (var user in ds) { for (var user in ds) {
@ -544,30 +570,48 @@ module.exports = function (Y/* :any */) {
} }
for (var i = 0; i < deletions.length; i++) { for (var i = 0; i < deletions.length; i++) {
var del = deletions[i] var del = deletions[i]
var id = [del[0], del[1]]
// always try to delete.. // always try to delete..
var state = yield* this.getState(id[0]) var state = yield* this.getState(del[0])
if (id[1] < state.clock) { 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) var addOperation = yield* this.deleteOperation(id)
if (addOperation) { if (addOperation) {
// TODO:.. really .. here? You could prevent calling all these functions in operationAdded // TODO:.. really .. here? You could prevent calling all these functions in operationAdded
yield* this.store.operationAdded(this, {struct: 'Delete', target: id}) yield* this.store.operationAdded(this, {struct: 'Delete', target: id})
} }
} else { if (del[3]) {
yield* this.markDeleted(id)
}
if (del[2]) {
// gc // gc
yield* this.garbageCollectOperation(id) yield* this.garbageCollectOperation(id)
} }
} }
} else {
if (del[3]) {
yield* this.markGarbageCollected([del[0], del[1]], del[2])
} else {
yield* this.markDeleted([del[0], del[1]], del[2])
}
}
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) { if (this.store.forwardAppliedOperations) {
for (let c = del[1]; c < del[1] + del[2]; c++) {
var ops = deletions.map(function (d) { var ops = deletions.map(function (d) {
return {struct: 'Delete', target: [d[0], d[1]]} return {struct: 'Delete', target: [d[0], c]} // TODO: implement Delete with deletion length!
}) })
this.store.y.connector.broadcastOps(ops) this.store.y.connector.broadcastOps(ops)
} }
} }
}
* isGarbageCollected (id) { * isGarbageCollected (id) {
var n = yield* this.ds.findWithUpperBound(id) var n = yield* this.ds.findWithUpperBound(id)
return n != null && n.id[0] === id[0] && id[1] < n.id[1] + n.len && n.gc return n != null && n.id[0] === id[0] && id[1] < n.id[1] + n.len && n.gc