mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-08-02 02:05:57 +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:
+25
-42
@@ -1214,59 +1214,42 @@ pub fn run() {
|
||||
tauri::async_runtime::spawn(async move {
|
||||
const SESSION_SYNC_INTERVAL_SECS: u64 = 60;
|
||||
|
||||
fn run_step<T>(name: &str, result: Result<T, crate::error::AppError>) {
|
||||
if let Err(e) = result {
|
||||
log::warn!("{name} failed: {e}");
|
||||
async fn run_session_sync(db: std::sync::Arc<crate::database::Database>, backfill: bool) {
|
||||
let _guard = crate::services::session_usage::session_sync_mutex()
|
||||
.lock()
|
||||
.await;
|
||||
let task = tauri::async_runtime::spawn_blocking(move || {
|
||||
if backfill {
|
||||
if let Err(error) = db.backfill_missing_usage_costs() {
|
||||
log::warn!("Usage cost startup backfill failed: {error}");
|
||||
}
|
||||
}
|
||||
crate::services::session_usage::sync_all_unlocked(&db)
|
||||
});
|
||||
match task.await {
|
||||
Ok(result) if !result.errors.is_empty() => {
|
||||
log::warn!(
|
||||
"Session usage sync completed with {} error(s)",
|
||||
result.errors.len()
|
||||
);
|
||||
}
|
||||
Ok(_) => {}
|
||||
Err(error) => log::warn!("Session usage blocking task failed: {error}"),
|
||||
}
|
||||
}
|
||||
|
||||
let db = &db_for_session_sync;
|
||||
|
||||
// 首次同步
|
||||
run_step(
|
||||
"Usage cost startup backfill",
|
||||
db.backfill_missing_usage_costs(),
|
||||
);
|
||||
run_step(
|
||||
"Session usage initial sync",
|
||||
crate::services::session_usage::sync_claude_session_logs(db),
|
||||
);
|
||||
run_step(
|
||||
"Codex usage initial sync",
|
||||
crate::services::session_usage_codex::sync_codex_usage(db),
|
||||
);
|
||||
run_step(
|
||||
"Gemini usage initial sync",
|
||||
crate::services::session_usage_gemini::sync_gemini_usage(db),
|
||||
);
|
||||
run_step(
|
||||
"OpenCode usage initial sync",
|
||||
crate::services::session_usage_opencode::sync_opencode_usage(db),
|
||||
);
|
||||
// 首次同步(含费用回填)
|
||||
run_session_sync(db_for_session_sync.clone(), true).await;
|
||||
|
||||
// 定期同步
|
||||
let mut interval = tokio::time::interval(std::time::Duration::from_secs(
|
||||
SESSION_SYNC_INTERVAL_SECS,
|
||||
));
|
||||
interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
|
||||
interval.tick().await; // skip immediate first tick
|
||||
loop {
|
||||
interval.tick().await;
|
||||
run_step(
|
||||
"Session usage periodic sync",
|
||||
crate::services::session_usage::sync_claude_session_logs(db),
|
||||
);
|
||||
run_step(
|
||||
"Codex usage periodic sync",
|
||||
crate::services::session_usage_codex::sync_codex_usage(db),
|
||||
);
|
||||
run_step(
|
||||
"Gemini usage periodic sync",
|
||||
crate::services::session_usage_gemini::sync_gemini_usage(db),
|
||||
);
|
||||
run_step(
|
||||
"OpenCode usage periodic sync",
|
||||
crate::services::session_usage_opencode::sync_opencode_usage(db),
|
||||
);
|
||||
run_session_sync(db_for_session_sync.clone(), false).await;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user