[Pkg-javascript-commits] [node-leveldown] 71/492: cleanup & complete & add tests for deferred ops
Andrew Kelley
andrewrk-guest at moszumanska.debian.org
Sun Jul 6 17:13:46 UTC 2014
This is an automated email from the git hooks/post-receive script.
andrewrk-guest pushed a commit to annotated tag rocksdb-0.10.1
in repository node-leveldown.
commit a56a380ad177ada9dc8ec8d6c40a676e1b2844ea
Author: Rod Vagg <rod at vagg.org>
Date: Sun Nov 18 12:13:10 2012 +1100
cleanup & complete & add tests for deferred ops
---
lib/levelup.js | 111 ++++++++++++++++++++++++++++++++-------------
test/common.js | 1 +
test/deferred-open-test.js | 98 +++++++++++++++++++++++++++++++++++++++
test/read-stream-test.js | 36 +++++++++++++++
test/simple-test.js | 2 +-
5 files changed, 216 insertions(+), 32 deletions(-)
diff --git a/lib/levelup.js b/lib/levelup.js
index 6216f9e..dd418d6 100644
--- a/lib/levelup.js
+++ b/lib/levelup.js
@@ -36,17 +36,25 @@ var bridge = require('bindings')('levelup.node')
return callback_
}
+// Possible this._status values:
+// - 'new' - newly created, not opened or closed
+// - 'opening' - waiting for the database to be opened, post open()
+// - 'open' - successfully opened the database, available for use
+// - 'closing' - waiting for the database to be closed, post close()
+// - 'closed' - database has been successfully closed, should not be used
+// except for another open() operation
+
function LevelUP (location, options) {
this.__proto__.__proto__ = EventEmitter.prototype
EventEmitter.call(this)
this._options = extend(extend({}, defaultOptions), options)
this._location = location
- this.status = "new"
+ this._status = 'new'
}
LevelUP.prototype = {
open: function (callback) {
- this.status = "opening"
+ this._status = 'opening'
var execute = function () {
var db = bridge.createDatabase()
db.open(this._location, this._options, function (err) {
@@ -57,7 +65,7 @@ LevelUP.prototype = {
this.emit('error', err)
} else {
this._db = db
- this.status = "open"
+ this._status = 'open'
callback && callback(null, this)
this.emit('ready')
}
@@ -73,19 +81,19 @@ LevelUP.prototype = {
//TODO: we can crash Node by submitting an operation between close() and the actual closing of the database
, close: function (callback) {
if (this.isOpen()) {
- this.status = "closing"
+ this._status = 'closing'
this._db.close(function () {
- this.status = "closed"
+ this._status = 'closed'
this.emit('closed')
callback.apply(null, arguments)
}.bind(this))
this._db = null
- } else if (this.status === "closed") {
+ } else if (this._status == 'closed') {
callback()
- } else if (this.status === "closing") {
- this.on("closed", callback)
- } else if (this.status === "opening") {
- this.on("ready", function () {
+ } else if (this._status == 'closing') {
+ this.on('closed', callback)
+ } else if (this._status == 'opening') {
+ this.on('ready', function () {
this.close(callback)
})
} else {
@@ -97,12 +105,27 @@ LevelUP.prototype = {
}
, isOpen: function () {
- return this.status === "open"
+ return this._status == 'open'
+ }
+
+ // in between these two there is 'new' and 'opening'
+
+ , isClosed: function () {
+ // covers 'closing' and 'closed'
+ return (/^clos/).test(this._status)
}
, get: function (key_, options_, callback_) {
- var callback = getCallback(options_, callback_)
- , options, key, err
+ var callback, options, key, err
+
+ if (!this.isOpen() && !this.isClosed()) {
+ // limbo, defer the operation
+ return this.once('ready', function () {
+ this.get(key_, options_, callback_)
+ })
+ }
+
+ callback = getCallback(options_, callback_)
if (this.isOpen()) {
options = getOptions(options_, this._options)
@@ -117,15 +140,24 @@ LevelUP.prototype = {
callback && callback(null, toEncoding(value, options.valueEncoding || options.encoding), key_)
})
} else {
- this.once("ready", function () {
- this.get(key_, options_, callback_)
- })
+ err = new errors.ReadError('Database is not open')
+ if (callback)
+ return callback(err)
+ throw err
}
}
, put: function (key, value, options_, callback_) {
- var callback = getCallback(options_, callback_)
- , options, err
+ var callback, options, err
+
+ if (!this.isOpen() && !this.isClosed()) {
+ // limbo, defer the operation
+ return this.once('ready', function () {
+ this.put(key, value, options_, callback_)
+ })
+ }
+
+ callback = getCallback(options_, callback_)
if (this.isOpen()) {
options = getOptions(options_, this._options)
@@ -144,15 +176,24 @@ LevelUP.prototype = {
}
}.bind(this))
} else {
- this.once("ready", function () {
- this.put(key, value, options_, callback_)
- })
+ err = new errors.WriteError('Database is not open')
+ if (callback)
+ return callback(err)
+ throw err
}
}
, del: function (key, options_, callback_) {
- var callback = getCallback(options_, callback_)
- , options, err
+ var callback, options, err
+
+ if (!this.isOpen() && !this.isClosed()) {
+ // limbo, defer the operation
+ return this.once('ready', function () {
+ this.del(key, options_, callback_)
+ })
+ }
+
+ callback = getCallback(options_, callback_)
if (this.isOpen()) {
options = getOptions(options_, this._options)
@@ -169,23 +210,31 @@ LevelUP.prototype = {
}
}.bind(this))
} else {
- this.once("ready", function () {
- this.del(key, options_, callback_)
- })
+ err = new errors.WriteError('Database is not open')
+ if (callback)
+ return callback(err)
+ throw err
}
}
, batch: function (arr, options_, callback_) {
- var callback = getCallback(options_, callback_)
- , empty = {}
- , options, keyEncoding, valueEncoding, err
+ var callback, options, keyEncoding, valueEncoding, err
if (!this.isOpen()) {
- return this.once("ready", function () {
+ return this.once('ready', function () {
this.batch(arr, options_, callback_)
})
}
+ callback = getCallback(options_, callback_)
+
+ if (this.isClosed()) {
+ err = new errors.WriteError('Database is not open')
+ if (callback)
+ return callback(err)
+ throw err
+ }
+
options = getOptions(options_, this._options)
keyEncoding = options.keyEncoding || options.encoding
valueEncoding = options.valueEncoding || options.encoding
@@ -199,7 +248,7 @@ LevelUP.prototype = {
o.value = toBuffer(e.value, valueEncoding)
return o
}
- return empty
+ return {}
})
this._db.batch(arr, options, function (err) {
if (err) {
diff --git a/test/common.js b/test/common.js
index 527609a..d75b098 100644
--- a/test/common.js
+++ b/test/common.js
@@ -39,6 +39,7 @@ module.exports.openTestDatabase = function () {
var options = typeof arguments[0] == 'object' ? arguments[0] : { createIfMissing: true, errorIfExists: true }
, callback = typeof arguments[0] == 'function' ? arguments[0] : arguments[1]
, location = typeof arguments[0] == 'string' ? arguments[0] : module.exports.nextLocation()
+
rimraf(location, function (err) {
refute(err)
this.cleanupDirs.push(location)
diff --git a/test/deferred-open-test.js b/test/deferred-open-test.js
new file mode 100644
index 0000000..98f05c5
--- /dev/null
+++ b/test/deferred-open-test.js
@@ -0,0 +1,98 @@
+/* Copyright (c) 2012 Rod Vagg <@rvagg> */
+
+var buster = require('buster')
+ , assert = buster.assert
+ , levelup = require('../lib/levelup.js')
+ , async = require('async')
+ , common = require('./common')
+
+buster.testCase('Deferred open()', {
+ 'setUp': common.commonSetUp
+ , 'tearDown': common.commonTearDown
+
+ , 'put() and get() on pre-opened database': function (done) {
+ var location = common.nextLocation()
+ // 1) open database without callback, opens in worker thread
+ , db = levelup(location, { createIfMissing: true, errorIfExists: true, encoding: 'utf8' })
+
+ this.closeableDatabases.push(db)
+ this.cleanupDirs.push(location)
+ assert.isObject(db)
+ assert.equals(db._location, location)
+
+ async.parallel([
+ // 2) insert 3 values with put(), these should be deferred until the database is actually open
+ db.put.bind(db, 'k1', 'v1')
+ , db.put.bind(db, 'k2', 'v2')
+ , db.put.bind(db, 'k3', 'v3')
+ ], function () {
+ // 3) when the callbacks have returned, the database should be open and those values should be in
+ // verify that the values are there
+ async.forEach(
+ [1,2,3]
+ , function (k, cb) {
+ db.get('k' + k, function (err, v) {
+ refute(err)
+ assert.equals(v, 'v' + k)
+ cb()
+ })
+ }
+ // sanity, this shouldn't exist
+ , function () {
+ db.get('k4', function (err) {
+ assert(err)
+ // DONE
+ done()
+ })
+ }
+ )
+ })
+
+ // we should still be in a state of limbo down here, not opened or closed, but 'new'
+ refute(db.isOpen())
+ refute(db.isClosed())
+ }
+
+ , 'batch() on pre-opened database': function (done) {
+ var location = common.nextLocation()
+ // 1) open database without callback, opens in worker thread
+ , db = levelup(location, { createIfMissing: true, errorIfExists: true, encoding: 'utf8' })
+
+ this.closeableDatabases.push(db)
+ this.cleanupDirs.push(location)
+ assert.isObject(db)
+ assert.equals(db._location, location)
+
+ // 2) insert 3 values with batch(), these should be deferred until the database is actually open
+ db.batch([
+ { type: 'put', key: 'k1', value: 'v1' }
+ , { type: 'put', key: 'k2', value: 'v2' }
+ , { type: 'put', key: 'k3', value: 'v3' }
+ ], function () {
+ // 3) when the callbacks have returned, the database should be open and those values should be in
+ // verify that the values are there
+ async.forEach(
+ [1,2,3]
+ , function (k, cb) {
+ db.get('k' + k, function (err, v) {
+ refute(err)
+ assert.equals(v, 'v' + k)
+ cb()
+ })
+ }
+ // sanity, this shouldn't exist
+ , function () {
+ db.get('k4', function (err) {
+ assert(err)
+ // DONE
+ done()
+ })
+ }
+ )
+ })
+
+ // we should still be in a state of limbo down here, not opened or closed, but 'new'
+ refute(db.isOpen())
+ refute(db.isClosed())
+ }
+})
\ No newline at end of file
diff --git a/test/read-stream-test.js b/test/read-stream-test.js
index 3b06d6c..87a671d 100644
--- a/test/read-stream-test.js
+++ b/test/read-stream-test.js
@@ -560,4 +560,40 @@ buster.testCase('ReadStream', {
setup(delayed.delayed(reopen, 0.05))
}
+
+
+ // this is just a fancy way of testing levelup('/path').readStream()
+ // i.e. not waiting for 'open' to complete
+ // the logic for this is inside the ReadStream constructor which waits for 'ready'
+ , 'test ReadStream on pre-opened db': function (done) {
+ var execute = function (db) {
+ // is in limbo
+ refute(db.isOpen())
+ refute(db.isClosed())
+
+ var rs = db.readStream()
+ assert.isFalse(rs.writable)
+ assert.isTrue(rs.readable)
+ rs.on('ready', this.readySpy)
+ rs.on('data' , this.dataSpy)
+ rs.on('data' , function () { console.log('data', arguments) })
+ rs.on('end' , this.endSpy)
+ rs.on('close', this.verify.bind(this, rs, done))
+ }.bind(this)
+ , setup = function (db) {
+ console.log('opened')
+ db.batch(this.sourceData.slice(), function (err) {
+ console.log('batching',err)
+ refute(err)
+ db.close(function (err) {
+ console.log('closed', err)
+ refute(err)
+ var db2 = levelup(db._location, { createIfMissing: false, errorIfExists: false, encoding: 'utf8' })
+ execute(db2)
+ })
+ }.bind(this))
+ }.bind(this)
+
+ this.openTestDatabase(setup)
+ }
})
\ No newline at end of file
diff --git a/test/simple-test.js b/test/simple-test.js
index 2b33d2d..a371ec3 100644
--- a/test/simple-test.js
+++ b/test/simple-test.js
@@ -20,7 +20,7 @@ buster.testCase('Basic API', {
, 'default options': function (done) {
var location = common.nextLocation()
- var db = levelup(location, { createIfMissing: true, errorIfExists: true }, function (err, db) {
+ levelup(location, { createIfMissing: true, errorIfExists: true }, function (err, db) {
assert.isTrue(db.isOpen())
this.closeableDatabases.push(db)
this.cleanupDirs.push(location)
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-leveldown.git
More information about the Pkg-javascript-commits
mailing list