Write the current snapshot to disk if configured and there are unsaved changes. Atomic: write to a sibling temp file, then rename.
(&self)
| 208 | /// Write the current snapshot to disk if configured and there are |
| 209 | /// unsaved changes. Atomic: write to a sibling temp file, then rename. |
| 210 | pub fn persist(&self) -> std::io::Result<()> { |
| 211 | let Some(path) = self.persist_path.as_ref() else { |
| 212 | return Ok(()); |
| 213 | }; |
| 214 | if !self.dirty.swap(false, Ordering::Relaxed) { |
| 215 | return Ok(()); |
| 216 | } |
| 217 | // Reset the overflow-warned flag so the next cycle can log again if |
| 218 | // overflow is still happening. We do this regardless of whether the |
| 219 | // write succeeds; worst case is an extra log line per cycle. |
| 220 | self.overflow_warned_this_cycle |
| 221 | .store(false, Ordering::Relaxed); |
| 222 | |
| 223 | let snapshot = self.snapshot(); |
| 224 | let content = serde_json::to_string_pretty(&snapshot) |
| 225 | .map_err(|e| std::io::Error::other(format!("failed to serialize stats: {e}")))?; |
| 226 | |
| 227 | if let Some(parent) = path.parent() { |
| 228 | std::fs::create_dir_all(parent)?; |
| 229 | } |
| 230 | let tmp_path = path.with_extension("json.tmp"); |
| 231 | std::fs::write(&tmp_path, content)?; |
| 232 | |
| 233 | #[cfg(unix)] |
| 234 | { |
| 235 | use std::os::unix::fs::PermissionsExt; |
| 236 | std::fs::set_permissions(&tmp_path, std::fs::Permissions::from_mode(0o600))?; |
| 237 | } |
| 238 | |
| 239 | std::fs::rename(&tmp_path, path)?; |
| 240 | debug!(path = %path.display(), "stats persisted to disk"); |
| 241 | Ok(()) |
| 242 | } |
| 243 | } |
| 244 | |
| 245 | impl std::fmt::Debug for Stats { |