fixed several issues of the gc. I.e. the gc sometimes did not collect the whole subtree when deleting an operation

This commit is contained in:
Kevin Jahns 2016-03-21 21:00:28 +01:00
parent f844dcbc1e
commit 88971b4e69
3 changed files with 129 additions and 25 deletions

View File

@ -110,6 +110,18 @@ module.exports = function (Y /* :any */) {
garbageCollect() garbageCollect()
} }
} }
emptyGarbageCollector () {
return new Promise (resolve => {
var check = () => {
if (this.gc1.length > 0 || this.gc2.length > 0) {
this.garbageCollect().then(check)
} else {
resolve()
}
}
setTimeout(check, 0)
})
}
addToDebug () { addToDebug () {
if (typeof YConcurrency_TestingMode !== 'undefined') { if (typeof YConcurrency_TestingMode !== 'undefined') {
var command /* :string */ = Array.prototype.map.call(arguments, function (s) { var command /* :string */ = Array.prototype.map.call(arguments, function (s) {
@ -339,15 +351,7 @@ module.exports = function (Y /* :any */) {
} }
} else { } else {
// increase SS // increase SS
var o = op yield* transaction.updateState(op.id[0])
var state = yield* transaction.getState(op.id[0])
while (o != null && o.id[1] === state.clock && op.id[0] === o.id[0]) {
// either its a new operation (1. case), or it is an operation that was deleted, but is not yet in the OS
state.clock++
yield* transaction.checkDeleteStoreForState(state)
o = yield* transaction.os.findNext(o.id)
}
yield* transaction.setState(state)
// notify whenOperation listeners (by id) // notify whenOperation listeners (by id)
var sid = JSON.stringify(op.id) var sid = JSON.stringify(op.id)
@ -364,6 +368,15 @@ module.exports = function (Y /* :any */) {
} }
var t = this.initializedTypes[JSON.stringify(op.parent)] var t = this.initializedTypes[JSON.stringify(op.parent)]
// if parent is deleted, mark as gc'd and return
if (op.parent != null) {
var parentIsDeleted = yield* transaction.isDeleted(op.parent)
if (parentIsDeleted) {
yield* transaction.deleteList(op.id)
return
}
}
// Delete if DS says this is actually deleted // Delete if DS says this is actually deleted
var opIsDeleted = yield* transaction.isDeleted(op.id) var opIsDeleted = yield* transaction.isDeleted(op.id)
if (!op.deleted && opIsDeleted) { if (!op.deleted && opIsDeleted) {
@ -375,8 +388,13 @@ 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 && !opIsDeleted) { if (t != null) {
yield* t._changed(transaction, Y.utils.copyObject(op)) let o = Y.utils.copyObject(op)
if (opIsDeleted && !o.deleted) {
// op did not reflect the created delete op (happens when not using y-memory)
o.deleted = true
}
yield* t._changed(transaction, o)
} }
} }
} }

View File

@ -139,9 +139,9 @@ g.applyRandomTransactionsWithGC = async(function * applyRandomTransactions (user
g.garbageCollectAllUsers = async(function * garbageCollectAllUsers (users) { g.garbageCollectAllUsers = async(function * garbageCollectAllUsers (users) {
// gc two times because of the two gc phases (really collect everything) // gc two times because of the two gc phases (really collect everything)
yield wait(100)
for (var i in users) { for (var i in users) {
yield users[i].db.garbageCollect() yield users[i].db.emptyGarbageCollector()
yield users[i].db.garbageCollect()
} }
}) })

View File

@ -154,17 +154,32 @@ module.exports = function (Y/* :any */) {
} }
* deleteList (start) { * deleteList (start) {
if (this.store.y.connector.isSynced) { while (start != null) {
while (start != null && this.store.y.connector.isSynced) { start = yield* this.getOperation(start)
start = yield* this.getOperation(start) if (start.gc) {
break
} else {
start.gc = true start.gc = true
start.deleted = true
yield* this.setOperation(start) yield* this.setOperation(start)
// TODO: will always reset the parent.. yield* this.markDeleted(start.id, 1)
this.store.gc1.push(start.id) if (start.opContent != null) {
yield* this.deleteOperation(start.opContent)
/*
yield* this.deleteOperation(start.opContent)
var opContent = yield* this.getOperation(start.opContent)
opContent.gc = true
yield* this.setOperation(opContent)
if (this.store.y.connector.isSynced) {
this.store.gc1.push(opContent.id)
}
*/
}
if (this.store.y.connector.isSynced){
this.store.gc1.push(start.id)
}
start = start.right start = start.right
} }
} else {
// TODO: when not possible??? do later in (gcWhenSynced)
} }
} }
@ -199,19 +214,24 @@ module.exports = function (Y/* :any */) {
if (target.start != null) { if (target.start != null) {
// TODO: don't do it like this .. -.- // TODO: don't do it like this .. -.-
yield* this.deleteList(target.start) yield* this.deleteList(target.start)
yield* this.deleteList(target.id) // yield* this.deleteList(target.id) -- do not gc itself because this may still get referenced
} }
if (target.map != null) { if (target.map != null) {
for (var name in target.map) { for (var name in target.map) {
yield* this.deleteList(target.map[name]) yield* this.deleteList(target.map[name])
} }
// TODO: here to.. (see above) // TODO: here to.. (see above)
yield* this.deleteList(target.id) // yield* this.deleteList(target.id) -- see above
} }
if (target.opContent != null) { if (target.opContent != null) {
yield* this.deleteOperation(target.opContent) yield* this.deleteOperation(target.opContent)
// target.opContent = null // target.opContent = null
} }
if (target.requires != null) {
for (var i = 0; i < target.requires.length; i++) {
yield* this.deleteOperation(target.requires[i])
}
}
} }
var left var left
if (target.left != null) { if (target.left != null) {
@ -377,9 +397,40 @@ module.exports = function (Y/* :any */) {
*/ */
* garbageCollectAfterSync () { * garbageCollectAfterSync () {
yield* this.os.iterate(this, null, null, function * (op) { yield* this.os.iterate(this, null, null, function * (op) {
if (op.deleted && op.left != null) { if (op.gc) {
var left = yield* this.getOperation(op.left) this.store.gc1.push(op.id)
this.store.addToGarbageCollector(op, left) } else {
if (op.parent != null) {
var parentDeleted = yield* this.isDeleted(op.parent)
if (parentDeleted) {
op.gc = true
if (!op.deleted) {
yield* this.markDeleted(op.id, 1)
op.deleted = true
if (op.opContent != null) {
yield* this.deleteOperation(op.opContent)
/*
var opContent = yield* this.getOperation(op.opContent)
opContent.gc = true
yield* this.setOperation(opContent)
this.store.gc1.push(opContent.id)
*/
}
if (op.requires != null) {
for (var i = 0; i < op.requires.length; i++) {
yield* this.deleteOperation(op.requires[i])
}
}
}
yield* this.setOperation(op)
this.store.gc1.push(op.id)
return
}
}
if (op.deleted && op.left != null) {
var left = yield* this.getOperation(op.left)
this.store.addToGarbageCollector(op, left)
}
} }
}) })
} }
@ -404,6 +455,29 @@ module.exports = function (Y/* :any */) {
o = yield* this.getOperation(id) o = yield* this.getOperation(id)
} }
*/ */
var deps = []
if (o.opContent != null) {
deps.push(o.opContent)
}
if (o.requires != null) {
deps = deps.concat(o.requires)
}
for (var i = 0; i < deps.length; i++) {
var dep = yield* this.getOperation(deps[i])
if (dep != null) {
if (!dep.deleted) {
yield* this.deleteOperation(dep.id)
dep = yield* this.getOperation(dep.id)
}
dep.gc = true
yield* this.setOperation(dep)
this.store.gc1.push(dep.id)
} else {
yield* this.markGarbageCollected(deps[i], 1)
yield* this.updateState(deps[i][0]) // TODO: unneccessary?
}
}
// remove gc'd op from the left op, if it exists // remove gc'd op from the left op, if it exists
if (o.left != null) { if (o.left != null) {
@ -535,6 +609,18 @@ module.exports = function (Y/* :any */) {
state.clock = Math.max(state.clock, n.id[1] + n.len) state.clock = Math.max(state.clock, n.id[1] + n.len)
} }
} }
* updateState (user) {
var state = yield* this.getState(user)
yield* this.checkDeleteStoreForState(state)
var o = yield* this.getOperation([user, state.clock])
while (o != null && o.id[1] === state.clock && user === o.id[0]) {
// either its a new operation (1. case), or it is an operation that was deleted, but is not yet in the OS
state.clock++
yield* this.checkDeleteStoreForState(state)
o = yield* this.os.findNext(o.id)
}
yield* this.setState(state)
}
/* /*
apply a delete set in order to get apply a delete set in order to get
the state of the supplied ds the state of the supplied ds