1use {
2 crate::{
3 accounts_background_service::{
4 SnapshotRequest, SnapshotRequestKind, SnapshotRequestSender,
5 },
6 bank::{Bank, SquashTiming},
7 },
8 agave_snapshots::{SnapshotInterval, snapshot_config::SnapshotConfig},
9 log::*,
10 solana_clock::Slot,
11 solana_measure::measure::Measure,
12 std::{
13 sync::{
14 Arc,
15 atomic::{AtomicBool, AtomicU64, Ordering},
16 },
17 time::Instant,
18 },
19};
20
21struct SnapshotGenerationIntervals {
22 full_snapshot_interval: SnapshotInterval,
23 incremental_snapshot_interval: SnapshotInterval,
24}
25
26pub struct SnapshotController {
27 abs_request_sender: SnapshotRequestSender,
28 snapshot_config: SnapshotConfig,
29 latest_abs_request_slot: AtomicU64,
30 request_fastboot_snapshot: AtomicBool,
31 latest_bank_snapshot_slot: AtomicU64,
32}
33
34impl SnapshotController {
35 pub fn new(
36 abs_request_sender: SnapshotRequestSender,
37 snapshot_config: SnapshotConfig,
38 root_slot: Slot,
39 ) -> Self {
40 Self {
41 abs_request_sender,
42 snapshot_config,
43 latest_abs_request_slot: AtomicU64::new(root_slot),
44 request_fastboot_snapshot: AtomicBool::new(false),
45 latest_bank_snapshot_slot: AtomicU64::new(root_slot),
46 }
47 }
48
49 pub fn snapshot_config(&self) -> &SnapshotConfig {
50 &self.snapshot_config
51 }
52
53 pub fn request_sender(&self) -> &SnapshotRequestSender {
54 &self.abs_request_sender
55 }
56
57 fn latest_abs_request_slot(&self) -> Slot {
58 self.latest_abs_request_slot.load(Ordering::Relaxed)
59 }
60
61 fn set_latest_abs_request_slot(&self, slot: Slot) {
62 self.latest_abs_request_slot.store(slot, Ordering::Relaxed);
63 }
64
65 pub fn request_fastboot_snapshot(&self) {
68 self.request_fastboot_snapshot
69 .store(true, Ordering::Relaxed);
70 }
71
72 pub fn latest_bank_snapshot_slot(&self) -> Slot {
73 self.latest_bank_snapshot_slot.load(Ordering::Relaxed)
74 }
75
76 pub fn set_latest_bank_snapshot_slot(&self, slot: Slot) {
77 self.latest_bank_snapshot_slot
78 .store(slot, Ordering::Relaxed);
79 }
80
81 pub fn handle_new_roots(&self, root: Slot, banks: &[&Arc<Bank>]) -> (bool, SquashTiming, u64) {
82 let mut is_root_bank_squashed = false;
83 let mut squash_timing = SquashTiming::default();
84 let mut total_snapshot_ms = 0;
85 let request_fastboot_snapshot = self.request_fastboot_snapshot.load(Ordering::Relaxed);
86
87 let SnapshotGenerationIntervals {
88 full_snapshot_interval,
89 incremental_snapshot_interval,
90 } = self.snapshot_generation_intervals();
91
92 if let Some((bank, request_kind)) = banks.iter().find_map(|bank| {
93 let should_request_full_snapshot =
94 if let SnapshotInterval::Slots(snapshot_interval) = full_snapshot_interval {
95 bank.block_height() % snapshot_interval == 0
96 } else {
97 false
98 };
99 let should_request_incremental_snapshot =
100 if let SnapshotInterval::Slots(snapshot_interval) = incremental_snapshot_interval {
101 bank.block_height() % snapshot_interval == 0
102 } else {
103 false
104 };
105
106 if bank.slot() <= self.latest_abs_request_slot() {
107 None
108 } else if should_request_full_snapshot {
109 Some((bank, SnapshotRequestKind::FullSnapshot))
110 } else if should_request_incremental_snapshot {
111 Some((bank, SnapshotRequestKind::IncrementalSnapshot))
112 } else if request_fastboot_snapshot {
113 self.request_fastboot_snapshot
116 .store(false, Ordering::Relaxed);
117 Some((bank, SnapshotRequestKind::FastbootSnapshot))
118 } else {
119 None
120 }
121 }) {
122 let bank_slot = bank.slot();
123 self.set_latest_abs_request_slot(bank_slot);
124 squash_timing += bank.squash();
125
126 is_root_bank_squashed = bank_slot == root;
127
128 let mut snapshot_time = Measure::start("squash::snapshot_time");
129 let status_cache_slot_deltas = bank.status_cache.read().unwrap().root_slot_deltas();
132 if let Err(e) = self.abs_request_sender.send(SnapshotRequest {
133 snapshot_root_bank: Arc::clone(bank),
134 status_cache_slot_deltas,
135 request_kind,
136 enqueued: Instant::now(),
137 }) {
138 warn!("Error sending snapshot request for bank: {bank_slot}, err: {e:?}");
139 }
140 snapshot_time.stop();
141 total_snapshot_ms += snapshot_time.as_ms();
142 }
143
144 (is_root_bank_squashed, squash_timing, total_snapshot_ms)
145 }
146
147 fn snapshot_generation_intervals(&self) -> SnapshotGenerationIntervals {
149 if self.snapshot_config.should_generate_snapshots() {
150 SnapshotGenerationIntervals {
151 full_snapshot_interval: self.snapshot_config.full_snapshot_archive_interval,
152 incremental_snapshot_interval: self
153 .snapshot_config
154 .incremental_snapshot_archive_interval,
155 }
156 } else {
157 SnapshotGenerationIntervals {
158 full_snapshot_interval: SnapshotInterval::Disabled,
159 incremental_snapshot_interval: SnapshotInterval::Disabled,
160 }
161 }
162 }
163
164 pub fn is_generating_snapshots(&self) -> bool {
167 let intervals = self.snapshot_generation_intervals();
168 !(intervals.full_snapshot_interval == SnapshotInterval::Disabled
169 && intervals.incremental_snapshot_interval == SnapshotInterval::Disabled)
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use {
176 super::*, crate::accounts_background_service::SnapshotRequestKind,
177 agave_snapshots::snapshot_config::SnapshotConfig, crossbeam_channel::bounded,
178 solana_genesis_config::create_genesis_config, solana_leader_schedule::SlotLeader,
179 std::sync::Arc, test_case::test_case,
180 };
181
182 fn create_banks(num_banks: u64) -> Vec<Arc<Bank>> {
183 let mut banks = vec![];
184 let (genesis_config, _) = create_genesis_config(1_000_000);
185 let (bank0, bank_forks) =
186 Bank::new_for_tests(&genesis_config).wrap_with_bank_forks_for_tests();
187 let mut parent_bank = bank0;
188 banks.push(parent_bank.clone());
189
190 for _ in 1..=num_banks {
191 let slot = parent_bank.slot() + 1;
192 let new_bank = Bank::new_from_parent_with_bank_forks(
193 bank_forks.as_ref(),
194 parent_bank.clone(),
195 SlotLeader::default(),
196 slot,
197 );
198 parent_bank = new_bank;
199 banks.push(parent_bank.clone());
200 }
201
202 banks
203 }
204 #[test_case(SnapshotInterval::Disabled, SnapshotInterval::Disabled,
205 50, None, None; "Snapshots Disabled")]
206 #[test_case(SnapshotInterval::Slots(10.try_into().unwrap()), SnapshotInterval::Slots(5.try_into().unwrap()),
207 4, None, None; "No snapshot triggered")]
208 #[test_case(SnapshotInterval::Slots(5.try_into().unwrap()), SnapshotInterval::Disabled,
209 8, Some(5), Some(SnapshotRequestKind::FullSnapshot); "Full without Incremental")]
210 #[test_case(SnapshotInterval::Slots(10.try_into().unwrap()), SnapshotInterval::Slots(10.try_into().unwrap()),
211 10, Some(10), Some(SnapshotRequestKind::FullSnapshot); "Full and Incremental on same slot, full is selected")]
212 #[test_case(SnapshotInterval::Slots(10.try_into().unwrap()), SnapshotInterval::Slots(5.try_into().unwrap()),
213 15, Some(15), Some(SnapshotRequestKind::IncrementalSnapshot); "Newer incremental picked over older Full")]
214 #[test_case(SnapshotInterval::Disabled, SnapshotInterval::Slots(5.try_into().unwrap()),
215 5, Some(5), Some(SnapshotRequestKind::IncrementalSnapshot); "Incremental without Full")]
216 fn test_handle_new_roots(
217 full_snapshot_archive_interval: SnapshotInterval,
218 incremental_snapshot_archive_interval: SnapshotInterval,
219 num_banks: u64,
220 expected_snapshot_slot: Option<u64>,
221 expected_snapshot_type: Option<SnapshotRequestKind>,
222 ) {
223 let banks = create_banks(num_banks);
224 let banks = banks.iter().rev().collect::<Vec<_>>();
225
226 let snapshot_config = SnapshotConfig {
227 full_snapshot_archive_interval,
228 incremental_snapshot_archive_interval,
229 ..Default::default()
230 };
231
232 let (snapshot_request_sender, snapshot_request_receiver) = bounded(1024);
233 let snapshot_controller =
234 SnapshotController::new(snapshot_request_sender, snapshot_config, 0);
235
236 let (root_bank_squashed, _, _) = snapshot_controller.handle_new_roots(num_banks, &banks);
237
238 if expected_snapshot_slot == Some(num_banks) {
240 assert!(root_bank_squashed);
241 } else {
242 assert!(!root_bank_squashed);
243 }
244
245 assert_eq!(
247 snapshot_controller.latest_abs_request_slot(),
248 expected_snapshot_slot.unwrap_or(0)
249 );
250
251 let sent_request = snapshot_request_receiver.try_recv();
253
254 if let Some(expected_snapshot_type) = expected_snapshot_type {
255 assert!(
256 sent_request.is_ok(),
257 "Expected a snapshot request to be sent"
258 );
259 let sent_request = sent_request.unwrap();
260 assert_eq!(sent_request.request_kind, expected_snapshot_type);
261 assert_eq!(
263 sent_request.snapshot_root_bank.slot(),
264 expected_snapshot_slot.unwrap()
265 );
266 } else {
267 assert!(
268 sent_request.is_err(),
269 "Expected no snapshot request to be sent"
270 );
271 }
272 }
273
274 #[test_case(SnapshotInterval::Disabled, SnapshotInterval::Disabled, false;
275 "both disabled")]
276 #[test_case(SnapshotInterval::Slots(10.try_into().unwrap()), SnapshotInterval::Disabled, true;
277 "full only")]
278 #[test_case(SnapshotInterval::Disabled, SnapshotInterval::Slots(5.try_into().unwrap()), true;
279 "incremental only")]
280 #[test_case(SnapshotInterval::Slots(10.try_into().unwrap()), SnapshotInterval::Slots(5.try_into().unwrap()), true;
281 "both enabled")]
282 fn test_is_generating_snapshots(
283 full_snapshot_archive_interval: SnapshotInterval,
284 incremental_snapshot_archive_interval: SnapshotInterval,
285 expected: bool,
286 ) {
287 let snapshot_config = SnapshotConfig {
288 full_snapshot_archive_interval,
289 incremental_snapshot_archive_interval,
290 ..Default::default()
291 };
292 let (snapshot_request_sender, _snapshot_request_receiver) = bounded(1024);
293 let snapshot_controller =
294 SnapshotController::new(snapshot_request_sender, snapshot_config, 0);
295 assert_eq!(snapshot_controller.is_generating_snapshots(), expected);
296 }
297
298 #[test_case(SnapshotInterval::Disabled, SnapshotInterval::Disabled,
299 50, SnapshotRequestKind::FastbootSnapshot; "Fastboot triggered when snapshots are disabled")]
300 #[test_case(SnapshotInterval::Slots(10.try_into().unwrap()), SnapshotInterval::Slots(5.try_into().unwrap()),
301 4, SnapshotRequestKind::FastbootSnapshot; "Fastboot triggered when no other snapshot conditions are met")]
302 #[test_case(SnapshotInterval::Slots(10.try_into().unwrap()), SnapshotInterval::Disabled,
303 10, SnapshotRequestKind::FullSnapshot; "Full snapshot overrides fastboot on the same slot")]
304 #[test_case(SnapshotInterval::Slots(10.try_into().unwrap()), SnapshotInterval::Slots(5.try_into().unwrap()),
305 5, SnapshotRequestKind::IncrementalSnapshot; "Incremental snapshot overrides fastboot on the same slot")]
306 #[test_case(SnapshotInterval::Slots(10.try_into().unwrap()), SnapshotInterval::Slots(5.try_into().unwrap()),
307 14, SnapshotRequestKind::FastbootSnapshot; "Fastboot triggered on a newer slot, overriding full and incremental")]
308 fn test_fastboot_snapshot(
309 full_snapshot_archive_interval: SnapshotInterval,
310 incremental_snapshot_archive_interval: SnapshotInterval,
311 num_banks: u64,
312 expected_snapshot_type: SnapshotRequestKind,
313 ) {
314 let banks = create_banks(num_banks);
315 let banks = banks.iter().rev().collect::<Vec<_>>();
316
317 let snapshot_config = SnapshotConfig {
318 full_snapshot_archive_interval,
319 incremental_snapshot_archive_interval,
320 ..Default::default()
321 };
322
323 let (snapshot_request_sender, snapshot_request_receiver) = bounded(1024);
324 let snapshot_controller =
325 SnapshotController::new(snapshot_request_sender, snapshot_config, 0);
326
327 snapshot_controller.request_fastboot_snapshot();
328
329 let (root_bank_squashed, _, _) = snapshot_controller.handle_new_roots(num_banks, &banks);
330
331 assert!(root_bank_squashed);
333
334 assert_eq!(snapshot_controller.latest_abs_request_slot(), num_banks,);
336
337 let sent_request = snapshot_request_receiver.try_recv();
339
340 assert!(
341 sent_request.is_ok(),
342 "Expected a snapshot request to be sent"
343 );
344 let sent_request = sent_request.unwrap();
345 assert_eq!(sent_request.request_kind, expected_snapshot_type);
346 assert_eq!(sent_request.snapshot_root_bank.slot(), num_banks);
348
349 assert_eq!(
351 snapshot_controller
352 .request_fastboot_snapshot
353 .load(Ordering::Relaxed),
354 expected_snapshot_type != SnapshotRequestKind::FastbootSnapshot,
355 );
356 }
357}