gc implementation
This commit is contained in:
parent
c8ded24842
commit
06e7caab2d
@ -61,6 +61,13 @@ async function applyRandomTransactions (users, objects, transactions, numberOfTr
|
||||
await users[0].connector.flushAll()
|
||||
}
|
||||
|
||||
async function garbageCollectAllUsers (users) {
|
||||
for (var i in users) {
|
||||
await users[i].db.garbageCollect()
|
||||
await users[i].db.garbageCollect()
|
||||
}
|
||||
}
|
||||
|
||||
async function compareAllUsers(users){//eslint-disable-line
|
||||
var s1, s2, ds1, ds2, allDels1, allDels2
|
||||
var db1 = []
|
||||
@ -81,6 +88,8 @@ async function compareAllUsers(users){//eslint-disable-line
|
||||
})
|
||||
}
|
||||
await users[0].connector.flushAll()
|
||||
await garbageCollectAllUsers(users)
|
||||
await wait(200)
|
||||
for (var uid = 0; uid < users.length; uid++) {
|
||||
var u = users[uid]
|
||||
// compare deleted ops against deleteStore
|
||||
@ -99,9 +108,8 @@ async function compareAllUsers(users){//eslint-disable-line
|
||||
var d = ds[j]
|
||||
for (var i = 0; i < d.len; i++) {
|
||||
var o = yield* this.getOperation([d.id[0], d.id[1] + i])
|
||||
if (o != null) {
|
||||
expect(o.deleted).toBeTruthy()
|
||||
}
|
||||
// gc'd or deleted
|
||||
expect(o == null || o.deleted).toBeTruthy()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -68,6 +68,7 @@ class AbstractOperationStore { // eslint-disable-line no-unused-vars
|
||||
this.gcTimeout = opts.gcTimeout || 5000
|
||||
var os = this
|
||||
function garbageCollect () {
|
||||
var def = Promise.defer()
|
||||
os.requestTransaction(function * () {
|
||||
for (var i in os.gc2) {
|
||||
var oid = os.gc2[i]
|
||||
@ -87,7 +88,9 @@ class AbstractOperationStore { // eslint-disable-line no-unused-vars
|
||||
if (os.gcTimeout > 0) {
|
||||
os.gcInterval = setTimeout(garbageCollect, os.gcTimeout)
|
||||
}
|
||||
def.resolve()
|
||||
})
|
||||
return def.promise
|
||||
}
|
||||
this.garbageCollect = garbageCollect
|
||||
if (this.gcTimeout > 0) {
|
||||
@ -125,8 +128,12 @@ class AbstractOperationStore { // eslint-disable-line no-unused-vars
|
||||
apply (ops) {
|
||||
for (var key in ops) {
|
||||
var o = ops[key]
|
||||
var required = Struct[o.struct].requiredOps(o)
|
||||
this.whenOperationsExist(required, o)
|
||||
if (!o.gc) {
|
||||
var required = Struct[o.struct].requiredOps(o)
|
||||
this.whenOperationsExist(required, o)
|
||||
} else {
|
||||
throw new Error("Must not receive gc'd ops!")
|
||||
}
|
||||
}
|
||||
}
|
||||
// op is executed as soon as every operation requested is available.
|
||||
@ -200,6 +207,7 @@ class AbstractOperationStore { // eslint-disable-line no-unused-vars
|
||||
var state = yield* this.getState(op.id[0])
|
||||
if (op.id[1] === state.clock) {
|
||||
state.clock++
|
||||
yield* this.checkDeleteStoreForState(state)
|
||||
yield* this.setState(state)
|
||||
yield* Struct[op.struct].execute.call(this, op)
|
||||
yield* this.addOperation(op)
|
||||
|
@ -125,6 +125,12 @@ Y.Memory = (function () { // eslint-disable-line no-unused-vars
|
||||
this.os = store.os
|
||||
this.ds = store.ds
|
||||
}
|
||||
* checkDeleteStoreForState (state) {
|
||||
var n = this.ds.findNodeWithUpperBound([state.user, state.clock])
|
||||
if (n !== null && n.val.id[0] === state.user) {
|
||||
state.clock = Math.max(state.clock, n.val.id[1] + n.val.len)
|
||||
}
|
||||
}
|
||||
* getDeleteSet (id) {
|
||||
return this.ds.toDeleteSet(id)
|
||||
}
|
||||
@ -196,14 +202,16 @@ Y.Memory = (function () { // eslint-disable-line no-unused-vars
|
||||
var endPos = endState.clock
|
||||
|
||||
this.os.iterate([user, startPos], [user, endPos], function (op) {// eslint-disable-line
|
||||
ops.push(Struct[op.struct].encode(op))
|
||||
if (!op.gc) {
|
||||
ops.push(Struct[op.struct].encode(op))
|
||||
}
|
||||
})
|
||||
}
|
||||
var res = []
|
||||
for (var op of ops) {
|
||||
res.push(yield* this.makeOperationReady(startSS, op))
|
||||
var state = startSS[op.id[0]] || 0
|
||||
if (state === op.id[1]) {
|
||||
if (state === op.id[1] || true) {
|
||||
startSS[op.id[0]] = state + 1
|
||||
} else {
|
||||
throw new Error('Unexpected operation!')
|
||||
@ -218,8 +226,8 @@ Y.Memory = (function () { // eslint-disable-line no-unused-vars
|
||||
var clock
|
||||
while (o.right != null) {
|
||||
// while unknown, go to the right
|
||||
clock = ss[o.right[0]]
|
||||
if (clock != null && o.right[1] < clock) {
|
||||
clock = ss[o.right[0]] || 0
|
||||
if (o.right[1] < clock && !o.gc) {
|
||||
break
|
||||
}
|
||||
o = yield* this.getOperation(o.right)
|
||||
@ -227,8 +235,8 @@ Y.Memory = (function () { // eslint-disable-line no-unused-vars
|
||||
op.right = o.right
|
||||
while (o.left != null) {
|
||||
// while unknown, go to the right
|
||||
clock = ss[o.left[0]]
|
||||
if (clock != null && o.left[1] < clock) {
|
||||
clock = ss[o.left[0]] || 0
|
||||
if (o.left[1] < clock && !o.gc) {
|
||||
break
|
||||
}
|
||||
o = yield* this.getOperation(o.left)
|
||||
|
@ -25,11 +25,12 @@ var Struct = {
|
||||
return op
|
||||
},
|
||||
requiredOps: function (op) {
|
||||
return [op.target]
|
||||
return [] // [op.target]
|
||||
},
|
||||
execute: function * (op) {
|
||||
console.log('Delete', op, console.trace())
|
||||
var target = yield* this.getOperation(op.target)
|
||||
if (!target.deleted) {
|
||||
if (target != null && !target.deleted) {
|
||||
target.deleted = true
|
||||
if (target.left !== null && (yield* this.getOperation(target.left)).deleted) {
|
||||
this.store.addToGarbageCollector(target.id)
|
||||
@ -44,13 +45,19 @@ var Struct = {
|
||||
}
|
||||
}
|
||||
yield* this.setOperation(target)
|
||||
this.ds.delete(target.id)
|
||||
var t = this.store.initializedTypes[JSON.stringify(target.parent)]
|
||||
if (t != null) {
|
||||
yield* t._changed(this, copyObject(op))
|
||||
}
|
||||
}
|
||||
|
||||
if (target == null || !target.deleted) {
|
||||
this.ds.delete(op.target)
|
||||
var state = yield* this.getState(op.target[0])
|
||||
if (state === op.target[1]) {
|
||||
yield* this.checkDeleteStoreForState(state)
|
||||
yield* this.setState(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Insert: {
|
||||
@ -65,7 +72,7 @@ var Struct = {
|
||||
}
|
||||
*/
|
||||
encode: function (op) {
|
||||
/*
|
||||
/* bad idea, right?
|
||||
var e = {
|
||||
id: op.id,
|
||||
left: op.left,
|
||||
|
@ -1,17 +1,17 @@
|
||||
/* global createUsers, wait, Y, compareAllUsers, getRandomNumber, applyRandomTransactions */
|
||||
/* eslint-env browser,jasmine */
|
||||
|
||||
var numberOfYArrayTests = 100
|
||||
var numberOfYArrayTests = 5
|
||||
|
||||
describe('Array Type', function () {
|
||||
var y1, y2, y3, yconfig1, yconfig2, yconfig3, flushAll
|
||||
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000
|
||||
beforeEach(async function (done) {
|
||||
await createUsers(this, 5)
|
||||
await createUsers(this, 2)
|
||||
y1 = (yconfig1 = this.users[0]).root
|
||||
y2 = (yconfig2 = this.users[1]).root
|
||||
y3 = (yconfig3 = this.users[2]).root
|
||||
// y3 = (yconfig3 = this.users[2]).root
|
||||
flushAll = this.users[0].connector.flushAll
|
||||
done()
|
||||
})
|
||||
@ -192,12 +192,12 @@ describe('Array Type', function () {
|
||||
await wait()
|
||||
l3 = await y3.get('Array')
|
||||
await flushAll()
|
||||
yconfig1.db.garbageCollect()
|
||||
yconfig1.db.garbageCollect()
|
||||
await garbageCollectAllUsers(this.users)
|
||||
yconfig1.db.logTable()
|
||||
expect(l1.toArray()).toEqual(l2.toArray())
|
||||
expect(l2.toArray()).toEqual(l3.toArray())
|
||||
expect(l2.toArray()).toEqual([])
|
||||
await compareAllUsers(this.users)
|
||||
done()
|
||||
})
|
||||
})
|
||||
@ -240,6 +240,9 @@ describe('Array Type', function () {
|
||||
done()
|
||||
})
|
||||
it(`succeed after ${numberOfYArrayTests} actions`, async function (done) {
|
||||
for (var u of this.users) {
|
||||
u.connector.debug = true
|
||||
}
|
||||
await applyRandomTransactions(this.users, this.arrays, randomArrayTransactions, numberOfYArrayTests)
|
||||
await flushAll()
|
||||
await compareArrayValues(this.arrays)
|
||||
|
Loading…
x
Reference in New Issue
Block a user