perf: hourly update check only with a browser open; opt-in retention, incremental auto-vacuum and Compact
- update-scheduler: default interval 1 h (DASHBOARD_UPDATE_CHECK_INTERVAL_MS and its 60 s floor kept); a scheduled tick with no /ws client is skipped, and the next browser to connect gets one catch-up check when the last one is stale (websocket.js onConnection). Manual check unchanged. - db-retention.js: shared session purge (used by POST /api/settings/cleanup too), opt-in AGENTHUB_RETENTION_DAYS applied by the hourly maintenance in 25-session transactions with yields (max 5000/run), bounded incremental_vacuum when the freelist is large, and a manual Compact (VACUUM in a worker, converting to auto_vacuum=INCREMENTAL). - db.js: new databases are INCREMENTAL; an existing one up to 32 MB is converted at startup. - GET/PUT /api/settings/retention, POST /api/settings/compact (confirm: true); Settings → Data → Session cleanup gets the Automatic cleanup control and Compact now (en + vi).
This commit is contained in:
+35
-1
@@ -141,6 +141,14 @@ migrateLegacyDatabase(DB_PATH);
|
||||
|
||||
const db = new Database(DB_PATH);
|
||||
|
||||
// Freed pages go back to the OS only with auto_vacuum. On a NEW database this
|
||||
// takes effect at once — it must come before `journal_mode = WAL`, which
|
||||
// writes the header; an existing one is converted further down, by a VACUUM,
|
||||
// only while it is small (`convertAutoVacuum`), else by Settings → Data →
|
||||
// Compact. `maintainDatabase` then runs a bounded `incremental_vacuum`.
|
||||
if (db.pragma("auto_vacuum", { simple: true }) === 0) {
|
||||
db.pragma("auto_vacuum = INCREMENTAL");
|
||||
}
|
||||
db.pragma("journal_mode = WAL");
|
||||
db.pragma("foreign_keys = ON");
|
||||
db.pragma("busy_timeout = 5000");
|
||||
@@ -1866,6 +1874,28 @@ for (const col of ["display_name", "email", "avatar_at"]) {
|
||||
}
|
||||
}
|
||||
|
||||
// An existing database made before auto_vacuum: the setting is stored only by
|
||||
// a VACUUM, which rewrites the whole file under an exclusive lock. Done here,
|
||||
// at startup and under the migration lock, only for a file up to
|
||||
// AUTO_VACUUM_CONVERT_MAX_BYTES (well under a second); a bigger one is
|
||||
// converted by the manual Compact (server/lib/db-retention.js). Best effort: a
|
||||
// second process holding a read open makes it fail, and the next start retries.
|
||||
const AUTO_VACUUM_CONVERT_MAX_BYTES = 32 * 1024 * 1024;
|
||||
function convertAutoVacuum() {
|
||||
try {
|
||||
if (db.pragma("auto_vacuum", { simple: true }) !== 0) return false;
|
||||
const bytes =
|
||||
db.pragma("page_count", { simple: true }) * db.pragma("page_size", { simple: true });
|
||||
if (bytes > AUTO_VACUUM_CONVERT_MAX_BYTES) return false;
|
||||
db.pragma("auto_vacuum = INCREMENTAL");
|
||||
db.exec("VACUUM");
|
||||
return db.pragma("auto_vacuum", { simple: true }) === 2;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
convertAutoVacuum();
|
||||
|
||||
migrationLock.exec("COMMIT");
|
||||
migrationLock.close();
|
||||
|
||||
@@ -2535,7 +2565,9 @@ const stmts = {
|
||||
* on a reader — the other dashboard's), after which the next write truncates
|
||||
* the WAL to `journal_size_limit`; and `PRAGMA optimize`, which refreshes the
|
||||
* planner's statistics only where they went stale. Never VACUUM: it rewrites
|
||||
* the whole file under an exclusive lock.
|
||||
* the whole file under an exclusive lock. The opt-in retention and the
|
||||
* bounded `incremental_vacuum` run after this, from
|
||||
* `server/lib/db-retention.js`'s `runScheduledMaintenance`.
|
||||
*/
|
||||
function maintainDatabase() {
|
||||
const out = {};
|
||||
@@ -2561,4 +2593,6 @@ module.exports = {
|
||||
applyIntroPricing,
|
||||
topUpDefaultPricing,
|
||||
maintainDatabase,
|
||||
convertAutoVacuum,
|
||||
AUTO_VACUUM_CONVERT_MAX_BYTES,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user