diff --git a/src/renderer/db/schema.ts b/src/renderer/db/schema.ts index 3faf359..aca95e3 100644 --- a/src/renderer/db/schema.ts +++ b/src/renderer/db/schema.ts @@ -90,6 +90,11 @@ const ACTIVE_TAB_COLUMNS: Record = { let dbPromise: Promise | null = null let initFailedNotified = false let healTriggered = false +// v0.6.1: 关闭流程标记 — closeDatabase 后禁止 getDb 重新打开库, +// 避免 forceClose 前其他组件重新建立连接(未关闭即被进程终止) +let isClosing = false +// v0.6.1: 初始化串行链 — 防止 dbPromise 失败重置后多个 getDb 并发触发 createDatabase +let initChain: Promise = Promise.resolve() const RESET_PENDING_KEY = 'marklite-db-reset-pending' // AriaEngine 的 IndexedDBBackend 内部库名 = `aria-${name}`(sqlark 源码约定) @@ -172,10 +177,11 @@ async function initDb(): Promise { } /** - * v0.6.0: 关闭数据库 — 应用退出前调用,确保 WAL/SSTable 干净落盘, + * v0.6.1: 关闭数据库 — 应用退出前调用,确保 WAL/SSTable 干净落盘, * 避免渲染进程被销毁时 flush 中断留下半写文件导致下次启动损坏。 */ export async function closeDatabase(): Promise { + isClosing = true const current = dbPromise dbPromise = null if (!current) return @@ -189,23 +195,36 @@ export async function closeDatabase(): Promise { export function getDb(): Promise { if (!dbPromise) { - dbPromise = initDb().catch(err => { - // v0.6.0: 初始化失败不再静默 — 记录日志并提示用户(否则所有数据持久化静默失效) - logError('数据库初始化失败', err) - if (!initFailedNotified) { - initFailedNotified = true - try { - const code = (err as { code?: string })?.code ?? 'UNKNOWN' - const msg = err instanceof Error ? err.message : String(err) - setTimeout(() => { - MeToast?.error(`数据库初始化失败(${code}):${msg}`) - }, 0) - } catch { - /* 提示失败不影响主流程 */ - } - } - throw err + if (isClosing) { + // 关闭流程中禁止重开(返回 rejected Promise,调用方 catch 吞掉) + return Promise.reject(new Error('数据库正在关闭')) + } + // 串行化初始化:失败后的重试排队执行,避免并发 createDatabase + initChain = initChain.catch(() => { + /* 前序失败不影响后续 */ }) + dbPromise = initChain + .then(() => initDb()) + .catch(err => { + // v0.6.1: 初始化失败不再静默 — 记录日志并提示用户(否则所有数据持久化静默失效) + logError('数据库初始化失败', err) + if (!initFailedNotified) { + initFailedNotified = true + try { + const code = (err as { code?: string })?.code ?? 'UNKNOWN' + const msg = err instanceof Error ? err.message : String(err) + // ARIA_OPEN_ERROR 等包装错误的具体原因在 details 里(原始异常) + const details = (err as { details?: { message?: string } })?.details?.message + const detailText = details ? ` | 详情: ${details}` : '' + setTimeout(() => { + MeToast?.error(`数据库初始化失败(${code}):${msg}${detailText}`) + }, 0) + } catch { + /* 提示失败不影响主流程 */ + } + } + throw err + }) // 初始化失败时允许下次重试 dbPromise.catch(() => { dbPromise = null