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

@@ -270,9 +270,9 @@ impl TokenStore {
tokens
}
/// Flush pending last_used timestamps to disk.
/// Flush pending last_used timestamps to disk (async to avoid blocking runtime).
/// Called periodically by background task (every 30s).
pub fn flush_last_used(&self) {
pub async fn flush_last_used(&self) {
let pending: HashMap<String, u64> = {
let mut map = self.pending_last_used.write();
std::mem::take(&mut *map)
@@ -284,7 +284,7 @@ impl TokenStore {
for (file_prefix, timestamp) in &pending {
let file_path = self.storage_path.join(format!("{}.json", file_prefix));
let content = match fs::read_to_string(&file_path) {
let content = match tokio::fs::read_to_string(&file_path).await {
Ok(c) => c,
Err(_) => continue,
};
@@ -294,7 +294,7 @@ impl TokenStore {
};
info.last_used = Some(*timestamp);
if let Ok(json) = serde_json::to_string_pretty(&info) {
let _ = fs::write(&file_path, &json);
let _ = tokio::fs::write(&file_path, &json).await;
set_file_permissions_600(&file_path);
}
}
@@ -597,8 +597,8 @@ mod tests {
assert_eq!(store.list_tokens("user2").len(), 1);
}
#[test]
fn test_token_updates_last_used() {
#[tokio::test]
async fn test_token_updates_last_used() {
let temp_dir = TempDir::new().unwrap();
let store = TokenStore::new(temp_dir.path());
@@ -609,7 +609,7 @@ mod tests {
store.verify_token(&token).unwrap();
// last_used is deferred — flush to persist
store.flush_last_used();
store.flush_last_used().await;
let tokens = store.list_tokens("testuser");
assert!(tokens[0].last_used.is_some());