fix: [3.0.X] Migrate to better-sqlite (#7568)

This commit is contained in:
Louis Lam
2026-07-04 18:13:25 +08:00
committed by GitHub
parent 28ee14d30d
commit be51de4e73
6 changed files with 243 additions and 761 deletions
-8
View File
@@ -48,11 +48,6 @@ jobs:
- run: npm --version
- run: npm clean-install --no-fund
- name: Rebuild native modules for ARM64
if: matrix.os == 'ubuntu-22.04-arm'
run: npm rebuild @louislam/sqlite3
- run: npm run build
- run: npm run test-backend
env:
@@ -109,9 +104,6 @@ jobs:
node-version: 26
- run: npm clean-install --no-fund
- name: Rebuild native modules for ARM64
run: npm rebuild @louislam/sqlite3
- name: Install Playwright ${{ env.PLAYWRIGHT_VERSION }}
run: npx playwright@${{ env.PLAYWRIGHT_VERSION }} install --with-deps
-18
View File
@@ -1,18 +0,0 @@
BEGIN TRANSACTION;
PRAGMA writable_schema = TRUE;
UPDATE
SQLITE_MASTER
SET
sql = replace(sql,
'monitor_id INTEGER NOT NULL',
'monitor_id INTEGER NOT NULL REFERENCES [monitor] ([id]) ON DELETE CASCADE ON UPDATE CASCADE'
)
WHERE
name = 'monitor_tls_info'
AND type = 'table';
PRAGMA writable_schema = RESET;
COMMIT;
+221 -707
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -65,12 +65,12 @@
"dependencies": {
"@grpc/grpc-js": "~1.8.22",
"@louislam/ping": "~0.4.4-mod.1",
"@louislam/sqlite3": "15.1.6",
"@vvo/tzdb": "~6.198.0",
"args-parser": "~1.3.0",
"axios": "~0.32.0",
"badge-maker": "~3.3.1",
"bcryptjs": "~2.4.3",
"better-sqlite3": "~12.11.1",
"chardet": "~1.4.0",
"check-password-strength": "~2.0.10",
"cheerio": "~1.0.0",
@@ -143,9 +143,9 @@
"thirty-two": "~1.0.2",
"tldts": "~7.0.23",
"tough-cookie": "~4.1.4",
"tsx": "~4.20.6",
"validator": "~13.15.26",
"web-push": "~3.6.7",
"tsx": "~4.20.6",
"ws": "~8.19.0",
"zod": "~4.1.12"
},
@@ -230,7 +230,7 @@
}
},
"allowScripts": {
"@louislam/sqlite3@15.1.6": true,
"better-sqlite3@12.11.1": true,
"@fortawesome/fontawesome-common-types@0.2.36": true,
"@fortawesome/fontawesome-svg-core@1.2.36": true,
"@fortawesome/free-regular-svg-icons@5.15.4": true,
+18 -21
View File
@@ -295,9 +295,6 @@ class Database {
fs.copyFileSync(Database.templatePath, Database.sqlitePath);
}
const Dialect = require("knex/lib/dialects/sqlite3/index.js");
Dialect.prototype._driver = () => require("@louislam/sqlite3");
// SQLite is actually multiple connections for WAL mode, so we can set it to a higher number.
// See: https://github.com/knex/knex/issues/3176#issuecomment-3389054899
let poolConfig = {
@@ -316,7 +313,7 @@ class Database {
}
config = {
client: Dialect,
client: "better-sqlite3",
connection: {
filename: Database.sqlitePath,
acquireConnectionTimeout: acquireConnectionTimeout,
@@ -326,9 +323,12 @@ class Database {
...poolConfig,
acquireTimeoutMillis: acquireConnectionTimeout,
afterCreate: (rawConn, done) => {
this.initSQLite(rawConn, testMode)
.then(() => done(undefined, rawConn))
.catch((err) => done(err, rawConn));
try {
this.initSQLite(rawConn, testMode);
done(undefined, rawConn);
} catch (err) {
done(err, rawConn);
}
},
},
};
@@ -447,35 +447,32 @@ class Database {
/**
* Initialize SQLite for each connection
* @param {any} rawConn The raw node-sqlite3 Database object
* @param {import("better-sqlite3").Database} rawConn The raw better-sqlite3 Database object
* @param {boolean} testMode Should the connection be started in test mode?
* @returns {Promise<void>}
*/
static async initSQLite(rawConn, testMode) {
// Since rawConn.run is callback based, in order to avoid callback hell, wrap it in a promise
const asyncRun = (sql) => {
return new Promise((resolve, reject) => rawConn.run(sql, (err) => (err ? reject(err) : resolve())));
};
static initSQLite(rawConn, testMode) {
// Required for legacy SQL patch files that use unsafe PRAGMAs (e.g. PRAGMA writable_schema)
rawConn.unsafeMode(true);
if (testMode) {
// Change to MEMORY
await asyncRun("PRAGMA journal_mode = MEMORY");
rawConn.exec("PRAGMA journal_mode = MEMORY");
} else {
// Change to WAL
await asyncRun("PRAGMA journal_mode = WAL");
rawConn.exec("PRAGMA journal_mode = WAL");
}
await asyncRun("PRAGMA foreign_keys = ON");
await asyncRun("PRAGMA cache_size = -12000");
await asyncRun("PRAGMA auto_vacuum = INCREMENTAL");
rawConn.exec("PRAGMA foreign_keys = ON");
rawConn.exec("PRAGMA cache_size = -12000");
rawConn.exec("PRAGMA auto_vacuum = INCREMENTAL");
// Avoid error "SQLITE_BUSY: database is locked" by allowing SQLITE to wait up to 5 seconds to do a write
await asyncRun("PRAGMA busy_timeout = 5000");
rawConn.exec("PRAGMA busy_timeout = 5000");
// This ensures that an operating system crash or power failure will not corrupt the database.
// FULL synchronous is very safe, but it is also slower.
// Read more: https://sqlite.org/pragma.html#pragma_synchronous
await asyncRun("PRAGMA synchronous = NORMAL");
rawConn.exec("PRAGMA synchronous = NORMAL");
}
/**
+1 -4
View File
@@ -20,12 +20,9 @@ describe("Database Migration", () => {
}
// Use the same SQLite driver as the project
const Dialect = require("knex/lib/dialects/sqlite3/index.js");
Dialect.prototype._driver = () => require("@louislam/sqlite3");
const knex = require("knex");
const db = knex({
client: Dialect,
client: "better-sqlite3",
connection: {
filename: testDbPath,
},