2020-02-29 01:11:20 -07:00
|
|
|
use crate::cluster_info::{ClusterInfo, MAX_SNAPSHOT_HASHES};
|
2020-06-17 09:27:03 -06:00
|
|
|
use solana_runtime::{snapshot_package::AccountsPackageReceiver, snapshot_utils};
|
2020-03-05 23:52:31 -07:00
|
|
|
use solana_sdk::{clock::Slot, hash::Hash};
|
2020-01-23 10:20:34 -07:00
|
|
|
use std::{
|
|
|
|
sync::{
|
|
|
|
atomic::{AtomicBool, Ordering},
|
|
|
|
mpsc::RecvTimeoutError,
|
2020-04-21 12:54:45 -07:00
|
|
|
Arc,
|
2020-01-23 10:20:34 -07:00
|
|
|
},
|
|
|
|
thread::{self, Builder, JoinHandle},
|
|
|
|
time::Duration,
|
|
|
|
};
|
2019-10-18 15:58:16 -06:00
|
|
|
|
|
|
|
pub struct SnapshotPackagerService {
|
|
|
|
t_snapshot_packager: JoinHandle<()>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SnapshotPackagerService {
|
2020-02-20 11:46:13 -08:00
|
|
|
pub fn new(
|
2020-04-16 15:12:20 -07:00
|
|
|
snapshot_package_receiver: AccountsPackageReceiver,
|
2020-03-05 23:52:31 -07:00
|
|
|
starting_snapshot_hash: Option<(Slot, Hash)>,
|
2020-02-20 11:46:13 -08:00
|
|
|
exit: &Arc<AtomicBool>,
|
2020-04-21 12:54:45 -07:00
|
|
|
cluster_info: &Arc<ClusterInfo>,
|
2020-02-20 11:46:13 -08:00
|
|
|
) -> Self {
|
2019-10-18 15:58:16 -06:00
|
|
|
let exit = exit.clone();
|
2020-02-20 11:46:13 -08:00
|
|
|
let cluster_info = cluster_info.clone();
|
2020-03-05 23:52:31 -07:00
|
|
|
|
2019-10-18 15:58:16 -06:00
|
|
|
let t_snapshot_packager = Builder::new()
|
|
|
|
.name("solana-snapshot-packager".to_string())
|
2020-02-21 18:42:24 -08:00
|
|
|
.spawn(move || {
|
2020-02-20 11:46:13 -08:00
|
|
|
let mut hashes = vec![];
|
2020-03-05 23:52:31 -07:00
|
|
|
if let Some(starting_snapshot_hash) = starting_snapshot_hash {
|
|
|
|
hashes.push(starting_snapshot_hash);
|
|
|
|
}
|
2020-04-21 12:54:45 -07:00
|
|
|
cluster_info.push_snapshot_hashes(hashes.clone());
|
2020-02-21 18:42:24 -08:00
|
|
|
loop {
|
|
|
|
if exit.load(Ordering::Relaxed) {
|
|
|
|
break;
|
|
|
|
}
|
2020-01-23 11:20:37 -07:00
|
|
|
|
2020-02-21 18:42:24 -08:00
|
|
|
match snapshot_package_receiver.recv_timeout(Duration::from_secs(1)) {
|
|
|
|
Ok(mut snapshot_package) => {
|
|
|
|
// Only package the latest
|
|
|
|
while let Ok(new_snapshot_package) =
|
|
|
|
snapshot_package_receiver.try_recv()
|
|
|
|
{
|
|
|
|
snapshot_package = new_snapshot_package;
|
|
|
|
}
|
2020-03-05 23:52:31 -07:00
|
|
|
if let Err(err) =
|
|
|
|
snapshot_utils::archive_snapshot_package(&snapshot_package)
|
|
|
|
{
|
2020-02-21 18:42:24 -08:00
|
|
|
warn!("Failed to create snapshot archive: {}", err);
|
2020-03-02 12:38:16 -07:00
|
|
|
} else {
|
|
|
|
hashes.push((snapshot_package.root, snapshot_package.hash));
|
|
|
|
while hashes.len() > MAX_SNAPSHOT_HASHES {
|
|
|
|
hashes.remove(0);
|
|
|
|
}
|
2020-04-21 12:54:45 -07:00
|
|
|
cluster_info.push_snapshot_hashes(hashes.clone());
|
2020-02-21 18:42:24 -08:00
|
|
|
}
|
2020-01-23 11:20:37 -07:00
|
|
|
}
|
2020-02-21 18:42:24 -08:00
|
|
|
Err(RecvTimeoutError::Disconnected) => break,
|
|
|
|
Err(RecvTimeoutError::Timeout) => (),
|
2019-10-18 15:58:16 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
Self {
|
|
|
|
t_snapshot_packager,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-13 11:12:09 -07:00
|
|
|
pub fn join(self) -> thread::Result<()> {
|
2019-10-18 15:58:16 -06:00
|
|
|
self.t_snapshot_packager.join()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2020-01-10 09:49:36 +09:00
|
|
|
use bincode::serialize_into;
|
2020-06-17 09:27:03 -06:00
|
|
|
use solana_runtime::{
|
|
|
|
accounts_db::AccountStorageEntry,
|
|
|
|
bank::BankSlotDelta,
|
|
|
|
bank_forks::CompressionType,
|
2020-04-16 15:12:20 -07:00
|
|
|
snapshot_package::AccountsPackage,
|
2020-01-23 11:20:37 -07:00
|
|
|
snapshot_utils::{self, SNAPSHOT_STATUS_CACHE_FILE_NAME},
|
|
|
|
};
|
2020-02-20 11:46:13 -08:00
|
|
|
use solana_sdk::hash::Hash;
|
2019-12-05 14:58:02 -05:00
|
|
|
use std::{
|
2020-01-23 11:20:37 -07:00
|
|
|
fs::{self, remove_dir_all, OpenOptions},
|
2019-12-05 14:58:02 -05:00
|
|
|
io::Write,
|
|
|
|
path::{Path, PathBuf},
|
|
|
|
};
|
2019-10-18 15:58:16 -06:00
|
|
|
use tempfile::TempDir;
|
|
|
|
|
2019-12-05 14:58:02 -05:00
|
|
|
// Create temporary placeholder directory for all test files
|
|
|
|
fn make_tmp_dir_path() -> PathBuf {
|
|
|
|
let out_dir = std::env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_string());
|
|
|
|
let path = PathBuf::from(format!("{}/tmp/test_package_snapshots", out_dir));
|
|
|
|
|
|
|
|
// whack any possible collision
|
|
|
|
let _ignored = std::fs::remove_dir_all(&path);
|
|
|
|
// whack any possible collision
|
|
|
|
let _ignored = std::fs::remove_file(&path);
|
|
|
|
|
|
|
|
path
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_package_snapshots_relative_ledger_path() {
|
|
|
|
let temp_dir = make_tmp_dir_path();
|
|
|
|
create_and_verify_snapshot(&temp_dir);
|
|
|
|
remove_dir_all(temp_dir).expect("should remove tmp dir");
|
|
|
|
}
|
|
|
|
|
2019-10-18 15:58:16 -06:00
|
|
|
#[test]
|
|
|
|
fn test_package_snapshots() {
|
2019-12-05 14:58:02 -05:00
|
|
|
create_and_verify_snapshot(TempDir::new().unwrap().path())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn create_and_verify_snapshot(temp_dir: &Path) {
|
|
|
|
let accounts_dir = temp_dir.join("accounts");
|
|
|
|
let snapshots_dir = temp_dir.join("snapshots");
|
|
|
|
let snapshot_package_output_path = temp_dir.join("snapshots_output");
|
2019-10-18 15:58:16 -06:00
|
|
|
fs::create_dir_all(&snapshot_package_output_path).unwrap();
|
|
|
|
|
2019-12-13 16:46:16 -08:00
|
|
|
fs::create_dir_all(&accounts_dir).unwrap();
|
2019-10-18 15:58:16 -06:00
|
|
|
// Create some storage entries
|
|
|
|
let storage_entries: Vec<_> = (0..5)
|
|
|
|
.map(|i| Arc::new(AccountStorageEntry::new(&accounts_dir, 0, i, 10)))
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
// Create some fake snapshot
|
|
|
|
let snapshots_paths: Vec<_> = (0..5)
|
|
|
|
.map(|i| {
|
2020-03-25 18:46:41 +09:00
|
|
|
let snapshot_file_name = format!("{}", i);
|
|
|
|
let snapshots_dir = snapshots_dir.join(&snapshot_file_name);
|
|
|
|
fs::create_dir_all(&snapshots_dir).unwrap();
|
|
|
|
let fake_snapshot_path = snapshots_dir.join(&snapshot_file_name);
|
2019-10-18 15:58:16 -06:00
|
|
|
let mut fake_snapshot_file = OpenOptions::new()
|
|
|
|
.read(true)
|
|
|
|
.write(true)
|
|
|
|
.create(true)
|
|
|
|
.open(&fake_snapshot_path)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
fake_snapshot_file.write_all(b"Hello, world!").unwrap();
|
|
|
|
fake_snapshot_path
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
// Create directory of hard links for snapshots
|
2019-12-05 14:58:02 -05:00
|
|
|
let link_snapshots_dir = tempfile::tempdir_in(&temp_dir).unwrap();
|
2019-10-18 15:58:16 -06:00
|
|
|
for snapshots_path in snapshots_paths {
|
|
|
|
let snapshot_file_name = snapshots_path.file_name().unwrap();
|
2020-03-25 18:46:41 +09:00
|
|
|
let link_snapshots_dir = link_snapshots_dir.path().join(snapshot_file_name);
|
|
|
|
fs::create_dir_all(&link_snapshots_dir).unwrap();
|
|
|
|
let link_path = link_snapshots_dir.join(snapshot_file_name);
|
2019-10-18 15:58:16 -06:00
|
|
|
fs::hard_link(&snapshots_path, &link_path).unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a packageable snapshot
|
2020-02-24 13:37:14 -07:00
|
|
|
let output_tar_path = snapshot_utils::get_snapshot_archive_path(
|
|
|
|
&snapshot_package_output_path,
|
|
|
|
&(42, Hash::default()),
|
2020-04-03 13:13:49 -07:00
|
|
|
&CompressionType::Bzip2,
|
2020-02-24 13:37:14 -07:00
|
|
|
);
|
2020-04-16 15:12:20 -07:00
|
|
|
let snapshot_package = AccountsPackage::new(
|
|
|
|
5,
|
2019-10-18 15:58:16 -06:00
|
|
|
5,
|
|
|
|
vec![],
|
|
|
|
link_snapshots_dir,
|
2020-02-21 15:27:55 +09:00
|
|
|
vec![storage_entries],
|
2019-10-18 15:58:16 -06:00
|
|
|
output_tar_path.clone(),
|
2020-02-20 11:46:13 -08:00
|
|
|
Hash::default(),
|
2020-04-03 13:13:49 -07:00
|
|
|
CompressionType::Bzip2,
|
2019-10-18 15:58:16 -06:00
|
|
|
);
|
|
|
|
|
|
|
|
// Make tarball from packageable snapshot
|
2020-01-23 11:20:37 -07:00
|
|
|
snapshot_utils::archive_snapshot_package(&snapshot_package).unwrap();
|
2019-10-18 15:58:16 -06:00
|
|
|
|
|
|
|
// before we compare, stick an empty status_cache in this dir so that the package comparision works
|
|
|
|
// This is needed since the status_cache is added by the packager and is not collected from
|
|
|
|
// the source dir for snapshots
|
2020-02-10 20:11:37 +09:00
|
|
|
let dummy_slot_deltas: Vec<BankSlotDelta> = vec![];
|
2020-01-10 09:49:36 +09:00
|
|
|
snapshot_utils::serialize_snapshot_data_file(
|
|
|
|
&snapshots_dir.join(SNAPSHOT_STATUS_CACHE_FILE_NAME),
|
|
|
|
|stream| {
|
|
|
|
serialize_into(stream, &dummy_slot_deltas)?;
|
|
|
|
Ok(())
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
2019-10-18 15:58:16 -06:00
|
|
|
|
2020-01-23 11:20:37 -07:00
|
|
|
// Check archive is correct
|
2020-04-03 13:13:49 -07:00
|
|
|
snapshot_utils::verify_snapshot_archive(
|
|
|
|
output_tar_path,
|
|
|
|
snapshots_dir,
|
|
|
|
accounts_dir,
|
|
|
|
CompressionType::Bzip2,
|
|
|
|
);
|
2019-10-18 15:58:16 -06:00
|
|
|
}
|
|
|
|
}
|