diff options
| author | tv <tv@krebsco.de> | 2026-10-25 19:59:14 +0100 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-10-25 19:59:14 +0100 |
| commit | 72472131a4bb3798759884da597cfb4ef7793782 (patch) | |
| tree | 5f094dfda00da31e1b564cc033ef5f1d24ba89df /src/files.rs | |
| parent | 866924934d635df8a8f222a022f806c44581a5f5 (diff) | |
allow to persist document
Diffstat (limited to 'src/files.rs')
| -rw-r--r-- | src/files.rs | 41 |
1 files changed, 36 insertions, 5 deletions
diff --git a/src/files.rs b/src/files.rs index 81c8b1e..7bf9117 100644 --- a/src/files.rs +++ b/src/files.rs @@ -1,9 +1,12 @@ //! The files this node reads and writes for itself, and how much it did (`/admin/stats`, -//! `disk`): the state file (`swarm::state_file`) and a backup to recover from -//! (`swarm::bootstrap`). Not the Mycelium daemon's files, configuration or logs. +//! `disk`): the state file (`swarm::state_file`), the document file +//! (`document::file`) and a backup to recover from (`swarm::bootstrap`). Not the +//! Mycelium daemon's files, configuration or logs. //! //! Writes replace a file atomically: a temporary file next to it, flushed to disk, then -//! renamed over it, so a crash leaves the old content or the new one. +//! renamed over it, so a crash leaves the old content or the new one. Appends add to the +//! end and are flushed to disk before they count as done; a crash may leave the last one +//! cut short, which the file's reader has to expect. use std::io::Write; use std::path::{Path, PathBuf}; @@ -13,15 +16,17 @@ use std::sync::{Mutex, MutexGuard}; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub(crate) enum FileKind { State, + Document, Backup, } impl FileKind { - pub(crate) const ALL: [FileKind; 2] = [FileKind::State, FileKind::Backup]; + pub(crate) const ALL: [FileKind; 3] = [FileKind::State, FileKind::Document, FileKind::Backup]; pub(crate) fn as_str(self) -> &'static str { match self { FileKind::State => "state", + FileKind::Document => "document", FileKind::Backup => "backup", } } @@ -36,9 +41,12 @@ impl FileKind { pub(crate) struct Counts { pub(crate) reads: u64, pub(crate) read_bytes: u64, + /// Whole files written. pub(crate) writes: u64, pub(crate) written_bytes: u64, - /// Reads or writes that failed (a missing file read is no failure). + pub(crate) appends: u64, + pub(crate) appended_bytes: u64, + /// Reads, writes or appends that failed (a missing file read is no failure). pub(crate) errors: u64, } @@ -96,6 +104,23 @@ impl Files { result } + /// Adds `bytes` to the end of the file (which must exist), flushed to disk. + pub(crate) fn append(&self, kind: FileKind, path: &Path, bytes: &[u8]) -> std::io::Result<()> { + let result = (|| { + let mut file = std::fs::OpenOptions::new().append(true).open(path)?; + file.write_all(bytes)?; + file.sync_data() + })(); + self.count(kind, |counts| match &result { + Ok(()) => { + counts.appends += 1; + counts.appended_bytes += bytes.len() as u64; + } + Err(_) => counts.errors += 1, + }); + result + } + /// Moves an unusable file aside (`<path><suffix>`), returning where to. pub(crate) fn move_aside(&self, path: &Path, suffix: &str) -> std::io::Result<PathBuf> { let aside = with_suffix(path, suffix); @@ -167,6 +192,12 @@ mod tests { assert_eq!(kind, FileKind::State); assert_eq!((state.writes, state.written_bytes), (2, 8)); assert_eq!((state.reads, state.read_bytes, state.errors), (1, 5, 0)); + files.append(FileKind::State, &path, b"!").unwrap(); + assert_eq!( + files.read(FileKind::State, &path).unwrap().as_deref(), + Some(&b"three!"[..]) + ); + assert_eq!(files.counts()[0].1.appended_bytes, 1); assert_eq!(files.counts()[1].1.reads, 0); std::fs::remove_dir_all(&dir).unwrap(); |
