started implementing the garbage collector
This commit is contained in:
parent
dae0f71cbc
commit
c8ded24842
@ -133,7 +133,7 @@ gulp.task('build_jasmine_browser', function () {
|
||||
.pipe(gulp.dest('build'))
|
||||
})
|
||||
|
||||
gulp.task('develop', ['build_jasmine_browser', 'build'], function () {
|
||||
gulp.task('develop', ['build_jasmine_browser'], function () {
|
||||
gulp.watch(files.test, ['build_jasmine_browser'])
|
||||
// gulp.watch(files.test, ["test"])
|
||||
gulp.watch(files.test, ['build'])
|
||||
|
@ -99,7 +99,9 @@ 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])
|
||||
expect(o.deleted).toBeTruthy()
|
||||
if (o != null) {
|
||||
expect(o.deleted).toBeTruthy()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -139,7 +141,8 @@ async function createUsers(self, numberOfUsers) {//eslint-disable-line
|
||||
for (var i = 0; i < numberOfUsers; i++) {
|
||||
promises.push(Y({
|
||||
db: {
|
||||
name: 'Memory'
|
||||
name: 'Memory',
|
||||
gcTimeout: -1
|
||||
},
|
||||
connector: {
|
||||
name: 'Test',
|
||||
|
@ -39,7 +39,7 @@ class AbstractTransaction { // eslint-disable-line no-unused-vars
|
||||
}
|
||||
|
||||
class AbstractOperationStore { // eslint-disable-line no-unused-vars
|
||||
constructor (y) {
|
||||
constructor (y, opts) {
|
||||
this.y = y
|
||||
// E.g. this.listenersById[id] : Array<Listener>
|
||||
this.listenersById = {}
|
||||
@ -62,6 +62,44 @@ class AbstractOperationStore { // eslint-disable-line no-unused-vars
|
||||
this.initializedTypes = {}
|
||||
this.whenUserIdSetListener = null
|
||||
this.waitingOperations = new RBTree()
|
||||
|
||||
this.gc1 = [] // first stage
|
||||
this.gc2 = [] // second stage -> after that, kill it
|
||||
this.gcTimeout = opts.gcTimeout || 5000
|
||||
var os = this
|
||||
function garbageCollect () {
|
||||
os.requestTransaction(function * () {
|
||||
for (var i in os.gc2) {
|
||||
var oid = os.gc2[i]
|
||||
var o = yield* this.getOperation(oid)
|
||||
if (o.left != null) {
|
||||
var left = yield* this.getOperation(o.left)
|
||||
left.right = o.right
|
||||
}
|
||||
if (o.right != null) {
|
||||
var right = yield* this.getOperation(o.right)
|
||||
right.left = o.left
|
||||
}
|
||||
yield* this.removeOperation(o.id)
|
||||
}
|
||||
os.gc2 = os.gc1
|
||||
os.gc1 = []
|
||||
if (os.gcTimeout > 0) {
|
||||
os.gcInterval = setTimeout(garbageCollect, os.gcTimeout)
|
||||
}
|
||||
})
|
||||
}
|
||||
this.garbageCollect = garbageCollect
|
||||
if (this.gcTimeout > 0) {
|
||||
garbageCollect()
|
||||
}
|
||||
}
|
||||
addToGarbageCollector (op) {
|
||||
this.gc1.push(op)
|
||||
}
|
||||
destroy () {
|
||||
clearInterval(this.gcInterval)
|
||||
this.gcInterval = null
|
||||
}
|
||||
setUserId (userId) {
|
||||
this.userId = userId
|
||||
@ -201,7 +239,7 @@ class AbstractOperationStore { // eslint-disable-line no-unused-vars
|
||||
}
|
||||
// notify parent, if it has been initialized as a custom type
|
||||
var t = this.initializedTypes[JSON.stringify(op.parent)]
|
||||
if (t != null) {
|
||||
if (t != null && !op.deleted) {
|
||||
yield* t._changed(transaction, copyObject(op))
|
||||
}
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ Y.IndexedDB = (function () { // eslint-disable-line
|
||||
}
|
||||
class OperationStore extends AbstractOperationStore { // eslint-disable-line no-undef
|
||||
constructor (y, opts) {
|
||||
super(y)
|
||||
super(y, opts)
|
||||
if (opts == null) {
|
||||
opts = {}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ if (typeof window !== 'undefined') {
|
||||
describe('IndexedDB', function () {
|
||||
var ob
|
||||
beforeAll(function () {
|
||||
ob = new Y.IndexedDB(null, {namespace: 'Test'})
|
||||
ob = new Y.IndexedDB(null, {namespace: 'Test', gcTimeout: -1})
|
||||
})
|
||||
|
||||
it('can add and get operation', function (done) {
|
||||
|
@ -238,8 +238,8 @@ Y.Memory = (function () { // eslint-disable-line no-unused-vars
|
||||
}
|
||||
}
|
||||
class OperationStore extends AbstractOperationStore { // eslint-disable-line no-undef
|
||||
constructor (y) {
|
||||
super(y)
|
||||
constructor (y, opts) {
|
||||
super(y, opts)
|
||||
this.os = new RBTree()
|
||||
this.ss = {}
|
||||
this.waitingTransactions = []
|
||||
@ -249,10 +249,10 @@ Y.Memory = (function () { // eslint-disable-line no-unused-vars
|
||||
logTable () {
|
||||
this.os.logTable()
|
||||
}
|
||||
requestTransaction (_makeGen) {
|
||||
requestTransaction (_makeGen, requestNow = false) {
|
||||
if (!this.transactionInProgress) {
|
||||
this.transactionInProgress = true
|
||||
setTimeout(() => {
|
||||
var transact = () => {
|
||||
var makeGen = _makeGen
|
||||
while (makeGen != null) {
|
||||
var t = new Transaction(this)
|
||||
@ -268,12 +268,18 @@ Y.Memory = (function () { // eslint-disable-line no-unused-vars
|
||||
makeGen = this.waitingTransactions.shift()
|
||||
}
|
||||
this.transactionInProgress = false
|
||||
}, 0)
|
||||
}
|
||||
if (!requestNow) {
|
||||
setTimeout(transact, 0)
|
||||
} else {
|
||||
transact()
|
||||
}
|
||||
} else {
|
||||
this.waitingTransactions.push(_makeGen)
|
||||
}
|
||||
}
|
||||
* removeDatabase () { // eslint-disable-line
|
||||
* destroy () { // eslint-disable-line
|
||||
super.destroy()
|
||||
delete this.os
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,18 @@ var Struct = {
|
||||
var target = yield* this.getOperation(op.target)
|
||||
if (!target.deleted) {
|
||||
target.deleted = true
|
||||
if (target.left !== null && (yield* this.getOperation(target.left)).deleted) {
|
||||
this.store.addToGarbageCollector(target.id)
|
||||
target.gc = true
|
||||
}
|
||||
if (target.right !== null) {
|
||||
var right = yield* this.getOperation(target.right)
|
||||
if (right.deleted && right.gc == null) {
|
||||
this.store.addToGarbageCollector(right.id)
|
||||
right.gc = true
|
||||
yield* this.setOperation(right)
|
||||
}
|
||||
}
|
||||
yield* this.setOperation(target)
|
||||
this.ds.delete(target.id)
|
||||
var t = this.store.initializedTypes[JSON.stringify(target.parent)]
|
||||
|
@ -179,6 +179,27 @@ describe('Array Type', function () {
|
||||
await wait(50)
|
||||
done()
|
||||
})
|
||||
it('garbage collects', async function (done) {
|
||||
var l1, l2, l3
|
||||
l1 = await y1.set('Array', Y.Array)
|
||||
l1.insert(0, ['x', 'y', 'z'])
|
||||
await flushAll()
|
||||
yconfig1.disconnect()
|
||||
l1.delete(0, 3)
|
||||
l2 = await y2.get('Array')
|
||||
await wait()
|
||||
yconfig1.reconnect()
|
||||
await wait()
|
||||
l3 = await y3.get('Array')
|
||||
await flushAll()
|
||||
yconfig1.db.garbageCollect()
|
||||
yconfig1.db.garbageCollect()
|
||||
yconfig1.db.logTable()
|
||||
expect(l1.toArray()).toEqual(l2.toArray())
|
||||
expect(l2.toArray()).toEqual(l3.toArray())
|
||||
expect(l2.toArray()).toEqual([])
|
||||
done()
|
||||
})
|
||||
})
|
||||
describe(`Random tests`, function () {
|
||||
var randomArrayTransactions = [
|
||||
|
@ -34,10 +34,18 @@
|
||||
if (op.left === null) {
|
||||
if (op.opContent != null) {
|
||||
delete this.contents[key]
|
||||
this.opContents[key] = op.opContent
|
||||
if (op.deleted) {
|
||||
delete this.opContents[key]
|
||||
} else {
|
||||
this.opContents[key] = op.opContent
|
||||
}
|
||||
} else {
|
||||
delete this.opContents[key]
|
||||
this.contents[key] = op.content
|
||||
if (op.deleted) {
|
||||
delete this.contents[key]
|
||||
} else {
|
||||
this.contents[key] = op.content
|
||||
}
|
||||
}
|
||||
this.map[key] = op.id
|
||||
var insertEvent = {
|
||||
@ -54,11 +62,8 @@
|
||||
}
|
||||
} else if (op.struct === 'Delete') {
|
||||
if (compareIds(this.map[key], op.target)) {
|
||||
if (this.opContents[key] != null) {
|
||||
delete this.opContents[key]
|
||||
} else {
|
||||
delete this.contents[key]
|
||||
}
|
||||
delete this.opContents[key]
|
||||
delete this.contents[key]
|
||||
var deleteEvent = {
|
||||
name: key,
|
||||
object: this,
|
||||
|
Loading…
x
Reference in New Issue
Block a user