use {
crate::snapshot_package::{
SnapshotPackage, are_snapshot_packages_the_same_kind, cmp_snapshot_packages_by_priority,
},
agave_snapshots::{SnapshotArchiveKind, SnapshotKind},
log::*,
std::cmp::Ordering::Greater,
};
#[derive(Debug, Default)]
pub struct PendingSnapshotPackages {
full: Option<SnapshotPackage>,
incremental: Option<SnapshotPackage>,
fastboot: Option<SnapshotPackage>,
}
impl PendingSnapshotPackages {
pub fn push(&mut self, snapshot_package: SnapshotPackage) {
let (pending_package, kind_str) = match snapshot_package.snapshot_kind {
SnapshotKind::Archive(SnapshotArchiveKind::Full) => (&mut self.full, "full"),
SnapshotKind::Archive(SnapshotArchiveKind::Incremental(_)) => {
(&mut self.incremental, "incremental")
}
SnapshotKind::Fastboot => (&mut self.fastboot, "fastboot"),
};
if let Some(pending_snapshot_package) = pending_package.as_ref() {
assert!(
are_snapshot_packages_the_same_kind(&snapshot_package, pending_snapshot_package),
"mismatched snapshot kinds: pending: {pending_snapshot_package:?}, new: \
{snapshot_package:?}",
);
assert_eq!(
cmp_snapshot_packages_by_priority(&snapshot_package, pending_snapshot_package),
Greater,
"{kind_str} snapshot package must be newer than pending package, old: \
{pending_snapshot_package:?}, new: {snapshot_package:?}",
);
info!(
"overwrote pending {kind_str} snapshot package, old slot: {}, new slot: {}",
pending_snapshot_package.slot, snapshot_package.slot,
);
}
*pending_package = Some(snapshot_package);
}
pub fn pop(&mut self) -> Option<SnapshotPackage> {
let pending_full = self.full.take();
let pending_incremental = self.incremental.take();
let pending_archive = match (pending_full, pending_incremental) {
(Some(pending_full), pending_incremental) => {
if let Some(pending_incremental) = pending_incremental {
let SnapshotKind::Archive(SnapshotArchiveKind::Incremental(base_slot)) =
&pending_incremental.snapshot_kind
else {
panic!(
"the pending incremental snapshot package must be of kind \
IncrementalSnapshot, but instead was {pending_incremental:?}",
);
};
if pending_incremental.slot > pending_full.slot
&& *base_slot >= pending_full.slot
{
self.incremental = Some(pending_incremental);
}
}
assert!(pending_full.snapshot_kind.is_full_snapshot());
Some(pending_full)
}
(None, Some(pending_incremental)) => {
assert!(pending_incremental.snapshot_kind.is_incremental_snapshot());
Some(pending_incremental)
}
(None, None) => None,
};
let pending_fastboot = self.fastboot.take();
match (pending_archive, pending_fastboot) {
(Some(pending_archive), Some(pending_fastboot)) => {
if pending_fastboot.slot > pending_archive.slot {
self.fastboot = Some(pending_fastboot);
}
Some(pending_archive)
}
(Some(pending_archive), None) => Some(pending_archive),
(None, Some(pending_fastboot)) => {
assert!(
pending_fastboot.snapshot_kind == SnapshotKind::Fastboot,
"the pending fastboot snapshot package must be of kind Fastboot, but instead \
was {pending_fastboot:?}",
);
Some(pending_fastboot)
}
(None, None) => None,
}
}
#[cfg(feature = "dev-context-only-utils")]
pub fn is_empty(&self) -> bool {
self.incremental.is_none() && self.full.is_none() && self.fastboot.is_none()
}
}
#[cfg(test)]
mod tests {
use {super::*, solana_clock::Slot};
fn new(snapshot_kind: SnapshotKind, slot: Slot) -> SnapshotPackage {
SnapshotPackage {
snapshot_kind,
slot,
..SnapshotPackage::default_for_tests()
}
}
fn new_full(slot: Slot) -> SnapshotPackage {
new(SnapshotKind::Archive(SnapshotArchiveKind::Full), slot)
}
fn new_incr(slot: Slot, base: Slot) -> SnapshotPackage {
new(
SnapshotKind::Archive(SnapshotArchiveKind::Incremental(base)),
slot,
)
}
fn new_fastboot(slot: Slot) -> SnapshotPackage {
new(SnapshotKind::Fastboot, slot)
}
#[test]
fn test_default() {
let pending_snapshot_packages = PendingSnapshotPackages::default();
assert!(pending_snapshot_packages.full.is_none());
assert!(pending_snapshot_packages.incremental.is_none());
assert!(pending_snapshot_packages.fastboot.is_none());
}
#[test]
fn test_push() {
let mut pending_snapshot_packages = PendingSnapshotPackages::default();
let slot = 100;
pending_snapshot_packages.push(new_full(slot));
assert_eq!(pending_snapshot_packages.full.as_ref().unwrap().slot, slot);
assert!(pending_snapshot_packages.incremental.is_none());
assert!(pending_snapshot_packages.fastboot.is_none());
let slot = slot + 100;
pending_snapshot_packages.push(new_full(slot));
assert_eq!(pending_snapshot_packages.full.as_ref().unwrap().slot, slot);
assert!(pending_snapshot_packages.incremental.is_none());
assert!(pending_snapshot_packages.fastboot.is_none());
let full_slot = slot;
let slot = full_slot + 10;
pending_snapshot_packages.push(new_incr(slot, full_slot));
assert_eq!(
pending_snapshot_packages.full.as_ref().unwrap().slot,
full_slot,
);
assert_eq!(
pending_snapshot_packages.incremental.as_ref().unwrap().slot,
slot,
);
assert!(pending_snapshot_packages.fastboot.is_none());
let slot = slot + 10;
pending_snapshot_packages.push(new_incr(slot, full_slot));
assert_eq!(
pending_snapshot_packages.full.as_ref().unwrap().slot,
full_slot,
);
assert_eq!(
pending_snapshot_packages.incremental.as_ref().unwrap().slot,
slot,
);
assert!(pending_snapshot_packages.fastboot.is_none());
let incremental_slot = slot;
let slot = slot + 50;
pending_snapshot_packages.push(new_fastboot(slot));
assert_eq!(
pending_snapshot_packages.full.as_ref().unwrap().slot,
full_slot,
);
assert_eq!(
pending_snapshot_packages.incremental.as_ref().unwrap().slot,
incremental_slot,
);
assert_eq!(
pending_snapshot_packages.fastboot.as_ref().unwrap().slot,
slot,
);
let slot = slot + 10;
pending_snapshot_packages.push(new_fastboot(slot));
assert_eq!(
pending_snapshot_packages.full.as_ref().unwrap().slot,
full_slot,
);
assert_eq!(
pending_snapshot_packages.incremental.as_ref().unwrap().slot,
incremental_slot,
);
assert_eq!(
pending_snapshot_packages.fastboot.as_ref().unwrap().slot,
slot,
);
let fastboot_slot = slot;
let slot = full_slot + 100;
pending_snapshot_packages.push(new_full(slot));
assert_eq!(pending_snapshot_packages.full.as_ref().unwrap().slot, slot);
assert_eq!(
pending_snapshot_packages.incremental.as_ref().unwrap().slot,
incremental_slot,
);
assert_eq!(
pending_snapshot_packages.fastboot.as_ref().unwrap().slot,
fastboot_slot,
);
let full_slot = slot;
let slot = slot + 10;
pending_snapshot_packages.push(new_incr(slot, full_slot));
assert_eq!(
pending_snapshot_packages.full.as_ref().unwrap().slot,
full_slot,
);
assert_eq!(
pending_snapshot_packages.incremental.as_ref().unwrap().slot,
slot,
);
assert_eq!(
pending_snapshot_packages.fastboot.as_ref().unwrap().slot,
fastboot_slot,
);
}
#[test]
#[should_panic(expected = "full snapshot package must be newer than pending package")]
fn test_push_older_full() {
let slot = 100;
let mut pending_snapshot_packages = PendingSnapshotPackages {
full: Some(new_full(slot)),
incremental: None,
fastboot: None,
};
pending_snapshot_packages.push(new_full(slot - 1));
}
#[test]
#[should_panic(expected = "incremental snapshot package must be newer than pending package")]
fn test_push_older_incremental() {
let base = 100;
let slot = base + 20;
let mut pending_snapshot_packages = PendingSnapshotPackages {
full: None,
incremental: Some(new_incr(slot, base)),
fastboot: None,
};
pending_snapshot_packages.push(new_incr(slot - 1, base));
}
#[test]
#[should_panic(expected = "fastboot snapshot package must be newer than pending package")]
fn test_push_older_fastboot() {
let slot = 100;
let mut pending_snapshot_packages = PendingSnapshotPackages {
full: None,
incremental: None,
fastboot: Some(new_fastboot(slot)),
};
pending_snapshot_packages.push(new_fastboot(slot - 1));
}
#[test]
fn test_pop() {
let mut pending_snapshot_packages = PendingSnapshotPackages::default();
let oldest_slot = 100;
let middle_slot = 200;
let newest_slot = 300;
let slots = vec![
None,
Some(oldest_slot),
Some(middle_slot),
Some(newest_slot),
];
let mut scenarios = Vec::new();
for &full_slot in &slots {
for &incr_slot in &slots {
for &fastboot_slot in &slots {
scenarios.push((full_slot, incr_slot, fastboot_slot));
}
}
}
for (queued_full_package_slot, queued_incr_package_slot, queued_fastboot_package_slot) in
scenarios
{
pending_snapshot_packages.full = queued_full_package_slot.map(new_full);
pending_snapshot_packages.incremental =
queued_incr_package_slot.map(|slot| new_incr(slot, slot));
pending_snapshot_packages.fastboot = queued_fastboot_package_slot.map(new_fastboot);
if let Some(full_slot) = queued_full_package_slot {
let full_package = pending_snapshot_packages.pop().unwrap();
assert_eq!(
full_package.snapshot_kind,
SnapshotKind::Archive(SnapshotArchiveKind::Full)
);
assert_eq!(full_package.slot, full_slot);
}
if let Some(incr_slot) = queued_incr_package_slot {
if incr_slot > queued_full_package_slot.unwrap_or(0) {
let incremental_package = pending_snapshot_packages.pop().unwrap();
assert_eq!(
incremental_package.snapshot_kind,
SnapshotKind::Archive(SnapshotArchiveKind::Incremental(incr_slot))
);
assert_eq!(incremental_package.slot, incr_slot);
}
}
if let Some(fastboot_slot) = queued_fastboot_package_slot {
if fastboot_slot > queued_full_package_slot.unwrap_or(0)
&& fastboot_slot > queued_incr_package_slot.unwrap_or(0)
{
let fastboot_package = pending_snapshot_packages.pop().unwrap();
assert_eq!(fastboot_package.snapshot_kind, SnapshotKind::Fastboot);
assert_eq!(fastboot_package.slot, fastboot_slot);
}
}
assert!(pending_snapshot_packages.pop().is_none());
}
}
#[test]
#[should_panic]
fn test_pop_invalid_pending_full() {
let mut pending_snapshot_packages = PendingSnapshotPackages {
full: Some(new_incr(110, 100)), incremental: None,
fastboot: None,
};
pending_snapshot_packages.pop();
}
#[test]
#[should_panic]
fn test_pop_invalid_pending_incremental() {
let mut pending_snapshot_packages = PendingSnapshotPackages {
full: None,
incremental: Some(new_full(100)), fastboot: None,
};
pending_snapshot_packages.pop();
}
#[test]
#[should_panic]
fn test_pop_invalid_pending_fastboot() {
let mut pending_snapshot_packages = PendingSnapshotPackages {
full: None,
incremental: None,
fastboot: Some(new_full(100)), };
pending_snapshot_packages.pop();
}
}