Commit d630b5d88bc for nodejs
commit d630b5d88bc19c10443b3d83b723b7b052fa0c7f
Author: Lazizbek Ergashev <lazerg2@gmail.com>
Date: Mon Oct 26 04:33:24 2026 +0500
sqlite: retry busy or locked backup steps
Signed-off-by: lazerg <lazerg2@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66769
Fixes: https://github.com/nodejs/node/issues/66760
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
diff --git a/src/node_sqlite.cc b/src/node_sqlite.cc
index b8b1988c50b..c8ebd65f5b4 100644
--- a/src/node_sqlite.cc
+++ b/src/node_sqlite.cc
@@ -680,6 +680,11 @@ class BackupJob : public ThreadPoolWork {
return;
}
+ if (backup_status_ == SQLITE_BUSY || backup_status_ == SQLITE_LOCKED) {
+ this->ScheduleWork();
+ return;
+ }
+
int total_pages = sqlite3_backup_pagecount(backup_.get());
int remaining_pages = sqlite3_backup_remaining(backup_.get());
if (remaining_pages != 0) {
diff --git a/test/parallel/test-sqlite-backup.mjs b/test/parallel/test-sqlite-backup.mjs
index 633c7da7735..d3d6a433993 100644
--- a/test/parallel/test-sqlite-backup.mjs
+++ b/test/parallel/test-sqlite-backup.mjs
@@ -1,6 +1,7 @@
// Flags: --expose-gc
import {
isWindows,
+ platformTimeout,
skipIfSQLiteMissing,
spawnPromisified,
} from '../common/index.mjs';
@@ -351,6 +352,32 @@ test('backup fails when path cannot be opened', async (t) => {
});
});
+test('backup waits for a write transaction on the source to end', async (t) => {
+ const database = makeSourceDb();
+ const destDb = nextDb();
+
+ t.after(() => { database.close(); });
+
+ database.exec('BEGIN IMMEDIATE');
+ database.exec("INSERT INTO data (key, value) VALUES (3, 'value-3')");
+ const p = backup(database, destDb);
+ setTimeout(() => database.exec('COMMIT'), platformTimeout(100));
+
+ await p;
+
+ const backupDb = new Database(destDb);
+
+ t.after(() => { backupDb.close(); });
+
+ const rows = backupDb.prepare('SELECT * FROM data').all();
+
+ t.assert.deepStrictEqual(rows, [
+ { __proto__: null, key: 1, value: 'value-1' },
+ { __proto__: null, key: 2, value: 'value-2' },
+ { __proto__: null, key: 3, value: 'value-3' },
+ ]);
+});
+
test('backup has correct name and length', (t) => {
t.assert.strictEqual(backup.name, 'backup');
t.assert.strictEqual(backup.length, 2);