From 45814c4e0055b92e502bd96fc99bd949e01fc32e Mon Sep 17 00:00:00 2001 From: Kevin Jahns <kevin.jahns@rwth-aachen.de> Date: Sat, 17 Oct 2015 17:16:36 +0200 Subject: [PATCH] fixed bug (o.right is already gc'd), implemented some test helpers --- src/Database.js | 36 ++++++++++++++++++-------- src/Transaction.js | 29 +++++++++++++-------- src/Types/Array.spec.js | 57 +++++++++++++++++++++++++++++++++++++++-- src/Types/Map.spec.js | 2 +- 4 files changed, 100 insertions(+), 24 deletions(-) diff --git a/src/Database.js b/src/Database.js index b0bce0b1..b803a09a 100644 --- a/src/Database.js +++ b/src/Database.js @@ -36,7 +36,9 @@ class AbstractDatabase { // wont be kept in memory. this.initializedTypes = {} this.whenUserIdSetListener = null - + if (typeof YConcurrency_TestingMode !== 'undefined') { + this.executeOrder = [] + } this.gc1 = [] // first stage this.gc2 = [] // second stage -> after that, remove the op this.gcTimeout = opts.gcTimeout || 5000 @@ -64,6 +66,21 @@ class AbstractDatabase { garbageCollect() } } + addToDebug () { + if (typeof YConcurrency_TestingMode !== 'undefined') { + var command = Array.prototype.map.call(arguments, function (s) { + if (typeof s === 'string') { + return s + } else { + return JSON.stringify(s) + } + }).join('').replace(/"/g, "'").replace(/,/g, ', ').replace(/:/g, ': ') + this.executeOrder.push(command) + } + } + getDebugData () { + console.log(this.executeOrder.join('\n')) + } stopGarbageCollector () { var self = this return new Promise(function (resolve) { @@ -230,6 +247,7 @@ class AbstractDatabase { Actually execute an operation, when all expected operations are available. */ * tryExecute (op) { + this.store.addToDebug('yield* this.store.tryExecute.call(this, ', JSON.stringify(op), ')') if (op.struct === 'Delete') { yield* Y.Struct.Delete.execute.call(this, op) } else if ((yield* this.getOperation(op.id)) == null && !(yield* this.isGarbageCollected(op.id))) { @@ -284,16 +302,14 @@ class AbstractDatabase { } } requestTransaction (makeGen, callImmediately) { - if (!this.transactionInProgress) { + if (callImmediately) { + this.transact(makeGen) + } else if (!this.transactionInProgress) { this.transactionInProgress = true - if (callImmediately) { - this.transact(makeGen) - } else { - var self = this - setTimeout(function () { - self.transact(makeGen) - }, 0) - } + var self = this + setTimeout(function () { + self.transact(makeGen) + }, 0) } else { this.waitingTransactions.push(makeGen) } diff --git a/src/Transaction.js b/src/Transaction.js index 7f147a90..4b2a602b 100644 --- a/src/Transaction.js +++ b/src/Transaction.js @@ -300,6 +300,7 @@ class Transaction { * reset origins of all right ops */ * garbageCollectOperation (id) { + this.store.addToDebug('yield* this.garbageCollectOperation(', id, ')') // check to increase the state of the respective user var state = yield* this.getState(id[0]) if (state.clock === id[1]) { @@ -371,15 +372,22 @@ class Transaction { // remove gc'd op from parent, if it exists var parent = yield* this.getOperation(o.parent) var setParent = false // whether to save parent to the os - if (Y.utils.compareIds(parent.start, o.id)) { - // gc'd op is the start - setParent = true - parent.start = o.right - } - if (Y.utils.compareIds(parent.end, o.id)) { - // gc'd op is the end - setParent = true - parent.end = o.left + if (o.parentSub != null) { + if (Y.utils.compareIds(parent.map[o.parentSub], o.id)) { + setParent = true + parent.map[o.parentSub] = o.right + } + } else { + if (Y.utils.compareIds(parent.start, o.id)) { + // gc'd op is the start + setParent = true + parent.start = o.right + } + if (Y.utils.compareIds(parent.end, o.id)) { + // gc'd op is the end + setParent = true + parent.end = o.left + } } if (setParent) { yield* this.setOperation(parent) @@ -570,9 +578,8 @@ class Transaction { continue } var startPos = startSS[user] || 0 - var endPos = endState.clock - yield* this.os.iterate(this, [user, startPos], [user, endPos], function * (op) { + yield* this.os.iterate(this, [user, startPos], [user, Number.MAX_VALUE], function * (op) { ops.push(op) }) } diff --git a/src/Types/Array.spec.js b/src/Types/Array.spec.js index ebf5a190..b633b085 100644 --- a/src/Types/Array.spec.js +++ b/src/Types/Array.spec.js @@ -1,8 +1,8 @@ /* global createUsers, wait, Y, compareAllUsers, getRandomNumber, applyRandomTransactionsAllRejoinNoGC, applyRandomTransactionsWithGC, async, garbageCollectAllUsers, describeManyTimes */ /* eslint-env browser,jasmine */ -var numberOfYArrayTests = 40 -var repeatArrayTests = 1 +var numberOfYArrayTests = 50 +var repeatArrayTests = 2 describe('Array Type', function () { var y1, y2, y3, yconfig1, yconfig2, yconfig3, flushAll @@ -192,6 +192,59 @@ describe('Array Type', function () { expect(l2.toArray()).toEqual([]) done() })) + it('debug right not existend in Insert.execute', async(function * (done) { + yconfig1.db.requestTransaction(function * () { + var ops = [{'struct':'Map','type':'Map','id':['130',0],'map':{}},{'id':['130',1],'left':null,'right':null,'origin':null,'parent':['_',0],'struct':'Insert','parentSub':'Map','opContent':['130',0]},{'struct':'Map','type':'Map','id':['130',0],'map':{}},{'id':['130',1],'left':null,'right':null,'origin':null,'parent':['_',0],'struct':'Insert','parentSub':'Map','opContent':['130',0]},{'struct':'Map','type':'Map','id':['130',0],'map':{}},{'id':['130',1],'left':null,'right':null,'origin':null,'parent':['_',0],'struct':'Insert','parentSub':'Map','opContent':['130',0]},{'left':null,'right':null,'origin':null,'parent':['130',0],'parentSub':'somekey','struct':'Insert','content':512,'id':['133',0]},{'id':['130',2],'left':null,'right':null,'origin':null,'parent':['130',0],'struct':'Insert','parentSub':'somekey','content':1131},{'id':['130',3],'left':null,'right':['130',2],'origin':null,'parent':['130',0],'struct':'Insert','parentSub':'somekey','content':4196},{'id':['131',3],'left':null,'right':null,'origin':null,'parent':['130',0],'struct':'Insert','parentSub':'somekey','content':5022}]//eslint-disable-line + + for (var o of ops) { + yield* this.store.tryExecute.call(this, o) + } + }) + yield wait() + yield yconfig3.disconnect() + yield yconfig2.disconnect() + yield flushAll() + wait() + yield yconfig3.reconnect() + yield yconfig2.reconnect() + yield wait() + yield flushAll() + done() + })) + it('debug right not existend in Insert.execute (2)', async(function * (done) { + yconfig1.db.requestTransaction(function * () { + yield* this.store.tryExecute.call(this, {'struct': 'Map', 'type': 'Map', 'id': ['153', 0], 'map': {}}) + yield* this.store.tryExecute.call(this, {'id': ['153', 1], 'left': null, 'right': null, 'origin': null, 'parent': ['_', 0], 'struct': 'Insert', 'parentSub': 'Map', 'opContent': ['153', 0]}) + yield* this.store.tryExecute.call(this, {'struct': 'Map', 'type': 'Map', 'id': ['153', 0], 'map': {}}) + yield* this.store.tryExecute.call(this, {'id': ['153', 1], 'left': null, 'right': null, 'origin': null, 'parent': ['_', 0], 'struct': 'Insert', 'parentSub': 'Map', 'opContent': ['153', 0]}) + yield* this.store.tryExecute.call(this, {'struct': 'Map', 'type': 'Map', 'id': ['153', 0], 'map': {}}) + yield* this.store.tryExecute.call(this, {'id': ['153', 1], 'left': null, 'right': null, 'origin': null, 'parent': ['_', 0], 'struct': 'Insert', 'parentSub': 'Map', 'opContent': ['153', 0]}) + yield* this.store.tryExecute.call(this, {'left': null, 'right': null, 'origin': null, 'parent': ['153', 0], 'parentSub': 'somekey', 'struct': 'Insert', 'content': 3784, 'id': ['154', 0]}) + yield* this.store.tryExecute.call(this, {'left': null, 'right': ['154', 0], 'origin': null, 'parent': ['153', 0], 'parentSub': 'somekey', 'struct': 'Insert', 'content': 8217, 'id': ['154', 1]}) + yield* this.store.tryExecute.call(this, {'left': null, 'right': ['154', 1], 'origin': null, 'parent': ['153', 0], 'parentSub': 'somekey', 'struct': 'Insert', 'content': 5036, 'id': ['154', 2]}) + yield* this.store.tryExecute.call(this, {'id': ['153', 2], 'left': null, 'right': null, 'origin': null, 'parent': ['153', 0], 'struct': 'Insert', 'parentSub': 'somekey', 'content': 417}) + yield* this.store.tryExecute.call(this, {'id': ['155', 0], 'left': null, 'right': null, 'origin': null, 'parent': ['153', 0], 'struct': 'Insert', 'parentSub': 'somekey', 'content': 2202}) + yield* this.garbageCollectOperation(['153', 2]) + yield* this.garbageCollectOperation(['154', 0]) + yield* this.garbageCollectOperation(['154', 1]) + yield* this.garbageCollectOperation(['154', 2]) + yield* this.garbageCollectOperation(['155', 0]) + yield* this.garbageCollectOperation(['156', 0]) + yield* this.garbageCollectOperation(['157', 0]) + yield* this.garbageCollectOperation(['157', 1]) + yield* this.store.tryExecute.call(this, {'id': ['153', 3], 'left': null, 'right': null, 'origin': null, 'parent': ['153', 0], 'struct': 'Insert', 'parentSub': 'somekey', 'content': 4372}) + }) + yield wait() + yield yconfig3.disconnect() + yield yconfig2.disconnect() + yield flushAll() + wait() + yield yconfig3.reconnect() + yield yconfig2.reconnect() + yield wait() + yield flushAll() + done() + })) }) describeManyTimes(repeatArrayTests, `Random tests`, function () { var randomArrayTransactions = [ diff --git a/src/Types/Map.spec.js b/src/Types/Map.spec.js index 245292d6..93abf875 100644 --- a/src/Types/Map.spec.js +++ b/src/Types/Map.spec.js @@ -2,7 +2,7 @@ /* eslint-env browser,jasmine */ var numberOfYMapTests = 40 -var repeatMapTeasts = 1 +var repeatMapTeasts = 2 describe('Map Type', function () { var y1, y2, y3, y4, flushAll -- GitLab