Fix hardlinking across filesystem boundaries (#5449)

* Fix hardlinking across filesystem boundaries

* create output dir for snapshot tar
This commit is contained in:
carllin
2019-08-07 13:12:53 -07:00
committed by GitHub
parent 2a17e90b7b
commit 8aa7a851ca
3 changed files with 47 additions and 43 deletions

View File

@ -293,6 +293,7 @@ impl BankForks {
&bank, &bank,
&slot_snapshot_paths[start..], &slot_snapshot_paths[start..],
tar_output_file, tar_output_file,
&config.snapshot_path,
)?; )?;
// Send the package to the packaging thread // Send the package to the packaging thread
@ -606,6 +607,7 @@ mod tests {
last_bank, last_bank,
&slot_snapshot_paths, &slot_snapshot_paths,
snapshot_utils::get_snapshot_tar_path(&snapshot_config.snapshot_package_output_path), snapshot_utils::get_snapshot_tar_path(&snapshot_config.snapshot_package_output_path),
&snapshot_config.snapshot_path,
) )
.unwrap(); .unwrap();
SnapshotPackagerService::package_snapshots(&snapshot_package).unwrap(); SnapshotPackagerService::package_snapshots(&snapshot_package).unwrap();
@ -738,7 +740,7 @@ mod tests {
let package_sender = { let package_sender = {
if slot == saved_slot as u64 { if slot == saved_slot as u64 {
// Only send one package on the real sende so that the packaging service // Only send one package on the real sender so that the packaging service
// doesn't take forever to run the packaging logic on all MAX_CACHE_ENTRIES // doesn't take forever to run the packaging logic on all MAX_CACHE_ENTRIES
// later // later
&sender &sender
@ -761,7 +763,22 @@ mod tests {
if slot == saved_slot as u64 { if slot == saved_slot as u64 {
let options = CopyOptions::new(); let options = CopyOptions::new();
fs_extra::dir::copy(&accounts_dir, &saved_accounts_dir, &options).unwrap(); fs_extra::dir::copy(&accounts_dir, &saved_accounts_dir, &options).unwrap();
fs_extra::dir::copy(&snapshots_dir, &saved_snapshots_dir, &options).unwrap(); let snapshot_paths: Vec<_> = fs::read_dir(&snapshot_config.snapshot_path)
.unwrap()
.filter_map(|entry| {
let e = entry.unwrap();
let file_path = e.path();
let file_name = file_path.file_name().unwrap();
file_name
.to_str()
.map(|s| s.parse::<u64>().ok().map(|_| file_path.clone()))
.unwrap_or(None)
})
.collect();
for snapshot_path in snapshot_paths {
fs_extra::dir::copy(&snapshot_path, &saved_snapshots_dir, &options).unwrap();
}
} }
} }
@ -796,9 +813,7 @@ mod tests {
// Check the tar we cached the state for earlier was generated correctly // Check the tar we cached the state for earlier was generated correctly
snapshot_utils::tests::verify_snapshot_tar( snapshot_utils::tests::verify_snapshot_tar(
saved_tar, saved_tar,
saved_snapshots_dir saved_snapshots_dir.path(),
.path()
.join(snapshots_dir.path().file_name().unwrap()),
saved_accounts_dir saved_accounts_dir
.path() .path()
.join(accounts_dir.path().file_name().unwrap()), .join(accounts_dir.path().file_name().unwrap()),

View File

@ -11,6 +11,7 @@ use std::sync::mpsc::{Receiver, RecvTimeoutError, Sender};
use std::sync::Arc; use std::sync::Arc;
use std::thread::{self, Builder, JoinHandle}; use std::thread::{self, Builder, JoinHandle};
use std::time::Duration; use std::time::Duration;
use tempfile::TempDir;
pub type SnapshotPackageSender = Sender<SnapshotPackage>; pub type SnapshotPackageSender = Sender<SnapshotPackage>;
pub type SnapshotPackageReceiver = Receiver<SnapshotPackage>; pub type SnapshotPackageReceiver = Receiver<SnapshotPackage>;
@ -19,31 +20,25 @@ pub const TAR_SNAPSHOTS_DIR: &str = "snapshots";
pub const TAR_ACCOUNTS_DIR: &str = "accounts"; pub const TAR_ACCOUNTS_DIR: &str = "accounts";
pub struct SnapshotPackage { pub struct SnapshotPackage {
snapshot_path: PathBuf, snapshot_links: TempDir,
storage_entries: Vec<Arc<AccountStorageEntry>>, storage_entries: Vec<Arc<AccountStorageEntry>>,
tar_output_file: PathBuf, tar_output_file: PathBuf,
} }
impl SnapshotPackage { impl SnapshotPackage {
pub fn new( pub fn new(
snapshot_path: PathBuf, snapshot_links: TempDir,
storage_entries: Vec<Arc<AccountStorageEntry>>, storage_entries: Vec<Arc<AccountStorageEntry>>,
tar_output_file: PathBuf, tar_output_file: PathBuf,
) -> Self { ) -> Self {
Self { Self {
snapshot_path, snapshot_links,
storage_entries, storage_entries,
tar_output_file, tar_output_file,
} }
} }
} }
impl Drop for SnapshotPackage {
fn drop(&mut self) {
let _ = fs::remove_dir_all(&self.snapshot_path);
}
}
pub struct SnapshotPackagerService { pub struct SnapshotPackagerService {
t_snapshot_packager: JoinHandle<()>, t_snapshot_packager: JoinHandle<()>,
} }
@ -72,11 +67,19 @@ impl SnapshotPackagerService {
} }
pub fn package_snapshots(snapshot_package: &SnapshotPackage) -> Result<()> { pub fn package_snapshots(snapshot_package: &SnapshotPackage) -> Result<()> {
let tar_dir = snapshot_package
.tar_output_file
.parent()
.expect("Tar output path is invalid");
fs::create_dir_all(tar_dir)?;
// Create the tar builder // Create the tar builder
let tar_gz = tempfile::Builder::new() let tar_gz = tempfile::Builder::new()
.prefix("new_state") .prefix("new_state")
.suffix(".tgz") .suffix(".tgz")
.tempfile()?; .tempfile_in(tar_dir)?;
let temp_tar_path = tar_gz.path(); let temp_tar_path = tar_gz.path();
let enc = GzEncoder::new(&tar_gz, Compression::default()); let enc = GzEncoder::new(&tar_gz, Compression::default());
let mut tar = tar::Builder::new(enc); let mut tar = tar::Builder::new(enc);
@ -88,9 +91,8 @@ impl SnapshotPackagerService {
// that was created to persist those snapshots while this package was being created // that was created to persist those snapshots while this package was being created
let res = tar.append_dir_all( let res = tar.append_dir_all(
tar_output_snapshots_dir, tar_output_snapshots_dir,
snapshot_package.snapshot_path.as_path(), snapshot_package.snapshot_links.path(),
); );
let _ = fs::remove_dir_all(snapshot_package.snapshot_path.as_path());
res?; res?;
// Add the AppendVecs into the compressible list // Add the AppendVecs into the compressible list
@ -171,11 +173,10 @@ mod tests {
.collect(); .collect();
// Create directory of hard links for snapshots // Create directory of hard links for snapshots
let link_snapshots_dir = temp_dir.path().join("link_snapshots"); let link_snapshots_dir = tempfile::tempdir_in(temp_dir.path()).unwrap();
fs::create_dir_all(&link_snapshots_dir).unwrap();
for snapshots_path in snapshots_paths { for snapshots_path in snapshots_paths {
let snapshot_file_name = snapshots_path.file_name().unwrap(); let snapshot_file_name = snapshots_path.file_name().unwrap();
let link_path = link_snapshots_dir.join(snapshot_file_name); let link_path = link_snapshots_dir.path().join(snapshot_file_name);
fs::hard_link(&snapshots_path, &link_path).unwrap(); fs::hard_link(&snapshots_path, &link_path).unwrap();
} }

View File

@ -48,24 +48,16 @@ impl SlotSnapshotPaths {
} }
} }
pub fn package_snapshot<Q: AsRef<Path>>( pub fn package_snapshot<P: AsRef<Path>, Q: AsRef<Path>>(
bank: &Bank, bank: &Bank,
snapshot_files: &[SlotSnapshotPaths], snapshot_files: &[SlotSnapshotPaths],
snapshot_package_output_file: Q, snapshot_package_output_file: P,
snapshot_path: Q,
) -> Result<SnapshotPackage> { ) -> Result<SnapshotPackage> {
let slot = bank.slot(); let slot = bank.slot();
// Hard link all the snapshots we need for this package // Hard link all the snapshots we need for this package
let snapshot_hard_links_dir = get_snapshots_hardlink_dir_for_package( let snapshot_hard_links_dir = tempfile::tempdir_in(snapshot_path)?;
snapshot_package_output_file
.as_ref()
.parent()
.expect("Invalid output path for tar"),
slot,
);
let _ = fs::remove_dir_all(&snapshot_hard_links_dir);
fs::create_dir_all(&snapshot_hard_links_dir)?;
// Get a reference to all the relevant AccountStorageEntries // Get a reference to all the relevant AccountStorageEntries
let account_storage_entries = bank.rc.get_storage_entries(); let account_storage_entries = bank.rc.get_storage_entries();
@ -76,18 +68,19 @@ pub fn package_snapshot<Q: AsRef<Path>>(
slot, slot,
account_storage_entries.len() account_storage_entries.len()
); );
let package = SnapshotPackage::new(
snapshot_hard_links_dir.clone(),
account_storage_entries,
snapshot_package_output_file.as_ref().to_path_buf(),
);
// Any errors from this point on will cause the above SnapshotPackage to drop, clearing // Any errors from this point on will cause the above SnapshotPackage to drop, clearing
// any temporary state created for the SnapshotPackage (like the snapshot_hard_links_dir) // any temporary state created for the SnapshotPackage (like the snapshot_hard_links_dir)
for files in snapshot_files { for files in snapshot_files {
files.hardlink_snapshot_directory(&snapshot_hard_links_dir)?; files.hardlink_snapshot_directory(snapshot_hard_links_dir.path())?;
} }
let package = SnapshotPackage::new(
snapshot_hard_links_dir,
account_storage_entries,
snapshot_package_output_file.as_ref().to_path_buf(),
);
Ok(package) Ok(package)
} }
@ -228,11 +221,6 @@ fn get_bank_snapshot_dir<P: AsRef<Path>>(path: P, slot: u64) -> PathBuf {
path.as_ref().join(slot.to_string()) path.as_ref().join(slot.to_string())
} }
fn get_snapshots_hardlink_dir_for_package<P: AsRef<Path>>(parent_dir: P, slot: u64) -> PathBuf {
let file_name = format!("snapshot_{}_hard_links", slot);
parent_dir.as_ref().join(file_name)
}
fn get_io_error(error: &str) -> Error { fn get_io_error(error: &str) -> Error {
warn!("BankForks error: {:?}", error); warn!("BankForks error: {:?}", error);
Error::IO(IOError::new(ErrorKind::Other, error)) Error::IO(IOError::new(ErrorKind::Other, error))