diff --git a/gulpfile.helper.js b/gulpfile.helper.js index 2e1ea71f..58ae80f3 100644 --- a/gulpfile.helper.js +++ b/gulpfile.helper.js @@ -94,7 +94,7 @@ module.exports = function (gulp, helperOptions) { return browserify({ entries: files.specs, - debug: options.debug + debug: true }).bundle() .pipe(source('specs.js')) .pipe(buffer()) diff --git a/gulpfile.js b/gulpfile.js index b4eee461..668f2f07 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -54,7 +54,7 @@ require('./gulpfile.helper.js')(gulp, { moduleName: 'yjs', includeRuntime: true, specs: [ - './src/Types/Map.spec.js', + '../y-array/src/Array.spec.js', './src/Database.spec.js' ] }) diff --git a/src/Database.js b/src/Database.js index 324f29c3..0dd2972c 100644 --- a/src/Database.js +++ b/src/Database.js @@ -39,6 +39,13 @@ module.exports = function (Y /* :any */) { */ constructor (y, opts) { this.y = y + var os = this + this.userId = null + var resolve + this.userIdPromise = new Promise(function (r) { + resolve = r + }) + this.userIdPromise.resolve = resolve // whether to broadcast all applied operations (insert & delete hook) this.forwardAppliedOperations = false // E.g. this.listenersById[id] : Array @@ -60,16 +67,15 @@ module.exports = function (Y /* :any */) { // TODO: Use ES7 Weak Maps. This way types that are no longer user, // wont be kept in memory. this.initializedTypes = {} - this.whenUserIdSetListener = null this.waitingTransactions = [] this.transactionInProgress = false + this.transactionIsFlushed = false 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 - var os = this function garbageCollect () { return os.whenTransactionsFinished().then(function () { if (os.gc1.length > 0 || os.gc2.length > 0) { @@ -175,26 +181,20 @@ module.exports = function (Y /* :any */) { this.gcInterval = null } setUserId (userId) { - var self = this - return new Promise(function (resolve) { + if (!this.userIdPromise.inProgress) { + this.userIdPromise.inProgress = true + var self = this self.requestTransaction(function * () { self.userId = userId var state = yield* this.getState(userId) self.opClock = state.clock - if (self.whenUserIdSetListener != null) { - self.whenUserIdSetListener() - self.whenUserIdSetListener = null - } - resolve() + self.userIdPromise.resolve(userId) }) - }) + } + return this.userIdPromise } whenUserIdSet (f) { - if (this.userId != null) { - f() - } else { - this.whenUserIdSetListener = f - } + this.userIdPromise.then(f) } getNextOpId () { if (this._nextUserId != null) { @@ -390,15 +390,26 @@ module.exports = function (Y /* :any */) { return Promise.resolve() } } + // Check if there is another transaction request. + // * the last transaction is always a flush :) getNextRequest () { if (this.waitingTransactions.length === 0) { - this.transactionInProgress = false - if (this.transactionsFinished != null) { - this.transactionsFinished.resolve() - this.transactionsFinished = null + if (this.transactionIsFlushed) { + this.transactionInProgress = false + this.transactionIsFlushed = false + if (this.transactionsFinished != null) { + this.transactionsFinished.resolve() + this.transactionsFinished = null + } + return null + } else { + this.transactionIsFlushed = true + return function * () { + yield* this.flush() + } } - return null } else { + this.transactionIsFlushed = false return this.waitingTransactions.shift() } } @@ -406,7 +417,7 @@ module.exports = function (Y /* :any */) { this.waitingTransactions.push(makeGen) if (!this.transactionInProgress) { this.transactionInProgress = true - if (true || callImmediately) { // TODO: decide whether this is ok or not.. + if (false || callImmediately) { // TODO: decide whether this is ok or not.. this.transact(this.getNextRequest()) } else { var self = this diff --git a/src/SpecHelper.js b/src/SpecHelper.js index baa9d586..4097b7a7 100644 --- a/src/SpecHelper.js +++ b/src/SpecHelper.js @@ -25,7 +25,7 @@ g.g = g g.YConcurrency_TestingMode = true -jasmine.DEFAULT_TIMEOUT_INTERVAL = 8000 +jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000 g.describeManyTimes = function describeManyTimes (times, name, f) { for (var i = 0; i < times; i++) { diff --git a/src/Transaction.js b/src/Transaction.js index da82223c..45d2e8d6 100644 --- a/src/Transaction.js +++ b/src/Transaction.js @@ -763,6 +763,11 @@ module.exports = function (Y/* :any */) { return op } */ + * flush () { + yield* this.os.flush() + yield* this.ss.flush() + yield* this.ds.flush() + } } Y.Transaction = TransactionInterface } diff --git a/src/Utils.js b/src/Utils.js index 03554f64..e6b1bdf6 100644 --- a/src/Utils.js +++ b/src/Utils.js @@ -207,7 +207,7 @@ module.exports = function (Y /* : any*/) { Defines a smaller relation on Id's */ function smaller (a, b) { - return a[0] < b[0] || (a[0] === b[0] && a[1] < b[1]) + return a[0] < b[0] || (a[0] === b[0] && (a[1] < b[1] || typeof a[1] < typeof b[1])) } Y.utils.smaller = smaller @@ -249,8 +249,8 @@ module.exports = function (Y /* : any*/) { I tried to optimize this for performance, therefore no highlevel operations. */ class SmallLookupBuffer extends Store { - constructor (read, write) { - super() + constructor () { + super(...arguments) this.writeBuffer = createEmptyOpsArray(5) this.readBuffer = createEmptyOpsArray(10) } diff --git a/src/y.js b/src/y.js index 7f7d2485..3718f311 100644 --- a/src/y.js +++ b/src/y.js @@ -110,9 +110,9 @@ class YConfig { share: {[key: string]: any}; */ constructor (opts, callback) { + this.options = opts this.db = new Y[opts.db.name](this, opts.db) this.connector = new Y[opts.connector.name](this, opts.connector) - this.options = opts } init (callback) { var opts = this.options @@ -131,7 +131,8 @@ class YConfig { } share[propertyname] = yield* this.getType(id) } - setTimeout(callback, 0) + this.store.whenTransactionsFinished() + .then(callback) }) } isConnected () {