fixed DS bugs (i guess..) now handling more complicated scenarios

This commit is contained in:
Kevin Jahns 2015-10-09 16:09:00 +02:00
parent 6a13419c62
commit aadef59934
7 changed files with 67 additions and 57 deletions

View File

@ -90,7 +90,7 @@ class AbstractConnector {
// Execute a function _when_ we are connected. // Execute a function _when_ we are connected.
// If not connected, wait until connected // If not connected, wait until connected
whenSynced (f) { whenSynced (f) {
if (this.isSynced === true) { if (this.isSynced) {
f() f()
} else { } else {
this.whenSyncedListeners.push(f) this.whenSyncedListeners.push(f)
@ -125,6 +125,7 @@ class AbstractConnector {
}) })
} else { } else {
this.isSynced = true this.isSynced = true
// call when synced listeners
for (var f of this.whenSyncedListeners) { for (var f of this.whenSyncedListeners) {
f() f()
} }
@ -171,13 +172,13 @@ class AbstractConnector {
conn.send(sender, { conn.send(sender, {
type: 'sync done' type: 'sync done'
}) })
conn._setSyncedWith(sender)
}, conn.syncingClientDuration) }, conn.syncingClientDuration)
} else { } else {
conn.send(sender, { conn.send(sender, {
type: 'sync done' type: 'sync done'
}) })
} }
conn._setSyncedWith(sender)
}) })
} else if (m.type === 'sync step 2') { } else if (m.type === 'sync step 2') {
let conn = this let conn = this

View File

@ -36,7 +36,7 @@ function wait (t) {
return new Promise(function (resolve) { return new Promise(function (resolve) {
setTimeout(function () { setTimeout(function () {
resolve() resolve()
}, t * 7) }, t)
}) })
} }
g.wait = wait g.wait = wait
@ -109,6 +109,7 @@ g.applyRandomTransactions = async(function * applyRandomTransactions (users, obj
}) })
g.garbageCollectAllUsers = async(function * garbageCollectAllUsers (users) { g.garbageCollectAllUsers = async(function * garbageCollectAllUsers (users) {
return yield wait(100)// TODO!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
for (var i in users) { for (var i in users) {
yield users[i].db.garbageCollect() yield users[i].db.garbageCollect()
yield users[i].db.garbageCollect() yield users[i].db.garbageCollect()

View File

@ -41,6 +41,7 @@
* addOperation(op) * addOperation(op)
- add an operation to the database. - add an operation to the database.
This may only be called once for every op.id This may only be called once for every op.id
Must return a function that returns the next operation in the database (ordered by id)
* getOperation(id) * getOperation(id)
* removeOperation(id) * removeOperation(id)
- remove an operation from the database. This is called when an operation - remove an operation from the database. This is called when an operation
@ -105,9 +106,9 @@ class AbstractTransaction {
for (var i = 0; i < ops.length; i++) { for (var i = 0; i < ops.length; i++) {
var op = ops[i] var op = ops[i]
yield* this.store.tryExecute.call(this, op) yield* this.store.tryExecute.call(this, op)
send.push(Y.utils.copyObject(Y.Struct[op.struct].encode(op))) send.push(Y.Struct[op.struct].encode(op))
} }
if (this.store.y.connector.broadcastedHB) { if (!this.store.y.connector.isDisconnected()) {
this.store.y.connector.broadcast({ this.store.y.connector.broadcast({
type: 'update', type: 'update',
ops: send ops: send
@ -246,7 +247,6 @@ class AbstractOperationStore {
// wont be kept in memory. // wont be kept in memory.
this.initializedTypes = {} this.initializedTypes = {}
this.whenUserIdSetListener = null this.whenUserIdSetListener = null
this.waitingOperations = new Y.utils.RBTree()
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
@ -441,56 +441,39 @@ class AbstractOperationStore {
} }
/* /*
Actually execute an operation, when all expected operations are available. Actually execute an operation, when all expected operations are available.
If op is not yet expected, add it to the list of waiting operations.
This will also try to execute waiting operations
(ops that were not expected yet), after it was applied
*/ */
* tryExecute (op) { * tryExecute (op) {
if (op.struct === 'Delete') { if (op.struct === 'Delete') {
yield* Y.Struct.Delete.execute.call(this, op) yield* Y.Struct.Delete.execute.call(this, op)
} else { } else if ((yield* this.getOperation(op.id)) == null) {
while (op != null) { yield* Y.Struct[op.struct].execute.call(this, op)
var state = yield* this.getState(op.id[0]) var next = yield* this.addOperation(op)
if (op.id[1] === state.clock) { yield* this.store.operationAdded(this, op, next)
// either its a new operation (1. case), or it is an operation that was deleted, but is not yet in the OS
if (op.id[1] === state.clock) {
state.clock++
yield* this.checkDeleteStoreForState(state)
yield* this.setState(state)
}
yield* Y.Struct[op.struct].execute.call(this, op) // Delete if DS says this is actually deleted
yield* this.addOperation(op) if (this.store.ds.isDeleted(op.id)) {
yield* this.store.operationAdded(this, op) yield* Y.Struct['Delete'].execute.call(this, {struct: 'Delete', target: op.id})
// Delete if DS says this is actually deleted
if (this.store.ds.isDeleted(op.id)) {
yield* Y.Struct['Delete'].execute.call(this, {struct: 'Delete', target: op.id})
}
// find next operation to execute
op = this.store.waitingOperations.find([op.id[0], state.clock])
if (op != null) {
this.store.waitingOperations.delete([op.id[0], state.clock])
}
} else {
if (op.id[1] > state.clock) {
// has to be executed at some point later
this.store.waitingOperations.add(op)
}
op = null
}
} }
} }
} }
// called by a transaction when an operation is added // called by a transaction when an operation is added
* operationAdded (transaction, op) { * operationAdded (transaction, op, next) {
// increase SS
var o = op
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 = next()
}
yield* transaction.setState(state)
// notify whenOperation listeners (by id)
var sid = JSON.stringify(op.id) var sid = JSON.stringify(op.id)
var l = this.listenersById[sid] var l = this.listenersById[sid]
delete this.listenersById[sid] delete this.listenersById[sid]
// notify whenOperation listeners (by id)
if (l != null) { if (l != null) {
for (var key in l) { for (var key in l) {
var listener = l[key] var listener = l[key]

View File

@ -186,7 +186,7 @@ Y.Memory = (function () {
if (d[2] && !n.gc) { if (d[2] && !n.gc) {
// d marks as gc'd but n does not // d marks as gc'd but n does not
// then delete either way // then delete either way
createDeletions(user, d[0], diff, d[2]) createDeletions(user, d[0], Math.min(diff, d[1]), d[2])
} }
} }
if (d[1] <= diff) { if (d[1] <= diff) {
@ -228,7 +228,15 @@ Y.Memory = (function () {
return op return op
} }
* addOperation (op) { * addOperation (op) {
this.os.add(op) var n = this.os.add(op)
return function () {
if (n != null) {
n = n.next()
return n != null ? n.val : null
} else {
return null
}
}
} }
* getOperation (id) { * getOperation (id) {
return this.os.find(id) return this.os.find(id)
@ -286,12 +294,14 @@ Y.Memory = (function () {
var res = [] var res = []
for (var op of ops) { for (var op of ops) {
res.push(yield* this.makeOperationReady(startSS, op)) res.push(yield* this.makeOperationReady(startSS, op))
/*
var state = startSS[op.id[0]] || 0 var state = startSS[op.id[0]] || 0
if ((state === op.id[1]) || true) { if ((state === op.id[1]) || true) {
startSS[op.id[0]] = state + 1 startSS[op.id[0]] = op.id[1] + 1
} else { } else {
throw new Error('Unexpected operation!') throw new Error('Unexpected operation!')
} }
*/
} }
return res return res
} }
@ -300,15 +310,17 @@ Y.Memory = (function () {
// instead of ss, you could use currSS (a ss that increments when you add an operation) // instead of ss, you could use currSS (a ss that increments when you add an operation)
op = Y.utils.copyObject(op) op = Y.utils.copyObject(op)
var o = op var o = op
while (o.right != null) { while (o.right != null) {
// while unknown, go to the right // while unknown, go to the right
if (o.right[1] < (ss[o.right[0]] || 0)) { if (o.right[1] < (ss[o.right[0]] || 0)) { // && !Y.utils.compareIds(op.id, o.origin)
break break
} }
o = yield* this.getOperation(o.right) o = yield* this.getOperation(o.right)
} }
// new right is not gc'd and known according to the ss // new right is known according to the ss
op.right = o.right op.right = o.right
/*
while (o.left != null) { while (o.left != null) {
// while unknown, go to the right // while unknown, go to the right
if (o.left[1] < (ss[o.left[0]] || 0)) { if (o.left[1] < (ss[o.left[0]] || 0)) {
@ -316,8 +328,9 @@ Y.Memory = (function () {
} }
o = yield* this.getOperation(o.left) o = yield* this.getOperation(o.left)
} }
// new left is not gc'd and known according to the ss // new left is known according to the ss
op.left = o.left op.left = o.left
*/
return op return op
} }
} }

View File

@ -1,4 +1,4 @@
/* global Y */ /* global Y, async */
/* eslint-env browser,jasmine,console */ /* eslint-env browser,jasmine,console */
describe('Memory', function () { describe('Memory', function () {
@ -96,5 +96,20 @@ describe('Memory', function () {
done() done()
}) })
})) }))
it('Debug #6', async(function * (done) {
var store = new Y.Memory(null, {
db: {
name: 'Memory',
gcTimeout: -1
}
})
store.requestTransaction(function * () {
yield* this.applyDeleteSet({'40': [[0, 3, false]]})
expect(this.ds.toDeleteSet()).toEqual({'40': [[0, 3, false]]})
yield* this.applyDeleteSet({'39': [[2, 2, false]], '40': [[0, 1, true], [1, 2, false]], '41': [[2, 1, false]]})
expect(this.ds.toDeleteSet()).toEqual({'39': [[2, 2, false]], '40': [[0, 1, true], [1, 2, false]], '41': [[2, 1, false]]})
done()
})
}))
}) })
}) })

View File

@ -80,6 +80,9 @@ var Struct = {
if (op.right != null) { if (op.right != null) {
ids.push(op.right) ids.push(op.right)
} }
if (op.origin != null && !Y.utils.compareIds(op.left, op.origin)) {
ids.push(op.origin)
}
// if (op.right == null && op.left == null) { // if (op.right == null && op.left == null) {
ids.push(op.parent) ids.push(op.parent)
@ -175,12 +178,6 @@ var Struct = {
op.right = left.right op.right = left.right
left.right = op.id left.right = op.id
/*/ if left exists, and it is supposed to be gc'd. Remove it from the gc
if (left.gc != null) {
this.store.removeFromGarbageCollector(left)
}
*/
yield* this.setOperation(left) yield* this.setOperation(left)
} else { } else {
op.right = op.parentSub ? parent.map[op.parentSub] || null : parent.start op.right = op.parentSub ? parent.map[op.parentSub] || null : parent.start

View File

@ -1,7 +1,7 @@
/* global createUsers, wait, Y, compareAllUsers, getRandomNumber, applyRandomTransactions, async, garbageCollectAllUsers, describeManyTimes */ /* global createUsers, wait, Y, compareAllUsers, getRandomNumber, applyRandomTransactions, async, garbageCollectAllUsers, describeManyTimes */
/* eslint-env browser,jasmine */ /* eslint-env browser,jasmine */
var numberOfYArrayTests = 20 var numberOfYArrayTests = 10
var repeatArrayTests = 1000 var repeatArrayTests = 1000
describe('Array Type', function () { describe('Array Type', function () {