Skip to content

Commit aa975de

Browse files
authored
Yield LEVEL_LOCKED error when lock is held (#8)
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.
1 parent cf161a5 commit aa975de

File tree

4 files changed

+71
-1
lines changed

4 files changed

+71
-1
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ Read-only getter that returns a string reflecting the current state of the datab
194194

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

197-
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.
197+
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.
198198

199199
The optional `options` object may contain:
200200

binding.cc

+8
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,14 @@ struct BaseWorker {
444444
argv = CreateCodeError(env, "LEVEL_NOT_FOUND", errMsg_);
445445
} else if (status_.IsCorruption()) {
446446
argv = CreateCodeError(env, "LEVEL_CORRUPTION", errMsg_);
447+
} else if (status_.IsIOError()) {
448+
if (strlen(errMsg_) > 15 && strncmp("IO error: lock ", errMsg_, 15) == 0) { // env_posix.cc
449+
argv = CreateCodeError(env, "LEVEL_LOCKED", errMsg_);
450+
} else if (strlen(errMsg_) > 19 && strncmp("IO error: LockFile ", errMsg_, 19) == 0) { // env_win.cc
451+
argv = CreateCodeError(env, "LEVEL_LOCKED", errMsg_);
452+
} else {
453+
argv = CreateCodeError(env, "LEVEL_IO_ERROR", errMsg_);
454+
}
447455
} else {
448456
argv = CreateError(env, errMsg_);
449457
}

test/lock-test.js

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
'use strict'
2+
3+
const test = require('tape')
4+
const tempy = require('tempy')
5+
const fork = require('child_process').fork
6+
const path = require('path')
7+
const { ClassicLevel } = require('..')
8+
9+
test('lock held by same process', async function (t) {
10+
t.plan(2)
11+
12+
const location = tempy.directory()
13+
const db1 = new ClassicLevel(location)
14+
await db1.open()
15+
const db2 = new ClassicLevel(location)
16+
17+
try {
18+
await db2.open()
19+
} catch (err) {
20+
t.is(err.code, 'LEVEL_DATABASE_NOT_OPEN', 'second instance failed to open')
21+
t.is(err.cause.code, 'LEVEL_LOCKED', 'second instance got lock error')
22+
}
23+
24+
return db1.close()
25+
})
26+
27+
test('lock held by other process', function (t) {
28+
t.plan(6)
29+
30+
const location = tempy.directory()
31+
const db = new ClassicLevel(location)
32+
33+
db.open(function (err) {
34+
t.ifError(err, 'no open error')
35+
36+
const child = fork(path.join(__dirname, 'lock.js'), [location])
37+
38+
child.on('message', function (err) {
39+
t.is(err.code, 'LEVEL_DATABASE_NOT_OPEN', 'second process failed to open')
40+
t.is(err.cause.code, 'LEVEL_LOCKED', 'second process got lock error')
41+
42+
child.disconnect()
43+
})
44+
45+
child.on('exit', function (code, sig) {
46+
t.is(code, 0, 'child exited normally')
47+
t.is(sig, null, 'not terminated due to signal')
48+
49+
db.close(t.ifError.bind(t))
50+
})
51+
})
52+
})

test/lock.js

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
'use strict'
2+
3+
const { ClassicLevel } = require('..')
4+
5+
const location = process.argv[2]
6+
const db = new ClassicLevel(location)
7+
8+
db.open(function (err) {
9+
process.send(err)
10+
})

0 commit comments

Comments
 (0)