Skip to content

Commit

Permalink
Tune ideal storage size based on total alive bytes and max ancient sl…
Browse files Browse the repository at this point in the history
…ots (#3206)

* Tune ideal storage size based on total alive bytes and max ancient slots

* Feedback

* Stats
  • Loading branch information
dmakarov authored Oct 18, 2024
1 parent 98b7826 commit 604cab8
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 52 deletions.
6 changes: 6 additions & 0 deletions accounts-db/src/accounts_db/stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ pub struct ShrinkAncientStats {
pub slots_eligible_to_shrink: AtomicU64,
pub total_dead_bytes: AtomicU64,
pub total_alive_bytes: AtomicU64,
pub ideal_storage_size: AtomicU64,
}

#[derive(Debug, Default)]
Expand Down Expand Up @@ -772,6 +773,11 @@ impl ShrinkAncientStats {
.swap(0, Ordering::Relaxed),
i64
),
(
"ideal_storage_size",
self.ideal_storage_size.swap(0, Ordering::Relaxed),
i64
),
);
}
}
114 changes: 62 additions & 52 deletions accounts-db/src/ancient_append_vecs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ use {
/// this many # of highest slot values should be treated as desirable to pack.
/// This gives us high slots to move packed accounts into.
const HIGH_SLOT_OFFSET: u64 = 100;
/// The smallest size of ideal ancient storage.
const MINUMAL_IDEAL_STORAGE_SIZE: u64 = 5_000_000;

/// ancient packing algorithm tuning per pass
#[derive(Debug)]
Expand Down Expand Up @@ -402,13 +404,23 @@ impl AccountsDb {
fn combine_ancient_slots_packed_internal(
&self,
sorted_slots: Vec<Slot>,
tuning: PackedAncientStorageTuning,
mut tuning: PackedAncientStorageTuning,
metrics: &mut ShrinkStatsSub,
) {
self.shrink_ancient_stats
.slots_considered
.fetch_add(sorted_slots.len() as u64, Ordering::Relaxed);
let mut ancient_slot_infos = self.collect_sort_filter_ancient_slots(sorted_slots, &tuning);
// ideal storage size is total alive bytes of ancient storages
// divided by half of max ancient slots
tuning.ideal_storage_size = NonZeroU64::new(
(ancient_slot_infos.total_alive_bytes.0 * 2 / tuning.max_ancient_slots.max(1) as u64)
.max(MINUMAL_IDEAL_STORAGE_SIZE),
)
.unwrap();
self.shrink_ancient_stats
.ideal_storage_size
.store(tuning.ideal_storage_size.into(), Ordering::Relaxed);

std::mem::swap(
&mut *self.best_ancient_slots_to_shrink.write().unwrap(),
Expand Down Expand Up @@ -512,11 +524,7 @@ impl AccountsDb {
slots: Vec<Slot>,
tuning: &PackedAncientStorageTuning,
) -> AncientSlotInfos {
let mut ancient_slot_infos = self.calc_ancient_slot_info(
slots,
tuning.can_randomly_shrink,
tuning.ideal_storage_size,
);
let mut ancient_slot_infos = self.calc_ancient_slot_info(slots, tuning);

ancient_slot_infos.filter_ancient_slots(tuning, &self.shrink_ancient_stats);
ancient_slot_infos
Expand Down Expand Up @@ -554,8 +562,7 @@ impl AccountsDb {
fn calc_ancient_slot_info(
&self,
slots: Vec<Slot>,
can_randomly_shrink: bool,
ideal_size: NonZeroU64,
tuning: &PackedAncientStorageTuning,
) -> AncientSlotInfos {
let len = slots.len();
let mut infos = AncientSlotInfos {
Expand All @@ -574,8 +581,8 @@ impl AccountsDb {
if infos.add(
*slot,
storage,
can_randomly_shrink,
ideal_size,
tuning.can_randomly_shrink,
tuning.ideal_storage_size,
is_high_slot(*slot),
) {
randoms += 1;
Expand Down Expand Up @@ -2544,6 +2551,14 @@ pub mod tests {
let storage = db.storage.get_slot_storage_entry(slot1).unwrap();
let alive_bytes_expected = storage.alive_bytes();
let high_slot = false;
let tuning = PackedAncientStorageTuning {
percent_of_alive_shrunk_data: 100,
max_ancient_slots: 0,
// irrelevant for what this test is trying to test, but necessary to avoid minimums
ideal_storage_size: NonZeroU64::new(get_ancient_append_vec_capacity()).unwrap(),
can_randomly_shrink,
..default_tuning()
};
match method {
TestCollectInfo::Add => {
// test lower level 'add'
Expand All @@ -2556,22 +2571,9 @@ pub mod tests {
);
}
TestCollectInfo::CalcAncientSlotInfo => {
infos = db.calc_ancient_slot_info(
vec![slot1],
can_randomly_shrink,
NonZeroU64::new(get_ancient_append_vec_capacity()).unwrap(),
);
infos = db.calc_ancient_slot_info(vec![slot1], &tuning);
}
TestCollectInfo::CollectSortFilterInfo => {
let tuning = PackedAncientStorageTuning {
percent_of_alive_shrunk_data: 100,
max_ancient_slots: 0,
// irrelevant for what this test is trying to test, but necessary to avoid minimums
ideal_storage_size: NonZeroU64::new(get_ancient_append_vec_capacity())
.unwrap(),
can_randomly_shrink,
..default_tuning()
};
infos = db.collect_sort_filter_ancient_slots(vec![slot1], &tuning);
}
}
Expand Down Expand Up @@ -2621,11 +2623,15 @@ pub mod tests {
high_slot,
);
} else {
infos = db.calc_ancient_slot_info(
vec![slot1],
let tuning = PackedAncientStorageTuning {
percent_of_alive_shrunk_data: 100,
max_ancient_slots: 0,
// irrelevant for what this test is trying to test, but necessary to avoid minimums
ideal_storage_size: NonZeroU64::new(get_ancient_append_vec_capacity()).unwrap(),
can_randomly_shrink,
NonZeroU64::new(get_ancient_append_vec_capacity()).unwrap(),
);
..default_tuning()
};
infos = db.calc_ancient_slot_info(vec![slot1], &tuning);
}
assert!(infos.all_infos.is_empty());
assert!(infos.shrink_indexes.is_empty());
Expand All @@ -2637,6 +2643,14 @@ pub mod tests {
#[test]
fn test_calc_ancient_slot_info_several() {
let can_randomly_shrink = false;
let tuning = PackedAncientStorageTuning {
percent_of_alive_shrunk_data: 100,
max_ancient_slots: 0,
// irrelevant for what this test is trying to test, but necessary to avoid minimums
ideal_storage_size: NonZeroU64::new(get_ancient_append_vec_capacity()).unwrap(),
can_randomly_shrink,
..default_tuning()
};
for alive in [true, false] {
for slots in 0..4 {
// 1_040_000 is big enough relative to page size to cause shrink ratio to be triggered
Expand All @@ -2651,11 +2665,7 @@ pub mod tests {
.iter()
.map(|storage| storage.alive_bytes() as u64)
.sum::<u64>();
let infos = db.calc_ancient_slot_info(
slot_vec.clone(),
can_randomly_shrink,
NonZeroU64::new(get_ancient_append_vec_capacity()).unwrap(),
);
let infos = db.calc_ancient_slot_info(slot_vec.clone(), &tuning);
if !alive {
assert!(infos.all_infos.is_empty());
assert!(infos.shrink_indexes.is_empty());
Expand Down Expand Up @@ -2696,6 +2706,11 @@ pub mod tests {
#[test]
fn test_calc_ancient_slot_info_one_alive_one_dead() {
let can_randomly_shrink = false;
let tuning = PackedAncientStorageTuning {
ideal_storage_size: NonZeroU64::new(get_ancient_append_vec_capacity()).unwrap(),
can_randomly_shrink,
..default_tuning()
};
for method in TestCollectInfo::iter() {
for slot1_is_alive in [false, true] {
let alives = [false /*dummy*/, slot1_is_alive, !slot1_is_alive];
Expand Down Expand Up @@ -2733,11 +2748,9 @@ pub mod tests {
.sum::<u64>();

let infos = match method {
TestCollectInfo::CalcAncientSlotInfo => db.calc_ancient_slot_info(
slot_vec.clone(),
can_randomly_shrink,
NonZeroU64::new(get_ancient_append_vec_capacity()).unwrap(),
),
TestCollectInfo::CalcAncientSlotInfo => {
db.calc_ancient_slot_info(slot_vec.clone(), &tuning)
}
TestCollectInfo::Add => {
continue; // unsupportable
}
Expand Down Expand Up @@ -3116,6 +3129,14 @@ pub mod tests {
#[test]
fn test_calc_ancient_slot_info_one_shrink_one_not() {
let can_randomly_shrink = false;
let tuning = PackedAncientStorageTuning {
percent_of_alive_shrunk_data: 100,
max_ancient_slots: 0,
// irrelevant for what this test is trying to test, but necessary to avoid minimums
ideal_storage_size: NonZeroU64::new(get_ancient_append_vec_capacity()).unwrap(),
can_randomly_shrink,
..default_tuning()
};
for method in TestCollectInfo::iter() {
for slot1_shrink in [false, true] {
let shrinks = [false /*dummy*/, slot1_shrink, !slot1_shrink];
Expand Down Expand Up @@ -3153,24 +3174,13 @@ pub mod tests {
.map(|storage| storage.alive_bytes() as u64)
.sum::<u64>();
let infos = match method {
TestCollectInfo::CalcAncientSlotInfo => db.calc_ancient_slot_info(
slot_vec.clone(),
can_randomly_shrink,
NonZeroU64::new(get_ancient_append_vec_capacity()).unwrap(),
),
TestCollectInfo::CalcAncientSlotInfo => {
db.calc_ancient_slot_info(slot_vec.clone(), &tuning)
}
TestCollectInfo::Add => {
continue; // unsupportable
}
TestCollectInfo::CollectSortFilterInfo => {
let tuning = PackedAncientStorageTuning {
percent_of_alive_shrunk_data: 100,
max_ancient_slots: 0,
// irrelevant for what this test is trying to test, but necessary to avoid minimums
ideal_storage_size: NonZeroU64::new(get_ancient_append_vec_capacity())
.unwrap(),
can_randomly_shrink,
..default_tuning()
};
// note this can sort infos.all_infos
db.collect_sort_filter_ancient_slots(slot_vec.clone(), &tuning)
}
Expand Down

0 comments on commit 604cab8

Please sign in to comment.