Skip to content

Commit

Permalink
Yield LEVEL_LOCKED error when lock is held (#8)
Browse files Browse the repository at this point in the history
This allows `level-party` or `rave-level` (its `abstract-level`
replacement) to catch this specific error, instead of using a
catch-all that swallows other errors.
  • Loading branch information
vweevers authored Mar 25, 2022
1 parent cf161a5 commit aa975de
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ Read-only getter that returns a string reflecting the current state of the datab

### `db.open([options][, callback])`

Open the database. The `callback` function will be called with no arguments when successfully opened, or with a single error argument if opening failed. If no callback is provided, a promise is returned. Options passed to `open()` take precedence over options passed to the database constructor.
Open the database. The `callback` function will be called with no arguments when successfully opened, or with a single error argument if opening failed. The database has an exclusive lock (on disk): if another process or instance has already opened the underlying LevelDB store at the given `location` then opening will fail with error code [`LEVEL_LOCKED`](https://github.com/Level/abstract-level#errors). If no callback is provided, a promise is returned. Options passed to `open()` take precedence over options passed to the database constructor.

The optional `options` object may contain:

Expand Down
8 changes: 8 additions & 0 deletions binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,14 @@ struct BaseWorker {
argv = CreateCodeError(env, "LEVEL_NOT_FOUND", errMsg_);
} else if (status_.IsCorruption()) {
argv = CreateCodeError(env, "LEVEL_CORRUPTION", errMsg_);
} else if (status_.IsIOError()) {
if (strlen(errMsg_) > 15 && strncmp("IO error: lock ", errMsg_, 15) == 0) { // env_posix.cc
argv = CreateCodeError(env, "LEVEL_LOCKED", errMsg_);
} else if (strlen(errMsg_) > 19 && strncmp("IO error: LockFile ", errMsg_, 19) == 0) { // env_win.cc
argv = CreateCodeError(env, "LEVEL_LOCKED", errMsg_);
} else {
argv = CreateCodeError(env, "LEVEL_IO_ERROR", errMsg_);
}
} else {
argv = CreateError(env, errMsg_);
}
Expand Down
52 changes: 52 additions & 0 deletions test/lock-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
'use strict'

const test = require('tape')
const tempy = require('tempy')
const fork = require('child_process').fork
const path = require('path')
const { ClassicLevel } = require('..')

test('lock held by same process', async function (t) {
t.plan(2)

const location = tempy.directory()
const db1 = new ClassicLevel(location)
await db1.open()
const db2 = new ClassicLevel(location)

try {
await db2.open()
} catch (err) {
t.is(err.code, 'LEVEL_DATABASE_NOT_OPEN', 'second instance failed to open')
t.is(err.cause.code, 'LEVEL_LOCKED', 'second instance got lock error')
}

return db1.close()
})

test('lock held by other process', function (t) {
t.plan(6)

const location = tempy.directory()
const db = new ClassicLevel(location)

db.open(function (err) {
t.ifError(err, 'no open error')

const child = fork(path.join(__dirname, 'lock.js'), [location])

child.on('message', function (err) {
t.is(err.code, 'LEVEL_DATABASE_NOT_OPEN', 'second process failed to open')
t.is(err.cause.code, 'LEVEL_LOCKED', 'second process got lock error')

child.disconnect()
})

child.on('exit', function (code, sig) {
t.is(code, 0, 'child exited normally')
t.is(sig, null, 'not terminated due to signal')

db.close(t.ifError.bind(t))
})
})
})
10 changes: 10 additions & 0 deletions test/lock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
'use strict'

const { ClassicLevel } = require('..')

const location = process.argv[2]
const db = new ClassicLevel(location)

db.open(function (err) {
process.send(err)
})

0 comments on commit aa975de

Please sign in to comment.