From c08b6fef7d41ca38fa7596d478fa6801b00b1190 Mon Sep 17 00:00:00 2001 From: xRangerDE Date: Mon, 11 May 2026 23:43:01 +0200 Subject: [PATCH] refactor(db): lift db handle to long-lived singleton + close in shutdown appDb module-scope let, getAppDb() exported getter, opened once in app.whenReady with migrator run inline, closed in shutdownCleanup before debugLog flush so WAL checkpoint completes cleanly. Unlocks IPC handlers to read/write SQLite without per-call open/close. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/main.ts | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/main.ts b/src/main.ts index f277bad..407f690 100644 --- a/src/main.ts +++ b/src/main.ts @@ -9,6 +9,7 @@ import { compareUpdateVersions, isNewerUpdateVersion, normalizeUpdateVersion } f import { writeFileAtomicSync } from './main/infra/fs-atomic'; import { parseDuration, formatDuration, formatDurationDashed } from './main/infra/duration'; import { tBackend as tBackendCore, type BackendMessageKey } from './main/domain/i18n-backend'; +import type { DbHandle } from './main/infra/db'; import { normalizeLogin, normalizeAutoRecordPollSeconds, @@ -7206,30 +7207,32 @@ ipcMain.handle('save-video-dialog', async (_, defaultName: string) => { // ========================================== // APP LIFECYCLE // ========================================== +// Long-lived SQLite-Handle (Plan 04b+ Voraussetzung). Wird in app.whenReady +// geoeffnet, in shutdownCleanup geschlossen. getAppDb() returnt null wenn +// Open fehlgeschlagen ist (Native-Build-Probleme) — Caller mussen das pruefen. +let appDb: DbHandle | null = null; +export function getAppDb(): DbHandle | null { return appDb; } + app.whenReady().then(() => { app.setAppUserModelId('com.twitch.vodmanager'); refreshBundledToolPaths(true); startMetadataCacheCleanup(); startDebugLogFlushTimer(); - // SQLite-Shadow-Migration (Plan 02 / v5.0.0-alpha.1). Idempotent + fail-soft — - // bei Fehler bleibt JSON der Master. Lazy require, damit Native-Build-Fehler - // den App-Start nicht verhindern. + // SQLite-Open + Shadow-Migration. Long-lived handle in appDb (siehe oben). + // Lazy require, damit Native-Build-Fehler den App-Start nicht verhindern. try { const { openDatabase } = require('./main/infra/db'); const { migrateJsonToSqlite } = require('./main/domain/migrator'); const dbPath = path.join(APPDATA_DIR, 'app.db'); - const db = openDatabase(dbPath); - try { - const result = migrateJsonToSqlite({ db, appDataDir: APPDATA_DIR }); - appendDebugLog('sqlite-migrator', result); - } finally { - db.close(); - } + appDb = openDatabase(dbPath); + const result = migrateJsonToSqlite({ db: appDb, appDataDir: APPDATA_DIR }); + appendDebugLog('sqlite-migrator', result); } catch (e) { - appendDebugLog('sqlite-migrator-failed', { + appendDebugLog('sqlite-open-failed', { error: e instanceof Error ? e.message : String(e), }); + appDb = null; } restartAutoRecordPoller(); @@ -7291,6 +7294,13 @@ function shutdownCleanup(reason: 'window-all-closed' | 'before-quit'): void { saveConfig(config); flushQueueSave(); + // SQLite-Handle schliessen, falls geoeffnet — WAL-Checkpoint passiert beim + // close, sodass beim naechsten Start keine .wal/.shm orphans bleiben. + if (appDb) { + try { appDb.close(); } catch { /* already closed */ } + appDb = null; + } + // Flush debug log AFTER persisting state so any errors saving config / // queue land in the log before the timer is gone. stopDebugLogFlushTimer(true);