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:
2026-10-16 10:20:38 +07:00
parent ecbbb4dd8f
commit e07993c536
21 changed files with 1323 additions and 58 deletions
+18
View File
@@ -48,6 +48,13 @@ function initWebSocket(server) {
});
wss.on("connection", (ws, req) => {
for (const fn of connectionListeners) {
try {
fn(ws);
} catch {
/* a listener never breaks a connection */
}
}
// Server mode: who this socket belongs to, for broadcast filtering, and
// the cookie to re-check it by (a logout or a disabled user ends it).
if (isServerMode()) {
@@ -252,6 +259,16 @@ function broadcast(type, data) {
});
}
/**
* Subscribe to new `/ws` browser connections (the update scheduler checks once
* when a browser returns after its ticks were skipped). Returns an unsubscribe.
*/
const connectionListeners = new Set();
function onConnection(fn) {
connectionListeners.add(fn);
return () => connectionListeners.delete(fn);
}
function getConnectionCount() {
if (!wss) return 0;
let count = 0;
@@ -306,6 +323,7 @@ module.exports = {
initPtyWebSocket,
broadcast,
getConnectionCount,
onConnection,
closeWebSocket,
sizeFromQuery,
};