fix: 数据库初始化加固 — 关闭流程禁止重开 + initDb 串行化 + 错误详情显示
- closeDatabase 后置 isClosing 标记,关闭流程中 getDb 直接拒绝, 避免 forceClose 前其他组件重新建立连接(未关闭即被进程终止) - initDb 串行链:失败重置后并发 getDb 不再同时 createDatabase - 弹框显示 ARIA_OPEN_ERROR 等包装错误的 details(原始异常信息)
This commit is contained in:
+36
-17
@@ -90,6 +90,11 @@ const ACTIVE_TAB_COLUMNS: Record<string, ColumnDef> = {
|
|||||||
let dbPromise: Promise<MetonaSqlark> | null = null
|
let dbPromise: Promise<MetonaSqlark> | null = null
|
||||||
let initFailedNotified = false
|
let initFailedNotified = false
|
||||||
let healTriggered = false
|
let healTriggered = false
|
||||||
|
// v0.6.1: 关闭流程标记 — closeDatabase 后禁止 getDb 重新打开库,
|
||||||
|
// 避免 forceClose 前其他组件重新建立连接(未关闭即被进程终止)
|
||||||
|
let isClosing = false
|
||||||
|
// v0.6.1: 初始化串行链 — 防止 dbPromise 失败重置后多个 getDb 并发触发 createDatabase
|
||||||
|
let initChain: Promise<unknown> = Promise.resolve()
|
||||||
|
|
||||||
const RESET_PENDING_KEY = 'marklite-db-reset-pending'
|
const RESET_PENDING_KEY = 'marklite-db-reset-pending'
|
||||||
// AriaEngine 的 IndexedDBBackend 内部库名 = `aria-${name}`(sqlark 源码约定)
|
// AriaEngine 的 IndexedDBBackend 内部库名 = `aria-${name}`(sqlark 源码约定)
|
||||||
@@ -172,10 +177,11 @@ async function initDb(): Promise<MetonaSqlark> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* v0.6.0: 关闭数据库 — 应用退出前调用,确保 WAL/SSTable 干净落盘,
|
* v0.6.1: 关闭数据库 — 应用退出前调用,确保 WAL/SSTable 干净落盘,
|
||||||
* 避免渲染进程被销毁时 flush 中断留下半写文件导致下次启动损坏。
|
* 避免渲染进程被销毁时 flush 中断留下半写文件导致下次启动损坏。
|
||||||
*/
|
*/
|
||||||
export async function closeDatabase(): Promise<void> {
|
export async function closeDatabase(): Promise<void> {
|
||||||
|
isClosing = true
|
||||||
const current = dbPromise
|
const current = dbPromise
|
||||||
dbPromise = null
|
dbPromise = null
|
||||||
if (!current) return
|
if (!current) return
|
||||||
@@ -189,23 +195,36 @@ export async function closeDatabase(): Promise<void> {
|
|||||||
|
|
||||||
export function getDb(): Promise<MetonaSqlark> {
|
export function getDb(): Promise<MetonaSqlark> {
|
||||||
if (!dbPromise) {
|
if (!dbPromise) {
|
||||||
dbPromise = initDb().catch(err => {
|
if (isClosing) {
|
||||||
// v0.6.0: 初始化失败不再静默 — 记录日志并提示用户(否则所有数据持久化静默失效)
|
// 关闭流程中禁止重开(返回 rejected Promise,调用方 catch 吞掉)
|
||||||
logError('数据库初始化失败', err)
|
return Promise.reject(new Error('数据库正在关闭'))
|
||||||
if (!initFailedNotified) {
|
}
|
||||||
initFailedNotified = true
|
// 串行化初始化:失败后的重试排队执行,避免并发 createDatabase
|
||||||
try {
|
initChain = initChain.catch(() => {
|
||||||
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
|
|
||||||
})
|
})
|
||||||
|
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.catch(() => {
|
||||||
dbPromise = null
|
dbPromise = null
|
||||||
|
|||||||
Reference in New Issue
Block a user