2021-05-26 09:15:46 -06:00
|
|
|
use solana_gossip::cluster_info::{ClusterInfo, MAX_SNAPSHOT_HASHES};
|
2021-01-11 10:21:15 -08:00
|
|
|
use solana_runtime::{snapshot_package::AccountsPackage, 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},
|
2021-01-11 10:21:15 -08:00
|
|
|
Arc, Mutex,
|
2020-01-23 10:20:34 -07:00
|
|
|
},
|
|
|
|
thread::{self, Builder, JoinHandle},
|
|
|
|
time::Duration,
|
|
|
|
};
|
2019-10-18 15:58:16 -06:00
|
|
|
|
2021-01-11 10:21:15 -08:00
|
|
|
pub type PendingSnapshotPackage = Arc<Mutex<Option<AccountsPackage>>>;
|
|
|
|
|
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(
|
2021-01-11 10:21:15 -08:00
|
|
|
pending_snapshot_package: PendingSnapshotPackage,
|
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>,
|
2021-05-12 10:32:27 -07:00
|
|
|
maximum_snapshots_to_retain: usize,
|
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()
|
2021-01-11 10:21:15 -08:00
|
|
|
.name("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
|
|
|
|
2021-01-11 10:21:15 -08:00
|
|
|
let snapshot_package = pending_snapshot_package.lock().unwrap().take();
|
|
|
|
if let Some(snapshot_package) = snapshot_package {
|
2021-05-12 10:32:27 -07:00
|
|
|
if let Err(err) = snapshot_utils::archive_snapshot_package(
|
|
|
|
&snapshot_package,
|
|
|
|
maximum_snapshots_to_retain,
|
|
|
|
) {
|
2021-01-11 10:21:15 -08:00
|
|
|
warn!("Failed to create snapshot archive: {}", err);
|
|
|
|
} else {
|
|
|
|
hashes.push((snapshot_package.slot, snapshot_package.hash));
|
|
|
|
while hashes.len() > MAX_SNAPSHOT_HASHES {
|
|
|
|
hashes.remove(0);
|
2020-02-21 18:42:24 -08:00
|
|
|
}
|
2021-01-11 10:21:15 -08:00
|
|
|
cluster_info.push_snapshot_hashes(hashes.clone());
|
2020-01-23 11:20:37 -07:00
|
|
|
}
|
2021-01-11 10:21:15 -08:00
|
|
|
} else {
|
|
|
|
std::thread::sleep(Duration::from_millis(100));
|
2019-10-18 15:58:16 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.unwrap();
|
2021-01-11 10:21:15 -08:00
|
|
|
|
2019-10-18 15:58:16 -06:00
|
|
|
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,
|
2021-01-07 22:45:42 -08:00
|
|
|
bank_forks::ArchiveFormat,
|
2020-04-16 15:12:20 -07:00
|
|
|
snapshot_package::AccountsPackage,
|
2020-06-19 06:38:37 +01:00
|
|
|
snapshot_utils::{self, SnapshotVersion, SNAPSHOT_STATUS_CACHE_FILE_NAME},
|
2020-01-23 11:20:37 -07:00
|
|
|
};
|
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(
|
2021-02-04 09:00:33 -06:00
|
|
|
snapshot_package_output_path,
|
2020-02-24 13:37:14 -07:00
|
|
|
&(42, Hash::default()),
|
2021-01-21 18:34:51 -08:00
|
|
|
ArchiveFormat::TarBzip2,
|
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(),
|
2021-01-07 22:45:42 -08:00
|
|
|
ArchiveFormat::TarBzip2,
|
2020-06-19 06:38:37 +01:00
|
|
|
SnapshotVersion::default(),
|
2019-10-18 15:58:16 -06:00
|
|
|
);
|
|
|
|
|
|
|
|
// Make tarball from packageable snapshot
|
2021-05-12 10:32:27 -07:00
|
|
|
snapshot_utils::archive_snapshot_package(
|
|
|
|
&snapshot_package,
|
|
|
|
snapshot_utils::DEFAULT_MAX_SNAPSHOTS_TO_RETAIN,
|
|
|
|
)
|
|
|
|
.unwrap();
|
2019-10-18 15:58:16 -06:00
|
|
|
|
2020-06-17 21:54:52 -06:00
|
|
|
// before we compare, stick an empty status_cache in this dir so that the package comparison works
|
2019-10-18 15:58:16 -06:00
|
|
|
// 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,
|
2021-01-07 22:45:42 -08:00
|
|
|
ArchiveFormat::TarBzip2,
|
2020-04-03 13:13:49 -07:00
|
|
|
);
|
2019-10-18 15:58:16 -06:00
|
|
|
}
|
|
|
|
}
|