Skip to content

Commit

Permalink
feat: write prefix record atomically (#1063)
Browse files Browse the repository at this point in the history
  • Loading branch information
wolfv authored Feb 18, 2025
1 parent c852486 commit 5b786f1
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 4 deletions.
2 changes: 1 addition & 1 deletion crates/rattler_conda_types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ serde-untagged = { workspace = true }
serde_yaml = { workspace = true }
smallvec = { workspace = true, features = ["serde", "const_new", "const_generics", "union"] }
strum = { workspace = true, features = ["derive"] }
tempfile = { workspace = true }
thiserror = { workspace = true }
tracing = { workspace = true }
typed-path = { workspace = true }
Expand All @@ -50,7 +51,6 @@ fs-err = { workspace = true }
rand = { workspace = true }
insta = { workspace = true, features = ["yaml", "redactions", "toml", "glob", "filters"] }
rattler_package_streaming = { path = "../rattler_package_streaming", default-features = false, features = ["rustls-tls"] }
tempfile = { workspace = true }
rstest = { workspace = true }
assert_matches = { workspace = true }
hex-literal = { workspace = true }
Expand Down
43 changes: 40 additions & 3 deletions crates/rattler_conda_types/src/prefix_record.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ use crate::package::FileMode;
use crate::repo_data::RecordFromPath;
use crate::repo_data_record::RepoDataRecord;
use crate::PackageRecord;
use fs_err::File;
use rattler_digest::serde::SerializableHash;
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use serde_with::serde_as;
use std::io::{BufWriter, Read};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use tempfile::NamedTempFile;

#[cfg(feature = "rayon")]
use rayon::prelude::*;
Expand Down Expand Up @@ -234,8 +234,45 @@ impl PrefixRecord {
path: impl AsRef<Path>,
pretty: bool,
) -> Result<(), std::io::Error> {
let file = File::create(path.as_ref())?;
self.write_to(BufWriter::with_capacity(50 * 1024, file), pretty)
let path = path.as_ref();
let parent = path.parent().ok_or_else(|| {
std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"Failed to get parent directory of path '{}'",
path.display()
),
)
})?;

// Use a temporary file in the same directory for atomic writes
let temp_file = NamedTempFile::with_prefix_in("prefix_record_", parent).map_err(|e| {
std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"Failed to create temporary file in '{}': {}",
parent.display(),
e
),
)
})?;

// Write to temp file with buffered writer
let writer = BufWriter::with_capacity(64 * 1024, &temp_file);
self.write_to(writer, pretty)?;

// Make sure that all data is written to disk
temp_file.as_file().sync_all()?;

// Atomically rename the temp file to the target path
temp_file.persist(path).map_err(|e| {
std::io::Error::new(
std::io::ErrorKind::Other,
format!("Failed to persist file {}: {}", path.display(), e),
)
})?;

Ok(())
}

/// Writes the contents of this instance to the file at the specified location.
Expand Down

0 comments on commit 5b786f1

Please sign in to comment.