fix(io): replace blocking I/O with async in hot paths

Three subsystems were using std::fs (blocking) inside async context,
which stalls the tokio runtime thread during I/O:

- DashboardMetrics::save(): now uses tokio::fs::write + rename
- TokenStore::flush_last_used(): now uses tokio::fs for batch updates
- AuditLog::log(): moved file write to spawn_blocking (fire-and-forget)

The background task and shutdown handler now properly .await the
async save/flush methods. AuditLog writer wrapped in Arc for
cross-thread access from spawn_blocking.
This commit is contained in:
2026-04-02 12:17:47 +00:00
parent 848f5f5571
commit be7e882391
4 changed files with 38 additions and 30 deletions

View File

@@ -111,8 +111,8 @@ impl DashboardMetrics {
metrics
}
/// Save current metrics to disk
pub fn save(&self) {
/// Save current metrics to disk (async to avoid blocking the runtime)
pub async fn save(&self) {
let Some(path) = &self.persist_path else {
return;
};
@@ -134,8 +134,8 @@ impl DashboardMetrics {
// Atomic write: write to tmp then rename
let tmp = path.with_extension("json.tmp");
if let Ok(data) = serde_json::to_string_pretty(&snap) {
if std::fs::write(&tmp, &data).is_ok() {
let _ = std::fs::rename(&tmp, path);
if tokio::fs::write(&tmp, &data).await.is_ok() {
let _ = tokio::fs::rename(&tmp, path).await;
}
}
}
@@ -317,8 +317,8 @@ mod tests {
assert_eq!(m.get_registry_uploads("unknown"), 0);
}
#[test]
fn test_persistence_save_and_load() {
#[tokio::test]
async fn test_persistence_save_and_load() {
let tmp = TempDir::new().unwrap();
let path = tmp.path().to_str().unwrap();
@@ -329,7 +329,7 @@ mod tests {
m.record_download("docker");
m.record_upload("maven");
m.record_cache_hit();
m.save();
m.save().await;
}
// Load in new instance