1mod pending_snapshot_packages;
6mod stats;
7pub use pending_snapshot_packages::PendingSnapshotPackages;
8#[cfg(feature = "dev-context-only-utils")]
9use qualifier_attr::qualifiers;
10use {
11 crate::{
12 bank::{Bank, BankSlotDelta, DropCallback},
13 bank_forks::BankForks,
14 snapshot_controller::SnapshotController,
15 snapshot_package::SnapshotPackage,
16 },
17 agave_snapshots::{SnapshotArchiveKind, SnapshotKind, error::SnapshotError},
18 crossbeam_channel::{Receiver, SendError, Sender},
19 log::*,
20 rayon::iter::{IntoParallelIterator, ParallelIterator},
21 solana_clock::{BankId, Slot},
22 solana_measure::{measure::Measure, measure_us},
23 stats::StatsManager,
24 std::{
25 boxed::Box,
26 cmp,
27 fmt::{self, Debug, Formatter},
28 sync::{
29 Arc, LazyLock, Mutex, RwLock,
30 atomic::{AtomicBool, AtomicU64, Ordering},
31 },
32 thread::{self, Builder, JoinHandle, sleep},
33 time::{Duration, Instant},
34 },
35};
36
37const MIN_LOOP_INTERVAL: Duration = Duration::from_millis(100);
43const CLEAN_INTERVAL: Duration = Duration::from_secs(50);
47const SHRINK_INTERVAL: Duration = Duration::from_secs(1);
48
49pub type SnapshotRequestSender = Sender<SnapshotRequest>;
50pub type SnapshotRequestReceiver = Receiver<SnapshotRequest>;
51pub type DroppedSlotsSender = Sender<(Slot, BankId)>;
52pub type DroppedSlotsReceiver = Receiver<(Slot, BankId)>;
53
54const BANK_DROP_SIGNAL_CHANNEL_REPORT_INTERVAL: u64 = 60_000;
56const MAX_DROP_BANK_SIGNAL_QUEUE_SIZE: usize = 10_000;
58
59#[derive(Debug, Default)]
60struct PrunedBankQueueLenReporter {
61 last_report_time: AtomicU64,
62}
63
64impl PrunedBankQueueLenReporter {
65 fn report(&self, q_len: usize) {
66 let now = solana_time_utils::timestamp();
67 let last_report_time = self.last_report_time.load(Ordering::Acquire);
68 if q_len > MAX_DROP_BANK_SIGNAL_QUEUE_SIZE
69 && now.saturating_sub(last_report_time) > BANK_DROP_SIGNAL_CHANNEL_REPORT_INTERVAL
70 {
71 datapoint_warn!("excessive_pruned_bank_channel_len", ("len", q_len, i64));
72 self.last_report_time.store(now, Ordering::Release);
73 }
74 }
75}
76
77static BANK_DROP_QUEUE_REPORTER: LazyLock<PrunedBankQueueLenReporter> =
78 LazyLock::new(PrunedBankQueueLenReporter::default);
79
80#[derive(Clone)]
81pub struct SendDroppedBankCallback {
82 sender: DroppedSlotsSender,
83}
84
85impl DropCallback for SendDroppedBankCallback {
86 fn callback(&self, bank: &Bank) {
87 BANK_DROP_QUEUE_REPORTER.report(self.sender.len());
88 if let Err(SendError(_)) = self.sender.send((bank.slot(), bank.bank_id())) {
89 info!("bank DropCallback signal queue disconnected.");
90 }
91 }
92
93 fn clone_box(&self) -> Box<dyn DropCallback + Send + Sync> {
94 Box::new(self.clone())
95 }
96}
97
98impl Debug for SendDroppedBankCallback {
99 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
100 write!(f, "SendDroppedBankCallback({self:p})")
101 }
102}
103
104impl SendDroppedBankCallback {
105 pub fn new(sender: DroppedSlotsSender) -> Self {
106 Self { sender }
107 }
108}
109
110pub struct SnapshotRequest {
111 pub snapshot_root_bank: Arc<Bank>,
112 pub status_cache_slot_deltas: Vec<BankSlotDelta>,
113 pub request_kind: SnapshotRequestKind,
114
115 pub enqueued: Instant,
118}
119
120impl Debug for SnapshotRequest {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 f.debug_struct("SnapshotRequest")
123 .field("request kind", &self.request_kind)
124 .field("bank slot", &self.snapshot_root_bank.slot())
125 .field("block height", &self.snapshot_root_bank.block_height())
126 .finish_non_exhaustive()
127 }
128}
129
130#[derive(Debug, Copy, Clone, Eq, PartialEq)]
132pub enum SnapshotRequestKind {
133 FullSnapshot,
134 IncrementalSnapshot,
135 FastbootSnapshot,
136}
137
138pub struct SnapshotRequestHandler {
139 pub snapshot_controller: Arc<SnapshotController>,
140 pub snapshot_request_receiver: SnapshotRequestReceiver,
141 pub pending_snapshot_packages: Arc<Mutex<PendingSnapshotPackages>>,
142}
143
144impl SnapshotRequestHandler {
145 pub fn handle_snapshot_requests(
147 &self,
148 non_snapshot_time_us: u128,
149 ) -> Option<Result<Slot, SnapshotError>> {
150 let (snapshot_request, num_outstanding_requests, num_re_enqueued_requests) =
151 self.get_next_snapshot_request()?;
152
153 datapoint_info!(
154 "handle_snapshot_requests",
155 ("num_outstanding_requests", num_outstanding_requests, i64),
156 ("num_re_enqueued_requests", num_re_enqueued_requests, i64),
157 (
158 "enqueued_time_us",
159 snapshot_request.enqueued.elapsed().as_micros(),
160 i64
161 ),
162 );
163
164 let snapshot_kind = new_snapshot_kind(&snapshot_request)?;
165 Some(self.handle_snapshot_request(non_snapshot_time_us, snapshot_request, snapshot_kind))
166 }
167
168 fn get_next_snapshot_request(
178 &self,
179 ) -> Option<(
180 SnapshotRequest,
181 usize,
182 usize,
183 )> {
184 let mut requests: Vec<_> = self.snapshot_request_receiver.try_iter().collect();
185 let requests_len = requests.len();
186 debug!("outstanding snapshot requests ({requests_len}): {requests:?}");
187
188 match requests_len {
189 0 => None,
190 1 => {
191 let snapshot_request = requests.pop().unwrap();
193 Some((snapshot_request, 1, 0))
194 }
195 _ => {
196 let max_idx = requests
197 .iter()
198 .enumerate()
199 .max_by(|(_, a), (_, b)| cmp_requests_by_priority(a, b))
200 .map(|(idx, _)| idx)
201 .unwrap(); let snapshot_request = requests.swap_remove(max_idx);
203 let handled_request_slot = snapshot_request.snapshot_root_bank.slot();
204 let num_re_enqueued_requests = requests
206 .into_iter()
207 .filter(|snapshot_request| {
208 snapshot_request.snapshot_root_bank.slot() > handled_request_slot
209 })
210 .map(|snapshot_request| {
211 self.snapshot_controller
212 .request_sender()
213 .try_send(snapshot_request)
214 .expect("re-enqueue snapshot request");
215 })
216 .count();
217
218 Some((snapshot_request, requests_len, num_re_enqueued_requests))
219 }
220 }
221 }
222
223 fn handle_snapshot_request(
224 &self,
225 non_snapshot_time_us: u128,
226 snapshot_request: SnapshotRequest,
227 snapshot_kind: SnapshotKind,
228 ) -> Result<Slot, SnapshotError> {
229 info!("handling snapshot request: {snapshot_request:?}, {snapshot_kind:?}");
230 let mut total_time = Measure::start("snapshot_request_receiver_total_time");
231 let SnapshotRequest {
232 snapshot_root_bank,
233 status_cache_slot_deltas,
234 request_kind: _,
235 enqueued: _,
236 } = snapshot_request;
237
238 if snapshot_kind.is_full_snapshot() {
239 snapshot_root_bank
243 .rc
244 .accounts
245 .accounts_db
246 .set_latest_full_snapshot_slot(snapshot_root_bank.slot());
247 }
248
249 let mut flush_accounts_cache_time = Measure::start("flush_accounts_cache_time");
250 snapshot_root_bank.force_flush_accounts_cache();
255 assert!(
259 snapshot_root_bank.slot()
260 <= snapshot_root_bank
261 .rc
262 .accounts
263 .accounts_db
264 .accounts_cache
265 .fetch_max_flush_root()
266 .expect("Roots have been flushed")
267 );
268 flush_accounts_cache_time.stop();
269
270 let mut clean_time = Measure::start("clean_time");
271 snapshot_root_bank.clean_accounts();
272 clean_time.stop();
273
274 let (_, shrink_ancient_time_us) = measure_us!(snapshot_root_bank.shrink_ancient_slots());
275
276 let mut shrink_time = Measure::start("shrink_time");
277 snapshot_root_bank.shrink_candidate_slots();
278 shrink_time.stop();
279
280 let mut snapshot_time = Measure::start("snapshot_time");
282 let snapshot_package = SnapshotPackage::new(
283 snapshot_kind,
284 &snapshot_root_bank,
285 snapshot_root_bank.get_snapshot_storages(None),
286 status_cache_slot_deltas,
287 );
288 self.pending_snapshot_packages
289 .lock()
290 .unwrap()
291 .push(snapshot_package);
292 snapshot_time.stop();
293 info!(
294 "Handled snapshot request. snapshot kind: {:?}, slot: {}, bank hash: {}",
295 snapshot_kind,
296 snapshot_root_bank.slot(),
297 snapshot_root_bank.hash(),
298 );
299
300 total_time.stop();
301
302 datapoint_info!(
303 "handle_snapshot_requests-timing",
304 (
305 "flush_accounts_cache_time",
306 flush_accounts_cache_time.as_us(),
307 i64
308 ),
309 ("shrink_time", shrink_time.as_us(), i64),
310 ("clean_time", clean_time.as_us(), i64),
311 ("snapshot_time", snapshot_time.as_us(), i64),
312 ("total_us", total_time.as_us(), i64),
313 ("non_snapshot_time_us", non_snapshot_time_us, i64),
314 ("shrink_ancient_time_us", shrink_ancient_time_us, i64),
315 );
316 Ok(snapshot_root_bank.slot())
317 }
318
319 fn peek_next_snapshot_request_slot(&self) -> Option<Slot> {
321 let (next_request, _, _) = self.get_next_snapshot_request()?;
326 let next_slot = next_request.snapshot_root_bank.slot();
327
328 self.snapshot_controller
330 .request_sender()
331 .try_send(next_request)
332 .expect("re-enqueue snapshot request");
333
334 Some(next_slot)
335 }
336}
337
338#[derive(Debug)]
339pub struct PrunedBanksRequestHandler {
340 pub pruned_banks_receiver: DroppedSlotsReceiver,
341}
342
343impl PrunedBanksRequestHandler {
344 #[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
345 fn handle_request(&self, bank: &Bank) -> usize {
346 let mut banks_to_purge: Vec<_> = self.pruned_banks_receiver.try_iter().collect();
347 banks_to_purge.sort_by_key(|(slot, _id)| *slot);
350 let num_banks_to_purge = banks_to_purge.len();
351
352 let grouped_banks_to_purge: Vec<_> = banks_to_purge.chunk_by(|a, b| a.0 == b.0).collect();
354
355 let num_banks_with_same_slot =
358 num_banks_to_purge.saturating_sub(grouped_banks_to_purge.len());
359 if num_banks_with_same_slot > 0 {
360 datapoint_info!(
361 "pruned_banks_request_handler",
362 ("num_pruned_banks", num_banks_to_purge, i64),
363 ("num_banks_with_same_slot", num_banks_with_same_slot, i64),
364 );
365 }
366
367 let accounts_db = bank.rc.accounts.accounts_db.as_ref();
370 accounts_db.thread_pool_background.install(|| {
371 grouped_banks_to_purge.into_par_iter().for_each(|group| {
372 group.iter().for_each(|(slot, bank_id)| {
373 accounts_db.purge_slot(*slot, *bank_id, true);
374 })
375 });
376 });
377
378 num_banks_to_purge
379 }
380
381 fn remove_dead_slots(
382 &self,
383 bank: &Bank,
384 removed_slots_count: &mut usize,
385 total_remove_slots_time: &mut u64,
386 ) {
387 let mut remove_slots_time = Measure::start("remove_slots_time");
388 *removed_slots_count += self.handle_request(bank);
389 remove_slots_time.stop();
390 *total_remove_slots_time += remove_slots_time.as_us();
391
392 if *removed_slots_count >= 100 {
393 datapoint_info!(
394 "remove_slots_timing",
395 ("remove_slots_time", *total_remove_slots_time, i64),
396 ("removed_slots_count", *removed_slots_count, i64),
397 );
398 *total_remove_slots_time = 0;
399 *removed_slots_count = 0;
400 }
401 }
402}
403
404pub struct AbsRequestHandlers {
405 pub snapshot_request_handler: SnapshotRequestHandler,
406 pub pruned_banks_request_handler: PrunedBanksRequestHandler,
407}
408
409impl AbsRequestHandlers {
410 pub fn handle_snapshot_requests(
412 &self,
413 non_snapshot_time_us: u128,
414 ) -> Option<Result<Slot, SnapshotError>> {
415 self.snapshot_request_handler
416 .handle_snapshot_requests(non_snapshot_time_us)
417 }
418}
419
420pub struct AccountsBackgroundService {
421 t_background: JoinHandle<()>,
422 status: AbsStatus,
423}
424
425impl AccountsBackgroundService {
426 pub fn new(
427 bank_forks: Arc<RwLock<BankForks>>,
428 exit: Arc<AtomicBool>,
429 request_handlers: AbsRequestHandlers,
430 ) -> Self {
431 let is_running = Arc::new(AtomicBool::new(true));
432 let stop = Arc::new(AtomicBool::new(false));
433 let mut last_cleaned_slot = 0;
434 let mut removed_slots_count = 0;
435 let mut total_remove_slots_time = 0;
436 let t_background = Builder::new()
437 .name("solAcctsBgSvc".to_string())
438 .spawn({
439 let is_running = is_running.clone();
440 let stop = stop.clone();
441
442 move || {
443 info!("AccountsBackgroundService has started");
444 let mut stats = StatsManager::new();
445 let mut last_snapshot_end_time = None;
446 let mut previous_clean_time = Instant::now();
447 let mut previous_shrink_time = Instant::now();
448
449 loop {
450 if exit.load(Ordering::Relaxed) || stop.load(Ordering::Relaxed) {
451 break;
452 }
453 let start_time = Instant::now();
454
455 let bank = bank_forks.read().unwrap().root_bank();
457
458 request_handlers
460 .pruned_banks_request_handler
461 .remove_dead_slots(
462 &bank,
463 &mut removed_slots_count,
464 &mut total_remove_slots_time,
465 );
466
467 let non_snapshot_time = last_snapshot_end_time
468 .map(|last_snapshot_end_time: Instant| {
469 last_snapshot_end_time.elapsed().as_micros()
470 })
471 .unwrap_or_default();
472
473 let snapshot_handle_result =
493 request_handlers.handle_snapshot_requests(non_snapshot_time);
494
495 if let Some(snapshot_handle_result) = snapshot_handle_result {
496 last_snapshot_end_time = Some(Instant::now());
499 match snapshot_handle_result {
500 Ok(snapshot_slot) => {
501 assert!(
502 last_cleaned_slot <= snapshot_slot,
503 "last cleaned slot: {last_cleaned_slot}, snapshot request \
504 slot: {snapshot_slot}, enqueued snapshot requests: {:?}",
505 request_handlers
506 .snapshot_request_handler
507 .snapshot_request_receiver
508 .try_iter()
509 .collect::<Vec<_>>(),
510 );
511 last_cleaned_slot = snapshot_slot;
512 previous_clean_time = Instant::now();
513 previous_shrink_time = Instant::now();
514 }
515 Err(err) => {
516 error!(
517 "Stopping AccountsBackgroundService! Fatal error while \
518 handling snapshot requests: {err}",
519 );
520 exit.store(true, Ordering::Relaxed);
521 break;
522 }
523 }
524 } else {
525 let next_snapshot_request_slot = request_handlers
528 .snapshot_request_handler
529 .peek_next_snapshot_request_slot();
530
531 let max_clean_slot_inclusive = cmp::min(
535 next_snapshot_request_slot.unwrap_or(Slot::MAX),
536 bank.slot(),
537 )
538 .saturating_sub(1);
539
540 let duration_since_previous_clean = previous_clean_time.elapsed();
541 let should_clean = duration_since_previous_clean > CLEAN_INTERVAL;
542
543 let force_flush = should_clean;
545 bank.rc
546 .accounts
547 .accounts_db
548 .flush_accounts_cache(force_flush, Some(max_clean_slot_inclusive));
549
550 if should_clean {
551 bank.rc
552 .accounts
553 .accounts_db
554 .clean_accounts(Some(max_clean_slot_inclusive), false);
555 last_cleaned_slot = max_clean_slot_inclusive;
556 previous_clean_time = Instant::now();
557 }
558
559 let duration_since_previous_shrink = previous_shrink_time.elapsed();
560 let should_shrink = duration_since_previous_shrink > SHRINK_INTERVAL;
561 if should_shrink || should_clean {
564 if should_clean {
565 bank.shrink_ancient_slots();
568 }
569 bank.shrink_candidate_slots();
570 previous_shrink_time = Instant::now();
571 }
572 }
573
574 let loop_dur = start_time.elapsed();
575 stats.record_and_maybe_submit(loop_dur);
576 if let Some(sleep_dur) = MIN_LOOP_INTERVAL.checked_sub(loop_dur) {
577 sleep(sleep_dur);
578 }
579 }
580 info!("AccountsBackgroundService has stopped");
581 is_running.store(false, Ordering::Relaxed);
582 }
583 })
584 .unwrap();
585
586 Self {
587 t_background,
588 status: AbsStatus { is_running, stop },
589 }
590 }
591
592 pub fn setup_bank_drop_callback(bank_forks: Arc<RwLock<BankForks>>) -> DroppedSlotsReceiver {
597 assert_eq!(bank_forks.read().unwrap().banks().len(), 1);
598
599 let (pruned_banks_sender, pruned_banks_receiver) = crossbeam_channel::unbounded();
600 {
601 let root_bank = bank_forks.read().unwrap().root_bank();
602
603 root_bank
604 .rc
605 .accounts
606 .accounts_db
607 .enable_bank_drop_callback();
608 root_bank.set_callback(Some(Box::new(SendDroppedBankCallback::new(
609 pruned_banks_sender,
610 ))));
611 }
612 pruned_banks_receiver
613 }
614
615 pub fn join(self) -> thread::Result<()> {
616 self.t_background.join()
617 }
618
619 pub fn status(&self) -> &AbsStatus {
621 &self.status
622 }
623}
624
625#[derive(Debug, Clone)]
627pub struct AbsStatus {
628 is_running: Arc<AtomicBool>,
630 stop: Arc<AtomicBool>,
632}
633
634impl AbsStatus {
635 pub fn is_running(&self) -> bool {
637 self.is_running.load(Ordering::Relaxed)
638 }
639
640 pub fn stop(&self) {
642 self.stop.store(true, Ordering::Relaxed)
643 }
644
645 #[cfg(feature = "dev-context-only-utils")]
646 pub fn new_for_tests() -> Self {
647 Self {
648 is_running: Arc::new(AtomicBool::new(false)),
649 stop: Arc::new(AtomicBool::new(false)),
650 }
651 }
652}
653
654#[must_use]
656fn new_snapshot_kind(snapshot_request: &SnapshotRequest) -> Option<SnapshotKind> {
657 match snapshot_request.request_kind {
658 SnapshotRequestKind::FullSnapshot => Some(SnapshotKind::Archive(SnapshotArchiveKind::Full)),
659 SnapshotRequestKind::IncrementalSnapshot => {
660 if let Some(latest_full_snapshot_slot) = snapshot_request
661 .snapshot_root_bank
662 .rc
663 .accounts
664 .accounts_db
665 .latest_full_snapshot_slot()
666 {
667 Some(SnapshotKind::Archive(SnapshotArchiveKind::Incremental(
668 latest_full_snapshot_slot,
669 )))
670 } else {
671 warn!(
672 "Ignoring IncrementalSnapshot request for slot {} because there is no latest \
673 full snapshot",
674 snapshot_request.snapshot_root_bank.slot()
675 );
676 None
677 }
678 }
679 SnapshotRequestKind::FastbootSnapshot => Some(SnapshotKind::Fastboot),
680 }
681}
682
683#[must_use]
692fn cmp_requests_by_priority(a: &SnapshotRequest, b: &SnapshotRequest) -> cmp::Ordering {
693 let slot_a = a.snapshot_root_bank.slot();
694 let slot_b = b.snapshot_root_bank.slot();
695 cmp_snapshot_request_kinds_by_priority(&a.request_kind, &b.request_kind)
696 .then(slot_a.cmp(&slot_b))
697}
698
699#[must_use]
706fn cmp_snapshot_request_kinds_by_priority(
707 a: &SnapshotRequestKind,
708 b: &SnapshotRequestKind,
709) -> cmp::Ordering {
710 use {
711 SnapshotRequestKind as Kind,
712 cmp::Ordering::{Equal, Greater, Less},
713 };
714 match (a, b) {
715 (Kind::FullSnapshot, Kind::FullSnapshot) => Equal,
716 (Kind::FullSnapshot, Kind::IncrementalSnapshot) => Greater,
717 (Kind::FullSnapshot, Kind::FastbootSnapshot) => Greater,
718 (Kind::IncrementalSnapshot, Kind::FullSnapshot) => Less,
719 (Kind::IncrementalSnapshot, Kind::IncrementalSnapshot) => Equal,
720 (Kind::IncrementalSnapshot, Kind::FastbootSnapshot) => Greater,
721 (Kind::FastbootSnapshot, Kind::FullSnapshot) => Less,
722 (Kind::FastbootSnapshot, Kind::IncrementalSnapshot) => Less,
723 (Kind::FastbootSnapshot, Kind::FastbootSnapshot) => Equal,
724 }
725}
726
727#[cfg(test)]
728mod test {
729 use {
730 super::*, crate::genesis_utils::create_genesis_config,
731 agave_snapshots::snapshot_config::SnapshotConfig, crossbeam_channel::bounded,
732 solana_account::AccountSharedData, solana_epoch_schedule::EpochSchedule,
733 solana_leader_schedule::SlotLeader, solana_pubkey::Pubkey,
734 };
735
736 #[test]
737 fn test_accounts_background_service_remove_dead_slots() {
738 let genesis = create_genesis_config(10);
739 let bank0 = Arc::new(Bank::new_for_tests(&genesis.genesis_config));
740 let (pruned_banks_sender, pruned_banks_receiver) = bounded(1024);
741 let pruned_banks_request_handler = PrunedBanksRequestHandler {
742 pruned_banks_receiver,
743 };
744
745 let account_key = Pubkey::new_unique();
747 bank0.store_account(
748 &account_key,
749 &AccountSharedData::new(264, 0, &Pubkey::default()),
750 );
751 assert!(bank0.get_account(&account_key).is_some());
752 pruned_banks_sender.send((0, 0)).unwrap();
753
754 assert!(!bank0.rc.accounts.scan_slot(0, |_| Some(())).is_empty());
755
756 pruned_banks_request_handler.remove_dead_slots(&bank0, &mut 0, &mut 0);
757
758 assert!(bank0.rc.accounts.scan_slot(0, |_| Some(())).is_empty());
759 }
760
761 #[test]
766 fn test_get_next_snapshot_request() {
767 const SLOTS_PER_EPOCH: Slot = 400;
770 const FULL_SNAPSHOT_INTERVAL: Slot = 80;
771 const INCREMENTAL_SNAPSHOT_INTERVAL: Slot = 30;
772 const FASTBOOT_SNAPSHOT_INTERVAL: Slot = 45;
773
774 let snapshot_config = SnapshotConfig::default();
778
779 let pending_snapshot_packages = Arc::new(Mutex::new(PendingSnapshotPackages::default()));
780 let (snapshot_request_sender, snapshot_request_receiver) = bounded(1024);
781 let snapshot_controller = Arc::new(SnapshotController::new(
782 snapshot_request_sender.clone(),
783 snapshot_config,
784 0,
785 ));
786 let snapshot_request_handler = SnapshotRequestHandler {
787 snapshot_controller,
788 snapshot_request_receiver,
789 pending_snapshot_packages,
790 };
791
792 let send_snapshot_request = |snapshot_root_bank, request_kind| {
793 let snapshot_request = SnapshotRequest {
794 snapshot_root_bank,
795 status_cache_slot_deltas: Vec::default(),
796 request_kind,
797 enqueued: Instant::now(),
798 };
799 snapshot_request_sender.send(snapshot_request).unwrap();
800 };
801
802 let mut genesis_config_info = create_genesis_config(10);
803 genesis_config_info.genesis_config.epoch_schedule =
804 EpochSchedule::custom(SLOTS_PER_EPOCH, SLOTS_PER_EPOCH, false);
805 let (mut bank, _bank_forks) = Bank::new_for_tests(&genesis_config_info.genesis_config)
806 .wrap_with_bank_forks_for_tests();
807
808 let bank0 = bank.clone();
812 fn latest_full_snapshot_slot(bank: &Bank) -> Option<Slot> {
813 bank.rc.accounts.accounts_db.latest_full_snapshot_slot()
814 }
815 fn set_latest_full_snapshot_slot(bank: &Bank, slot: Slot) {
816 bank.rc
817 .accounts
818 .accounts_db
819 .set_latest_full_snapshot_slot(slot);
820 }
821
822 let mut make_banks = |num_banks| {
841 for _ in 0..num_banks {
842 let slot = bank.slot() + 1;
843 bank = Arc::new(Bank::new_from_parent(
844 bank.clone(),
845 SlotLeader::new_unique(),
846 slot,
847 ));
848
849 if bank.block_height().is_multiple_of(FULL_SNAPSHOT_INTERVAL) {
852 send_snapshot_request(Arc::clone(&bank), SnapshotRequestKind::FullSnapshot);
853 } else if bank
854 .block_height()
855 .is_multiple_of(INCREMENTAL_SNAPSHOT_INTERVAL)
856 {
857 send_snapshot_request(
858 Arc::clone(&bank),
859 SnapshotRequestKind::IncrementalSnapshot,
860 );
861 } else if bank
862 .block_height()
863 .is_multiple_of(FASTBOOT_SNAPSHOT_INTERVAL)
864 {
865 send_snapshot_request(Arc::clone(&bank), SnapshotRequestKind::FastbootSnapshot);
866 }
867 }
868 };
869 make_banks(318);
870
871 assert_eq!(latest_full_snapshot_slot(&bank0), None);
874 let (snapshot_request, ..) = snapshot_request_handler
875 .get_next_snapshot_request()
876 .unwrap();
877 assert_eq!(
878 snapshot_request.request_kind,
879 SnapshotRequestKind::FullSnapshot
880 );
881 assert_eq!(snapshot_request.snapshot_root_bank.slot(), 240);
882 set_latest_full_snapshot_slot(&bank0, 240);
883
884 assert_eq!(latest_full_snapshot_slot(&bank0), Some(240));
887 let (snapshot_request, ..) = snapshot_request_handler
888 .get_next_snapshot_request()
889 .unwrap();
890 assert_eq!(
891 snapshot_request.request_kind,
892 SnapshotRequestKind::IncrementalSnapshot
893 );
894 assert_eq!(snapshot_request.snapshot_root_bank.slot(), 300);
895
896 assert_eq!(latest_full_snapshot_slot(&bank0), Some(240));
899 let (snapshot_request, ..) = snapshot_request_handler
900 .get_next_snapshot_request()
901 .unwrap();
902 assert_eq!(
903 snapshot_request.request_kind,
904 SnapshotRequestKind::FastbootSnapshot
905 );
906 assert_eq!(snapshot_request.snapshot_root_bank.slot(), 315);
907
908 assert_eq!(latest_full_snapshot_slot(&bank0), Some(240));
910 assert!(
911 snapshot_request_handler
912 .get_next_snapshot_request()
913 .is_none()
914 );
915 }
916
917 #[test]
919 fn test_pruned_banks_request_handler_handle_request() {
920 let (pruned_banks_sender, pruned_banks_receiver) = bounded(1024);
921 let pruned_banks_request_handler = PrunedBanksRequestHandler {
922 pruned_banks_receiver,
923 };
924 let genesis_config_info = create_genesis_config(10);
925 let bank = Bank::new_for_tests(&genesis_config_info.genesis_config);
926 bank.rc.accounts.accounts_db.enable_bank_drop_callback();
927 bank.set_callback(Some(Box::new(SendDroppedBankCallback::new(
928 pruned_banks_sender,
929 ))));
930
931 let (fork0_bank0, bank_forks) = bank.wrap_with_bank_forks_for_tests();
932 let fork0_bank1 = Arc::new(Bank::new_from_parent(
933 fork0_bank0.clone(),
934 SlotLeader::new_unique(),
935 fork0_bank0.slot() + 1,
936 ));
937 let fork1_bank1 = Arc::new(Bank::new_from_parent(
938 fork0_bank0.clone(),
939 SlotLeader::new_unique(),
940 fork0_bank0.slot() + 1,
941 ));
942 let fork2_bank1 = Arc::new(Bank::new_from_parent(
943 fork0_bank0.clone(),
944 SlotLeader::new_unique(),
945 fork0_bank0.slot() + 1,
946 ));
947 let fork0_bank2 = Arc::new(Bank::new_from_parent(
948 fork0_bank1.clone(),
949 SlotLeader::new_unique(),
950 fork0_bank1.slot() + 1,
951 ));
952 let fork1_bank2 = Arc::new(Bank::new_from_parent(
953 fork1_bank1.clone(),
954 SlotLeader::new_unique(),
955 fork1_bank1.slot() + 1,
956 ));
957 let fork0_bank3 = Arc::new(Bank::new_from_parent(
958 fork0_bank2.clone(),
959 SlotLeader::new_unique(),
960 fork0_bank2.slot() + 1,
961 ));
962 let fork3_bank3 = Arc::new(Bank::new_from_parent(
963 fork0_bank2.clone(),
964 SlotLeader::new_unique(),
965 fork0_bank2.slot() + 1,
966 ));
967 fork0_bank3.squash();
968
969 drop(fork3_bank3);
970 drop(fork1_bank2);
971 drop(fork0_bank2);
972 drop(fork1_bank1);
973 drop(fork2_bank1);
974 drop(fork0_bank1);
975 drop(fork0_bank0);
976 drop(bank_forks);
977 let num_banks_purged = pruned_banks_request_handler.handle_request(&fork0_bank3);
978 assert_eq!(num_banks_purged, 7);
979 }
980
981 #[test]
982 fn test_cmp_snapshot_request_kinds_by_priority() {
983 use cmp::Ordering::{Equal, Greater, Less};
984 for (snapshot_request_kind_a, snapshot_request_kind_b, expected_result) in [
985 (
986 SnapshotRequestKind::FullSnapshot,
987 SnapshotRequestKind::FullSnapshot,
988 Equal,
989 ),
990 (
991 SnapshotRequestKind::FullSnapshot,
992 SnapshotRequestKind::IncrementalSnapshot,
993 Greater,
994 ),
995 (
996 SnapshotRequestKind::FullSnapshot,
997 SnapshotRequestKind::FastbootSnapshot,
998 Greater,
999 ),
1000 (
1001 SnapshotRequestKind::IncrementalSnapshot,
1002 SnapshotRequestKind::FullSnapshot,
1003 Less,
1004 ),
1005 (
1006 SnapshotRequestKind::IncrementalSnapshot,
1007 SnapshotRequestKind::IncrementalSnapshot,
1008 Equal,
1009 ),
1010 (
1011 SnapshotRequestKind::IncrementalSnapshot,
1012 SnapshotRequestKind::FastbootSnapshot,
1013 Greater,
1014 ),
1015 (
1016 SnapshotRequestKind::FastbootSnapshot,
1017 SnapshotRequestKind::FullSnapshot,
1018 Less,
1019 ),
1020 (
1021 SnapshotRequestKind::FastbootSnapshot,
1022 SnapshotRequestKind::IncrementalSnapshot,
1023 Less,
1024 ),
1025 (
1026 SnapshotRequestKind::FastbootSnapshot,
1027 SnapshotRequestKind::FastbootSnapshot,
1028 Equal,
1029 ),
1030 ] {
1031 let actual_result = cmp_snapshot_request_kinds_by_priority(
1032 &snapshot_request_kind_a,
1033 &snapshot_request_kind_b,
1034 );
1035 assert_eq!(expected_result, actual_result);
1036 }
1037 }
1038}