fix: 关闭应用前干净关闭数据库 + 自愈仅启动早期触发
- 退出前 db.close() 确保 WAL/SSTable 干净落盘,避免渲染进程销毁时 flush 中断留下半写文件导致下次启动损坏 - 自愈(删损坏库/重载)仅限页面生命周期内首次初始化;运行中失败直接抛错,不再阻塞应用关闭流程
This commit is contained in:
@@ -12,6 +12,7 @@ import { useDragDrop } from './hooks/useDragDrop'
|
||||
import { useAutoSave } from './hooks/useAutoSave'
|
||||
import { useIpcListeners } from './hooks/useIpcListeners'
|
||||
import { MeToast } from './lib/toast'
|
||||
import { closeDatabase } from './db/schema'
|
||||
import { Toolbar } from './components/Toolbar/Toolbar'
|
||||
import { StatusBar } from './components/StatusBar'
|
||||
import { TabBar } from './components/TabBar/TabBar'
|
||||
@@ -58,7 +59,13 @@ export function App() {
|
||||
})
|
||||
}, [])
|
||||
|
||||
useUnsavedWarning(() => tabs.some(t => t.isModified), handleConfirmClose, flushSaveToDB)
|
||||
// v0.6.0: 关闭前先保存标签快照,再干净关闭数据库(避免 flush 中断导致下次启动损坏)
|
||||
const handleBeforeForceClose = useCallback(async () => {
|
||||
await flushSaveToDB()
|
||||
await closeDatabase()
|
||||
}, [])
|
||||
|
||||
useUnsavedWarning(() => tabs.some(t => t.isModified), handleConfirmClose, handleBeforeForceClose)
|
||||
useKeyboard(handleOpenFile, handleSave, handleSaveAs)
|
||||
useIpcListeners()
|
||||
|
||||
|
||||
+46
-23
@@ -79,18 +79,19 @@ const ACTIVE_TAB_COLUMNS: Record<string, ColumnDef> = {
|
||||
}
|
||||
|
||||
/**
|
||||
* v0.6.0: MetonaSqlark 初始化 — 懒加载单例。
|
||||
* create() 为异步,无法像 Dexie 那样模块顶层同步实例化;
|
||||
* 首次调用时创建,之后复用同一 Promise。
|
||||
*
|
||||
* v0.6.0: 数据损坏自愈 —
|
||||
* AriaEngine 在异常退出(强杀/断电)时可能留下残缺的 SSTable 文件,
|
||||
* 重新打开解析块偏移会抛 RangeError(offset is out of bounds)导致初始化失败。
|
||||
* 检测到打开失败后:标记 localStorage → 重载页面(干净环境无残留 IDB 连接)
|
||||
* → 删除损坏库 → 重建。create 成功后才清除标记,避免自愈失败死循环。
|
||||
*
|
||||
* 注意:自愈(删库/重载)只允许在页面生命周期内首次初始化时触发;
|
||||
* 运行中(如应用关闭流程的 flushSaveToDB)getDb 失败时直接抛错,
|
||||
* 绝不删库/重载,避免关闭流程被数据库操作阻塞。
|
||||
*/
|
||||
let dbPromise: Promise<MetonaSqlark> | null = null
|
||||
let initFailedNotified = false
|
||||
let healTriggered = false
|
||||
|
||||
// IndexedDBBackend 的内部库名 = `aria-${name}`(sqlark 源码约定)
|
||||
const DB_STORAGE_NAME = `aria-${DB_NAME}`
|
||||
@@ -144,28 +145,50 @@ async function createDatabase(): Promise<MetonaSqlark> {
|
||||
}
|
||||
|
||||
async function initDb(): Promise<MetonaSqlark> {
|
||||
await resetCorruptDatabaseIfNeeded()
|
||||
try {
|
||||
const db = await createDatabase()
|
||||
// 创建成功:清除重置标记(自愈完成)
|
||||
// 页面生命周期内仅首次初始化走自愈流程(删损坏库 + 失败重载)
|
||||
if (!healTriggered) {
|
||||
healTriggered = true
|
||||
await resetCorruptDatabaseIfNeeded()
|
||||
try {
|
||||
localStorage.removeItem(RESET_PENDING_KEY)
|
||||
} catch {
|
||||
/* 忽略 */
|
||||
}
|
||||
return db
|
||||
} catch (err) {
|
||||
logError('数据库打开失败', err)
|
||||
// 数据损坏自愈:首次失败 → 标记 + 重载页面(干净环境删除损坏库重建)
|
||||
try {
|
||||
if (localStorage.getItem(RESET_PENDING_KEY) !== '1') {
|
||||
localStorage.setItem(RESET_PENDING_KEY, '1')
|
||||
window.location.reload()
|
||||
const db = await createDatabase()
|
||||
// 创建成功:清除重置标记(自愈完成)
|
||||
try {
|
||||
localStorage.removeItem(RESET_PENDING_KEY)
|
||||
} catch {
|
||||
/* 忽略 */
|
||||
}
|
||||
} catch {
|
||||
/* 忽略 */
|
||||
return db
|
||||
} catch (err) {
|
||||
logError('数据库打开失败', err)
|
||||
// 启动早期失败:标记 + 重载页面(干净环境删除损坏库重建)— 仅此一次
|
||||
try {
|
||||
if (localStorage.getItem(RESET_PENDING_KEY) !== '1') {
|
||||
localStorage.setItem(RESET_PENDING_KEY, '1')
|
||||
window.location.reload()
|
||||
}
|
||||
} catch {
|
||||
/* 忽略 */
|
||||
}
|
||||
throw err
|
||||
}
|
||||
throw err
|
||||
}
|
||||
// 运行中(自愈已尝试过):不再删库/重载,直接尝试创建,失败抛给调用方
|
||||
return createDatabase()
|
||||
}
|
||||
|
||||
/**
|
||||
* v0.6.0: 关闭数据库 — 应用退出前调用,确保 WAL/SSTable 干净落盘,
|
||||
* 避免渲染进程被销毁时 flush 中断留下半写文件导致下次启动损坏。
|
||||
*/
|
||||
export async function closeDatabase(): Promise<void> {
|
||||
const current = dbPromise
|
||||
dbPromise = null
|
||||
if (!current) return
|
||||
try {
|
||||
const db = await current.catch(() => null)
|
||||
if (db) await db.close()
|
||||
} catch (err) {
|
||||
logError('关闭数据库失败', err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user