use crate::{ accounts_db::SnapshotStorages, bank::{Bank, BankSlotDelta}, }; use crate::{ snapshot_archive_info::{SnapshotArchiveInfo, SnapshotArchiveInfoGetter}, snapshot_utils::{ self, ArchiveFormat, BankSnapshotInfo, Result, SnapshotVersion, TMP_BANK_SNAPSHOT_PREFIX, }, }; use log::*; use solana_sdk::clock::Slot; use solana_sdk::genesis_config::ClusterType; use solana_sdk::hash::Hash; use std::{ fs, path::{Path, PathBuf}, sync::{ mpsc::{Receiver, SendError, Sender}, Arc, Mutex, }, }; use tempfile::TempDir; /// The sender side of the AccountsPackage channel, used by AccountsBackgroundService pub type AccountsPackageSender = Sender; /// The receiver side of the AccountsPackage channel, used by AccountsHashVerifier pub type AccountsPackageReceiver = Receiver; /// The error type when sending an AccountsPackage over the channel fails pub type AccountsPackageSendError = SendError; /// The PendingSnapshotPackage passes a SnapshotPackage from AccountsHashVerifier to /// SnapshotPackagerService for archiving pub type PendingSnapshotPackage = Arc>>; #[derive(Debug)] pub struct AccountsPackage { pub slot: Slot, pub block_height: Slot, pub slot_deltas: Vec, pub snapshot_links: TempDir, pub snapshot_storages: SnapshotStorages, pub hash: Hash, // temporarily here while we still have to calculate hash before serializing bank pub archive_format: ArchiveFormat, pub snapshot_version: SnapshotVersion, pub snapshot_archives_dir: PathBuf, pub expected_capitalization: u64, pub hash_for_testing: Option, pub cluster_type: ClusterType, pub snapshot_type: Option, } impl AccountsPackage { /// Package up bank files, storages, and slot deltas for a snapshot #[allow(clippy::too_many_arguments)] pub fn new( bank: &Bank, bank_snapshot_info: &BankSnapshotInfo, bank_snapshots_dir: impl AsRef, slot_deltas: Vec, snapshot_archives_dir: impl AsRef, snapshot_storages: SnapshotStorages, archive_format: ArchiveFormat, snapshot_version: SnapshotVersion, hash_for_testing: Option, snapshot_type: Option, ) -> Result { info!( "Package snapshot for bank {} has {} account storage entries (snapshot type: {:?})", bank.slot(), snapshot_storages.len(), snapshot_type, ); if let Some(SnapshotType::IncrementalSnapshot(incremental_snapshot_base_slot)) = snapshot_type { assert!( bank.slot() > incremental_snapshot_base_slot, "Incremental snapshot base slot must be less than the bank being snapshotted!" ); assert!( snapshot_storages.iter().all(|storage| storage .iter() .all(|entry| entry.slot() > incremental_snapshot_base_slot)), "Incremental snapshot package must only contain storage entries where slot > incremental snapshot base slot (i.e. full snapshot slot)!" ); } // Hard link the snapshot into a tmpdir, to ensure its not removed prior to packaging. let snapshot_links = tempfile::Builder::new() .prefix(&format!("{}{}-", TMP_BANK_SNAPSHOT_PREFIX, bank.slot())) .tempdir_in(bank_snapshots_dir)?; { let snapshot_hardlink_dir = snapshot_links .path() .join(bank_snapshot_info.slot.to_string()); fs::create_dir_all(&snapshot_hardlink_dir)?; fs::hard_link( &bank_snapshot_info.snapshot_path, &snapshot_hardlink_dir.join(bank_snapshot_info.slot.to_string()), )?; } Ok(Self { slot: bank.slot(), block_height: bank.block_height(), slot_deltas, snapshot_links, snapshot_storages, hash: bank.get_accounts_hash(), archive_format, snapshot_version, snapshot_archives_dir: snapshot_archives_dir.as_ref().to_path_buf(), expected_capitalization: bank.capitalization(), hash_for_testing, cluster_type: bank.cluster_type(), snapshot_type, }) } } pub struct SnapshotPackage { pub snapshot_archive_info: SnapshotArchiveInfo, pub block_height: Slot, pub slot_deltas: Vec, pub snapshot_links: TempDir, pub snapshot_storages: SnapshotStorages, pub snapshot_version: SnapshotVersion, pub snapshot_type: SnapshotType, } impl From for SnapshotPackage { fn from(accounts_package: AccountsPackage) -> Self { assert!( accounts_package.snapshot_type.is_some(), "Cannot make a SnapshotPackage from an AccountsPackage when SnapshotType is None!" ); let snapshot_archive_path = match accounts_package.snapshot_type.unwrap() { SnapshotType::FullSnapshot => snapshot_utils::build_full_snapshot_archive_path( accounts_package.snapshot_archives_dir, accounts_package.slot, &accounts_package.hash, accounts_package.archive_format, ), SnapshotType::IncrementalSnapshot(incremental_snapshot_base_slot) => { snapshot_utils::build_incremental_snapshot_archive_path( accounts_package.snapshot_archives_dir, incremental_snapshot_base_slot, accounts_package.slot, &accounts_package.hash, accounts_package.archive_format, ) } }; Self { snapshot_archive_info: SnapshotArchiveInfo { path: snapshot_archive_path, slot: accounts_package.slot, hash: accounts_package.hash, archive_format: accounts_package.archive_format, }, block_height: accounts_package.block_height, slot_deltas: accounts_package.slot_deltas, snapshot_links: accounts_package.snapshot_links, snapshot_storages: accounts_package.snapshot_storages, snapshot_version: accounts_package.snapshot_version, snapshot_type: accounts_package.snapshot_type.unwrap(), } } } impl SnapshotArchiveInfoGetter for SnapshotPackage { fn snapshot_archive_info(&self) -> &SnapshotArchiveInfo { &self.snapshot_archive_info } } /// Snapshots come in two flavors, Full and Incremental. The IncrementalSnapshot has a Slot field, /// which is the incremental snapshot base slot. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum SnapshotType { FullSnapshot, IncrementalSnapshot(Slot), } impl SnapshotType { pub fn is_full_snapshot(&self) -> bool { matches!(self, SnapshotType::FullSnapshot) } pub fn is_incremental_snapshot(&self) -> bool { matches!(self, SnapshotType::IncrementalSnapshot(_)) } }