mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-08-01 04:02:02 +08:00
perf(usage): coalesce session-sync notifications and serialize sync execution
Replace per-insert usage notifications with a single notification per sync pass, guard all sync entry points (startup backfill, 60s loop, manual sync) behind a process-wide single-flight tokio mutex, and move blocking file/DB work onto spawn_blocking with MissedTickBehavior::Skip. Extend SessionSyncResult with deferredFiles/suspectedDuplicates observability fields (saturating aggregation) and add a test seam that counts notifications even without an injected AppHandle.
This commit is contained in:
@@ -275,52 +275,18 @@ pub fn delete_model_pricing(state: State<'_, AppState>, model_id: String) -> Res
|
||||
|
||||
/// 手动触发会话日志同步
|
||||
#[tauri::command]
|
||||
pub fn sync_session_usage(
|
||||
pub async fn sync_session_usage(
|
||||
state: State<'_, AppState>,
|
||||
) -> Result<crate::services::session_usage::SessionSyncResult, AppError> {
|
||||
// 同步 Claude 会话日志
|
||||
let mut result = crate::services::session_usage::sync_claude_session_logs(&state.db)?;
|
||||
|
||||
// 同步 Codex 使用数据
|
||||
match crate::services::session_usage_codex::sync_codex_usage(&state.db) {
|
||||
Ok(codex_result) => {
|
||||
result.imported += codex_result.imported;
|
||||
result.skipped += codex_result.skipped;
|
||||
result.files_scanned += codex_result.files_scanned;
|
||||
result.errors.extend(codex_result.errors);
|
||||
}
|
||||
Err(e) => {
|
||||
result.errors.push(format!("Codex 同步失败: {e}"));
|
||||
}
|
||||
}
|
||||
|
||||
// 同步 Gemini 使用数据
|
||||
match crate::services::session_usage_gemini::sync_gemini_usage(&state.db) {
|
||||
Ok(gemini_result) => {
|
||||
result.imported += gemini_result.imported;
|
||||
result.skipped += gemini_result.skipped;
|
||||
result.files_scanned += gemini_result.files_scanned;
|
||||
result.errors.extend(gemini_result.errors);
|
||||
}
|
||||
Err(e) => {
|
||||
result.errors.push(format!("Gemini 同步失败: {e}"));
|
||||
}
|
||||
}
|
||||
|
||||
// 同步 OpenCode 使用数据
|
||||
match crate::services::session_usage_opencode::sync_opencode_usage(&state.db) {
|
||||
Ok(opencode_result) => {
|
||||
result.imported += opencode_result.imported;
|
||||
result.skipped += opencode_result.skipped;
|
||||
result.files_scanned += opencode_result.files_scanned;
|
||||
result.errors.extend(opencode_result.errors);
|
||||
}
|
||||
Err(e) => {
|
||||
result.errors.push(format!("OpenCode 同步失败: {e}"));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
let db = state.db.clone();
|
||||
let _guard = crate::services::session_usage::session_sync_mutex()
|
||||
.lock()
|
||||
.await;
|
||||
tauri::async_runtime::spawn_blocking(move || {
|
||||
crate::services::session_usage::sync_all_unlocked(&db)
|
||||
})
|
||||
.await
|
||||
.map_err(|error| AppError::Message(format!("会话用量同步任务失败: {error}")))
|
||||
}
|
||||
|
||||
/// 获取数据来源分布
|
||||
|
||||
Reference in New Issue
Block a user