Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Breaking: parent db must support deferredOpen #89

Merged
merged 1 commit into from
Apr 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 72 additions & 18 deletions leveldown.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ function SubIterator (db, ite, prefix) {
inherits(SubIterator, abstract.AbstractIterator)

SubIterator.prototype._next = function (cb) {
if (maybeError(this.db.leveldown, cb)) return

var self = this
this.iterator.next(function (err, key, value) {
if (err) return cb(err)
Expand All @@ -39,6 +41,7 @@ SubIterator.prototype._seek = function (key) {
}

SubIterator.prototype._end = function (cb) {
if (maybeError(this.db.leveldown, cb)) return
this.iterator.end(cb)
}

Expand All @@ -64,12 +67,39 @@ function SubDown (db, prefix, opts) {
})

this.db = db
this.leveldown = null
this.ownPrefix = separator + prefix + separator
this.prefix = this.ownPrefix
this.prefix = separator + prefix + separator
this._beforeOpen = opts.open

var self = this
var manifest = db.supports || {}

// The parent db must open itself or be (re)opened by the user because a
// sublevel can't (shouldn't) initiate state changes on the rest of the db.
if (!manifest.deferredOpen && !reachdown.is(db, 'levelup')) {
throw new Error('Parent database must support deferredOpen')
}

var subdb = reachdown(db, 'subleveldown')

if (subdb) {
// Old subleveldown doesn't know its prefix and leveldown until opened
if (!subdb.prefix || !subdb.leveldown) {
throw new Error('Incompatible with subleveldown < 5.0.0')
}

this.prefix = subdb.prefix + this.prefix
this.leveldown = subdb.leveldown
} else {
this.leveldown = reachdown(db, matchdown, false)
}

if (reachdown.is(this.leveldown, 'deferred-leveldown')) {
// Old deferred-leveldown doesn't expose its underlying db until opened
throw new Error('Incompatible with deferred-leveldown < 2.0.0')
} else if (!this.leveldown.status) {
// Old abstract-leveldown doesn't have a status property
throw new Error('Incompatible with abstract-leveldown < 2.4.0')
}

this._wrap = {
gt: function (x) {
Expand All @@ -91,47 +121,55 @@ inherits(SubDown, abstract.AbstractLevelDOWN)

SubDown.prototype.type = 'subleveldown'

// TODO: remove _open() once abstract-leveldown supports deferredOpen,
// because that means we can always do operations on this.leveldown.
// Alternatively have the sublevel follow the open state of this.db.
SubDown.prototype._open = function (opts, cb) {
var self = this

this.db.open(function (err) {
if (err) return cb(err)
// TODO: make _isOpening public in levelup or add a method like
// ready(cb) which waits for - but does not initiate - a state change.
var m = typeof this.db.isOpening === 'function' ? 'isOpening' : '_isOpening'

var subdb = reachdown(self.db, 'subleveldown')
if (this.db[m]()) {
this.db.once('open', onopen)
} else {
this._nextTick(onopen)
}

if (subdb && subdb.prefix) {
self.prefix = subdb.prefix + self.ownPrefix
self.leveldown = subdb.leveldown
} else {
self.leveldown = reachdown(self.db, matchdown, false)
}
function onopen () {
if (!self.db.isOpen()) return cb(new Error('Parent database is not open'))
if (self.leveldown.status !== 'open') return cb(new Error('Inner database is not open'))

if (self._beforeOpen) self._beforeOpen(cb)
else cb()
})
}
// TODO: add hooks to abstract-leveldown
if (self._beforeOpen) return self._beforeOpen(cb)

SubDown.prototype._close = function (cb) {
this.leveldown.close(cb)
cb()
}
}

SubDown.prototype._serializeKey = function (key) {
return Buffer.isBuffer(key) ? key : String(key)
}

SubDown.prototype._put = function (key, value, opts, cb) {
if (maybeError(this.leveldown, cb)) return
this.leveldown.put(concat(this.prefix, key), value, opts, cb)
}

SubDown.prototype._get = function (key, opts, cb) {
if (maybeError(this.leveldown, cb)) return
this.leveldown.get(concat(this.prefix, key), opts, cb)
}

SubDown.prototype._del = function (key, opts, cb) {
if (maybeError(this.leveldown, cb)) return
this.leveldown.del(concat(this.prefix, key), opts, cb)
}

SubDown.prototype._batch = function (operations, opts, cb) {
if (maybeError(this.leveldown, cb)) return

// No need to make a copy of the array, abstract-leveldown does that
for (var i = 0; i < operations.length; i++) {
operations[i].key = concat(this.prefix, operations[i].key)
Expand All @@ -141,6 +179,8 @@ SubDown.prototype._batch = function (operations, opts, cb) {
}

SubDown.prototype._clear = function (opts, cb) {
if (maybeError(this.leveldown, cb)) return

if (typeof this.leveldown.clear === 'function') {
// Prefer optimized implementation of clear()
opts = addRestOptions(wrap(opts, this._wrap), opts)
Expand Down Expand Up @@ -169,6 +209,20 @@ function isEmptyBuffer (key) {
return Buffer.isBuffer(key) && key.length === 0
}

// Before any operation, check if the inner db is open. Needed
// because we don't follow open state of the parent db atm.
// TODO: move to abstract-leveldown
function maybeError (leveldown, callback) {
if (leveldown.status !== 'open') {
// Same error message as levelup
// TODO: use require('level-errors').ReadError
process.nextTick(callback, new Error('Database is not open'))
return true
}

return false
}

// TODO (refactor): use addRestOptions instead
function extend (xopts, opts) {
xopts.keys = opts.keys
Expand Down
1 change: 1 addition & 0 deletions matchdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module.exports = function matchdown (db, type) {
if (type === 'levelup') return false
if (type === 'encoding-down') return false
if (type === 'deferred-leveldown') return false
if (type === 'subleveldown') return false

return true
}
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@
"test": "test"
},
"dependencies": {
"abstract-leveldown": "^6.1.1",
"abstract-leveldown": "^6.2.3",
"encoding-down": "^6.2.0",
"inherits": "^2.0.3",
"level-option-wrap": "^1.1.0",
"levelup": "^4.3.1",
"reachdown": "^1.0.0"
"reachdown": "^1.1.0"
},
"devDependencies": {
"after": "^0.8.2",
Expand All @@ -31,7 +31,6 @@
"hallmark": "^2.0.0",
"level-community": "^3.0.0",
"level-concat-iterator": "^2.0.1",
"memdb": "^1.3.1",
"memdown": "^5.0.0",
"nyc": "^14.0.0",
"standard": "^14.0.0",
Expand Down
Loading