Skip to content

Commit 0e814c7

Browse files
committed
fix(db): ensure backup integrity with WAL checkpointing and refine restoration
- Implement `MainStore::checkpoint` to flush WAL data before file-level backups. - Update `backup_setting` command to trigger database checkpointing via PRAGMA wal_checkpoint. - Refine `MainStore::atomic_restore` to correctly handle missing machine-specific keys using `delete_config`. - Make `restore_user_files` public and remove obsolete restoration methods in `DbBackup`. - Update release notes for v1.2.4 to include data integrity and restoration logic improvements.
1 parent 44e5c50 commit 0e814c7

File tree

4 files changed

+38
-3
lines changed

4 files changed

+38
-3
lines changed

RELEASE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
### 🪄 Improvements
88

99
- **Database Architecture Hardening**: Upgraded the database engine to use **WAL (Write-Ahead Logging)** mode and implemented a 5-second **Busy Timeout**. These changes significantly improve concurrency, allowing the background proxy (CCProxy) and the main chat interface to access the database simultaneously without locking issues.
10+
- **Data Integrity for Backups**: Implemented a mandatory **SQL Checkpoint (TRUNCATE)** before any backup operation. This ensures that all latest messages (previously held in the `-wal` log file) are properly flushed to the main database file before encryption, guaranteeing that your backups always contain the most recent data.
1011
- **Atomic Restoration with State Preservation**: Completely refactored the database restoration process. The system now performs a safe, atomic "disconnect-replace-reconnect" sequence that prevents file corruption. It also preserves machine-specific settings (window positions, sizes, and network proxy configurations) during restoration, ensuring a seamless experience when migrating data.
1112
- **Mission-Critical Stability Hardening**: Performed a comprehensive audit and refactoring of the application's startup sequence. All hardcoded `.expect()` and `.unwrap()` calls in critical paths (logging, database path resolution, and window management) have been replaced with graceful error handling. This ensures the application remains operational even in highly restricted environments like Windows Server 2019.
1213
- **Graceful Logging Fallback**: The logging system now automatically degrades to console-only output if the designated log directory is unwritable or inaccessible, preventing immediate startup crashes.

RELEASE.zh-CN.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,16 @@
77
### 🪄 改进
88

99
- **数据库架构加固**:将数据库引擎升级为 **WAL (Write-Ahead Logging)** 模式,并实现了 5 秒的 **Busy Timeout (忙碌重试)**。这些改进显著提升了并发性能,允许后台代理 (CCProxy) 与主聊天界面同时访问数据库而不会发生锁定冲突。
10-
- **状态保留的原子化还原**:彻底重构了数据库还原流程。系统现在执行安全的原子级“断开-替换-重连”序列,防止了文件损坏。同时,在还原过程中能够自动保留机器特有的设置(如窗口位置、尺寸、网络代理配置等),确保数据迁移后的无缝体验。
10+
- **备份数据完整性保障**:在执行任何备份操作前引入了强制的 **SQL Checkpoint (TRUNCATE)** 机制。这确保了所有最新的消息记录(原先可能暂存在 `-wal` 日志文件中)在加密前都被完整同步到数据库主文件中,解决了“备份不到最新消息”的问题。
11+
- **状态保留的原子化还原**:彻底重构了数据库还原流程。系统现在执行安全的原子级“断开-替换-重连”序列,防止了文件损坏和重启崩溃。同时,在还原过程中能够自动保留机器特有的设置(如窗口位置、尺寸、网络代理配置等),确保数据迁移后的无缝体验。
1112
- **启动路径鲁棒性加固**:对应用程序启动序列进行了全面审计和重构。移除了所有在关键路径(如日志系统、数据库路径解析及窗口管理)中硬编码的 `.expect()``.unwrap()` 调用。这确保了在 Windows Server 2019 等权限高度受限的环境下,程序仍能平稳启动而不会闪退。
1213
- **日志系统自动降级**:当日志目录不可写或无法访问时,日志系统现在会自动降级为仅控制台输出模式,彻底消除了由于磁盘写入权限导致的启动 Panic。
1314

1415
### 🐞 修复
1516

1617
- **生产环境数据库锁定修复**:解决了在生产环境下,由于初始化过程中冗余的文件句柄请求导致的“attempt to write a readonly database”关键错误。
1718
- **代理路由优先级修复**:解决了通配分组路径(如 `/{group}/...`)可能意外拦截 `/switch``/compat``/compat_mode` 等特定功能前缀的冲突问题。该修复确保了所有访问模式都能正确分发,解决了组合路径下可能出现的 404 错误。
18-
- **兼容模式别名支持**:新增了 `compat` 简写别名作为 `compat_mode` 的便捷替代方案(例如:`/group/compat/v1/messages`),提升了 API 调用的易用性
19+
- **兼容模式别名支持**:新增了 `compat` 简写别名作为 `compat_mode` 的便捷替代方案(例如:`/group/compat/v1/messages`),提升了 API 调用体验
1920
- **代理统计校准**:修复了在 `工具兼容模式` (tool_compat_mode) 和 `直接转发` (direct_forward) 模式下,输出 Token 统计始终为 0 的问题。现在系统能准确估算所有协议(OpenAI、Claude、Gemini、Ollama)中的**推理/思考内容 (Reasoning/Thinking)****工具调用参数**的 Token 消耗。
2021
- **统一缓存 Token 追踪**:实现了跨协议的缓存 Token 统一映射。现在能正确捕捉并记录来自不同协议的缓存数据(如 OpenAI 的 `prompt_cached_tokens`、Claude 的 `cache_read_input_tokens` 以及 Gemini 的 `cached_content_tokens`),并确保其在流式响应结束时准确入库。
2122
- **路径解析 Panic 修复**:解决了在部分受限环境下,若操作系统无法返回当前工作目录或应用数据目录时可能导致的 Panic。

src-tauri/src/commands/setting.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -710,7 +710,18 @@ fn upload_logo(image_path: String) -> Result<String> {
710710
// Backup
711711
// =================================================
712712
#[tauri::command]
713-
pub async fn backup_setting(app: AppHandle, backup_dir: Option<String>) -> Result<()> {
713+
pub async fn backup_setting(
714+
app: AppHandle,
715+
state: State<'_, Arc<RwLock<MainStore>>>,
716+
backup_dir: Option<String>,
717+
) -> Result<()> {
718+
// 1. Ensure all data is flushed from WAL to the main DB file before copying.
719+
// This is critical when WAL mode is enabled.
720+
{
721+
let store = state.read().map_err(|e| AppError::Db(StoreError::LockError(e.to_string())))?;
722+
store.checkpoint().map_err(AppError::Db)?;
723+
}
724+
714725
let result = tokio::spawn(async move {
715726
DbBackup::new(&app, BackupConfig { backup_dir })
716727
.and_then(|mut backup| backup.backup_to_directory())

src-tauri/src/db/main_store.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,28 @@ impl MainStore {
279279
}
280280
}
281281

282+
/// Performs a database checkpoint, flushing all WAL data to the main database file.
283+
/// This is critical before performing file-level backups in WAL mode.
284+
pub fn checkpoint(&self) -> Result<(), StoreError> {
285+
let conn = self
286+
.conn
287+
.lock()
288+
.map_err(|e| StoreError::LockError(e.to_string()))?;
289+
290+
// PRAGMA wal_checkpoint returns rows, so we use query_row to handle it correctly.
291+
// TRUNCATE ensures the WAL file is actually integrated and reduced in size.
292+
let _ = conn
293+
.query_row("PRAGMA wal_checkpoint(TRUNCATE);", [], |_| Ok(()))
294+
.map_err(|e| {
295+
log::error!("Failed to checkpoint database: {}", e);
296+
StoreError::from(e)
297+
})?;
298+
299+
Ok(())
300+
}
301+
302+
/// Reopens the database connection. This is useful during restoration when the physical file is replaced.
303+
282304
/// Reopens the database connection. This is useful during restoration when the physical file is replaced.
283305
pub fn reopen<P: AsRef<Path>>(&mut self, db_path: P) -> Result<(), StoreError> {
284306
let mut conn_guard = self

0 commit comments

Comments
 (0)