solana_runtime/
snapshot_package.rs

1use {
2    crate::{
3        accounts::Accounts,
4        accounts_db::SnapshotStorages,
5        bank::{Bank, BankSlotDelta},
6        rent_collector::RentCollector,
7        snapshot_archive_info::{SnapshotArchiveInfo, SnapshotArchiveInfoGetter},
8        snapshot_utils::{
9            self, ArchiveFormat, BankSnapshotInfo, Result, SnapshotVersion,
10            TMP_BANK_SNAPSHOT_PREFIX,
11        },
12    },
13    log::*,
14    solana_sdk::{
15        clock::Slot, genesis_config::ClusterType, hash::Hash, sysvar::epoch_schedule::EpochSchedule,
16    },
17    std::{
18        fs,
19        path::{Path, PathBuf},
20        sync::{Arc, Mutex},
21    },
22    tempfile::TempDir,
23};
24
25/// The PendingAccountsPackage passes an AccountsPackage from AccountsBackgroundService to
26/// AccountsHashVerifier for hashing
27pub type PendingAccountsPackage = Arc<Mutex<Option<AccountsPackage>>>;
28
29/// The PendingSnapshotPackage passes a SnapshotPackage from AccountsHashVerifier to
30/// SnapshotPackagerService for archiving
31pub type PendingSnapshotPackage = Arc<Mutex<Option<SnapshotPackage>>>;
32
33#[derive(Debug)]
34pub struct AccountsPackage {
35    pub slot: Slot,
36    pub block_height: Slot,
37    pub slot_deltas: Vec<BankSlotDelta>,
38    pub snapshot_links: TempDir,
39    pub snapshot_storages: SnapshotStorages,
40    pub archive_format: ArchiveFormat,
41    pub snapshot_version: SnapshotVersion,
42    pub full_snapshot_archives_dir: PathBuf,
43    pub incremental_snapshot_archives_dir: PathBuf,
44    pub expected_capitalization: u64,
45    pub accounts_hash_for_testing: Option<Hash>,
46    pub cluster_type: ClusterType,
47    pub snapshot_type: Option<SnapshotType>,
48    pub accounts: Arc<Accounts>,
49    pub epoch_schedule: EpochSchedule,
50    pub rent_collector: RentCollector,
51}
52
53impl AccountsPackage {
54    /// Package up bank files, storages, and slot deltas for a snapshot
55    #[allow(clippy::too_many_arguments)]
56    pub fn new(
57        bank: &Bank,
58        bank_snapshot_info: &BankSnapshotInfo,
59        bank_snapshots_dir: impl AsRef<Path>,
60        slot_deltas: Vec<BankSlotDelta>,
61        full_snapshot_archives_dir: impl AsRef<Path>,
62        incremental_snapshot_archives_dir: impl AsRef<Path>,
63        snapshot_storages: SnapshotStorages,
64        archive_format: ArchiveFormat,
65        snapshot_version: SnapshotVersion,
66        accounts_hash_for_testing: Option<Hash>,
67        snapshot_type: Option<SnapshotType>,
68    ) -> Result<Self> {
69        info!(
70            "Package snapshot for bank {} has {} account storage entries (snapshot type: {:?})",
71            bank.slot(),
72            snapshot_storages.len(),
73            snapshot_type,
74        );
75
76        if let Some(SnapshotType::IncrementalSnapshot(incremental_snapshot_base_slot)) =
77            snapshot_type
78        {
79            assert!(
80                bank.slot() > incremental_snapshot_base_slot,
81                "Incremental snapshot base slot must be less than the bank being snapshotted!"
82            );
83        }
84
85        // Hard link the snapshot into a tmpdir, to ensure its not removed prior to packaging.
86        let snapshot_links = tempfile::Builder::new()
87            .prefix(&format!("{}{}-", TMP_BANK_SNAPSHOT_PREFIX, bank.slot()))
88            .tempdir_in(bank_snapshots_dir)?;
89        {
90            let snapshot_hardlink_dir = snapshot_links
91                .path()
92                .join(bank_snapshot_info.slot.to_string());
93            fs::create_dir_all(&snapshot_hardlink_dir)?;
94            let file_name =
95                snapshot_utils::path_to_file_name_str(&bank_snapshot_info.snapshot_path)?;
96            fs::hard_link(
97                &bank_snapshot_info.snapshot_path,
98                &snapshot_hardlink_dir.join(file_name),
99            )?;
100        }
101
102        Ok(Self {
103            slot: bank.slot(),
104            block_height: bank.block_height(),
105            slot_deltas,
106            snapshot_links,
107            snapshot_storages,
108            archive_format,
109            snapshot_version,
110            full_snapshot_archives_dir: full_snapshot_archives_dir.as_ref().to_path_buf(),
111            incremental_snapshot_archives_dir: incremental_snapshot_archives_dir
112                .as_ref()
113                .to_path_buf(),
114            expected_capitalization: bank.capitalization(),
115            accounts_hash_for_testing,
116            cluster_type: bank.cluster_type(),
117            snapshot_type,
118            accounts: bank.accounts(),
119            epoch_schedule: *bank.epoch_schedule(),
120            rent_collector: bank.rent_collector().clone(),
121        })
122    }
123}
124
125pub struct SnapshotPackage {
126    pub snapshot_archive_info: SnapshotArchiveInfo,
127    pub block_height: Slot,
128    pub slot_deltas: Vec<BankSlotDelta>,
129    pub snapshot_links: TempDir,
130    pub snapshot_storages: SnapshotStorages,
131    pub snapshot_version: SnapshotVersion,
132    pub snapshot_type: SnapshotType,
133}
134
135impl SnapshotPackage {
136    pub fn new(accounts_package: AccountsPackage, accounts_hash: Hash) -> Self {
137        assert!(
138            accounts_package.snapshot_type.is_some(),
139            "Cannot make a SnapshotPackage from an AccountsPackage when SnapshotType is None!"
140        );
141
142        let mut snapshot_storages = accounts_package.snapshot_storages;
143        let snapshot_archive_path = match accounts_package.snapshot_type.unwrap() {
144            SnapshotType::FullSnapshot => snapshot_utils::build_full_snapshot_archive_path(
145                accounts_package.full_snapshot_archives_dir,
146                accounts_package.slot,
147                &accounts_hash,
148                accounts_package.archive_format,
149            ),
150            SnapshotType::IncrementalSnapshot(incremental_snapshot_base_slot) => {
151                snapshot_storages.retain(|storages| {
152                    storages
153                        .first() // storages are grouped by slot in the outer Vec, so all storages will have the same slot as the first
154                        .map(|storage| storage.slot() > incremental_snapshot_base_slot)
155                        .unwrap_or_default()
156                });
157                assert!(
158                    snapshot_storages.iter().all(|storage| storage
159                        .iter()
160                        .all(|entry| entry.slot() > incremental_snapshot_base_slot)),
161                    "Incremental snapshot package must only contain storage entries where slot > incremental snapshot base slot (i.e. full snapshot slot)!"
162                    );
163                snapshot_utils::build_incremental_snapshot_archive_path(
164                    accounts_package.incremental_snapshot_archives_dir,
165                    incremental_snapshot_base_slot,
166                    accounts_package.slot,
167                    &accounts_hash,
168                    accounts_package.archive_format,
169                )
170            }
171        };
172
173        Self {
174            snapshot_archive_info: SnapshotArchiveInfo {
175                path: snapshot_archive_path,
176                slot: accounts_package.slot,
177                hash: accounts_hash,
178                archive_format: accounts_package.archive_format,
179            },
180            block_height: accounts_package.block_height,
181            slot_deltas: accounts_package.slot_deltas,
182            snapshot_links: accounts_package.snapshot_links,
183            snapshot_storages,
184            snapshot_version: accounts_package.snapshot_version,
185            snapshot_type: accounts_package.snapshot_type.unwrap(),
186        }
187    }
188}
189
190impl SnapshotArchiveInfoGetter for SnapshotPackage {
191    fn snapshot_archive_info(&self) -> &SnapshotArchiveInfo {
192        &self.snapshot_archive_info
193    }
194}
195
196/// Snapshots come in two flavors, Full and Incremental.  The IncrementalSnapshot has a Slot field,
197/// which is the incremental snapshot base slot.
198#[derive(Clone, Copy, Debug, Eq, PartialEq)]
199pub enum SnapshotType {
200    FullSnapshot,
201    IncrementalSnapshot(Slot),
202}
203
204impl SnapshotType {
205    pub fn is_full_snapshot(&self) -> bool {
206        matches!(self, SnapshotType::FullSnapshot)
207    }
208    pub fn is_incremental_snapshot(&self) -> bool {
209        matches!(self, SnapshotType::IncrementalSnapshot(_))
210    }
211}
212
213/// Helper function to retain only max n of elements to the right of a vector,
214/// viz. remove v.len() - n elements from the left of the vector.
215#[inline(always)]
216pub fn retain_max_n_elements<T>(v: &mut Vec<T>, n: usize) {
217    if v.len() > n {
218        let to_truncate = v.len() - n;
219        v.rotate_left(to_truncate);
220        v.truncate(n);
221    }
222}