Skip to content

Commit e046a85

Browse files
committed
apply review suggestions
1 parent 2913993 commit e046a85

File tree

2 files changed

+54
-45
lines changed

2 files changed

+54
-45
lines changed

doc/api/sqlite.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ console.log(query.all());
7878
<!-- YAML
7979
added: v22.5.0
8080
changes:
81+
- version: REPLACEME
82+
pr-url: https://github.com/nodejs/node/pull/57752
83+
description: Add `timeout` option
8184
- version: v23.10.0
8285
pr-url: https://github.com/nodejs/node/pull/56991
8386
description: The `path` argument now supports Buffer and URL objects.
@@ -116,8 +119,9 @@ added: v22.5.0
116119
and the `loadExtension()` method are enabled.
117120
You can call `enableLoadExtension(false)` later to disable this feature.
118121
**Default:** `false`.
119-
* `timeout` {number} Number of milliseconds to wait for the database to
120-
become available when it is locked. **Default:** `0`.
122+
* `timeout` {number} The [busy timeout][] in milliseconds. This is the maximum amount of
123+
time that SQLite will wait for a database lock to be released before
124+
returning an error. **Default:** `0`.
121125

122126
Constructs a new `DatabaseSync` instance.
123127

@@ -720,6 +724,7 @@ resolution handler passed to [`database.applyChangeset()`][]. See also
720724
</tr>
721725
</table>
722726

727+
[busy timeout]: https://sqlite.org/c3ref/busy_timeout.html
723728
[Changesets and Patchsets]: https://www.sqlite.org/sessionintro.html#changesets_and_patchsets
724729
[Constants Passed To The Conflict Handler]: https://www.sqlite.org/session/c_changeset_conflict.html
725730
[Constants Returned From The Conflict Handler]: https://www.sqlite.org/session/c_changeset_abort.html

test/parallel/test-sqlite-timeout.js

Lines changed: 47 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
'use strict';
22
require('../common');
33
const tmpdir = require('../common/tmpdir');
4-
const { path: fixturesPath } = require('../common/fixtures');
5-
const { spawn } = require('node:child_process');
64
const { join } = require('node:path');
75
const { DatabaseSync } = require('node:sqlite');
86
const { test } = require('node:test');
7+
const { once } = require('node:events');
8+
const { Worker } = require('node:worker_threads');
99
let cnt = 0;
1010

1111
tmpdir.refresh();
@@ -14,55 +14,59 @@ function nextDb() {
1414
return join(tmpdir.path, `database-${cnt++}.db`);
1515
}
1616

17-
test('connection can perform queries when lock is released before the timeout', (t, done) => {
18-
const databasePath = nextDb();
19-
const conn = new DatabaseSync(databasePath, { timeout: 1100 });
20-
t.after(() => { conn.close(); });
21-
22-
conn.exec('CREATE TABLE data (key INTEGER PRIMARY KEY, value TEXT)');
23-
24-
// Spawns a child process to locks the database for 1 second
25-
const child = spawn('./node', [fixturesPath('sqlite/lock-db.js'), databasePath], {
26-
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
17+
test('waits to acquire lock', async (t) => {
18+
const DB_PATH = nextDb();
19+
const conn = new DatabaseSync(DB_PATH);
20+
t.after(() => {
21+
try {
22+
conn.close();
23+
} catch {
24+
// Ignore.
25+
}
2726
});
2827

29-
child.on('message', (msg) => {
30-
if (msg === 'locked') {
31-
conn.exec('INSERT INTO data (key, value) VALUES (?, ?)', [1, 'value-1']);
32-
33-
setTimeout(() => {
34-
done();
35-
}, 1100);
28+
conn.exec('CREATE TABLE IF NOT EXISTS data (value TEXT)');
29+
conn.exec('BEGIN EXCLUSIVE;');
30+
const worker = new Worker(`
31+
'use strict';
32+
const { DatabaseSync } = require('node:sqlite');
33+
const { workerData } = require('node:worker_threads');
34+
const conn = new DatabaseSync(workerData.database, { timeout: 30000 });
35+
conn.exec('SELECT * FROM data');
36+
conn.close();
37+
`, {
38+
eval: true,
39+
workerData: {
40+
database: DB_PATH,
3641
}
3742
});
43+
await once(worker, 'online');
44+
conn.exec('COMMIT;');
45+
await once(worker, 'exit');
3846
});
3947

40-
test('trhows if lock is holden longer than the provided timeout', (t, done) => {
41-
const databasePath = nextDb();
42-
const conn = new DatabaseSync(databasePath, { timeout: 800 });
43-
t.after(() => { conn.close(); });
44-
45-
conn.exec('CREATE TABLE data (key INTEGER PRIMARY KEY, value TEXT)');
46-
47-
// Spawns a child process to locks the database for 1 second
48-
const child = spawn('./node', [fixturesPath('sqlite/lock-db.js'), databasePath], {
49-
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
50-
});
51-
52-
child.on('message', (msg) => {
53-
if (msg === 'locked') {
54-
t.assert.throws(() => {
55-
conn.exec('INSERT INTO data (key, value) VALUES (?, ?)', [1, 'value-1']);
56-
}, {
57-
code: 'ERR_SQLITE_ERROR',
58-
message: 'database is locked',
59-
});
48+
test('throws if the lock cannot be acquired before timeout', (t) => {
49+
const DB_PATH = nextDb();
50+
const conn1 = new DatabaseSync(DB_PATH);
51+
t.after(() => {
52+
try {
53+
conn1.close();
54+
} catch {
55+
// Ignore.
6056
}
6157
});
62-
63-
child.on('exit', (code) => {
64-
if (code === 0) {
65-
done();
58+
const conn2 = new DatabaseSync(DB_PATH, { timeout: 1 });
59+
t.after(() => {
60+
try {
61+
conn2.close();
62+
} catch {
63+
// Ignore.
6664
}
6765
});
66+
67+
conn1.exec('CREATE TABLE IF NOT EXISTS data (value TEXT)');
68+
conn1.exec('PRAGMA locking_mode = EXCLUSIVE; BEGIN EXCLUSIVE;');
69+
t.assert.throws(() => {
70+
conn2.exec('SELECT * FROM data');
71+
}, /database is locked/);
6872
});

0 commit comments

Comments
 (0)