@@ -2457,11 +2457,30 @@ impl AccountsDb {
2457
2457
is_startup: bool,
2458
2458
timings: &mut CleanKeyTimings,
2459
2459
epoch_schedule: &EpochSchedule,
2460
+ old_storages_policy: OldStoragesPolicy,
2460
2461
) -> CleaningCandidates {
2461
2462
let oldest_non_ancient_slot = self.get_oldest_non_ancient_slot(epoch_schedule);
2462
2463
let mut dirty_store_processing_time = Measure::start("dirty_store_processing");
2463
- let max_slot_inclusive =
2464
- max_clean_root_inclusive.unwrap_or_else(|| self.accounts_index.max_root_inclusive());
2464
+ let max_root_inclusive = self.accounts_index.max_root_inclusive();
2465
+ let max_slot_inclusive = max_clean_root_inclusive.unwrap_or(max_root_inclusive);
2466
+
2467
+ if old_storages_policy == OldStoragesPolicy::Clean {
2468
+ let slot_one_epoch_old =
2469
+ max_root_inclusive.saturating_sub(epoch_schedule.slots_per_epoch);
2470
+ // do nothing special for these 100 old storages that will likely get cleaned up shortly
2471
+ let acceptable_straggler_slot_count = 100;
2472
+ let old_slot_cutoff =
2473
+ slot_one_epoch_old.saturating_sub(acceptable_straggler_slot_count);
2474
+ let (old_storages, old_slots) = self.get_snapshot_storages(..old_slot_cutoff);
2475
+ let num_old_storages = old_storages.len();
2476
+ self.accounts_index
2477
+ .add_uncleaned_roots(old_slots.iter().copied());
2478
+ for (old_slot, old_storage) in std::iter::zip(old_slots, old_storages) {
2479
+ self.dirty_stores.entry(old_slot).or_insert(old_storage);
2480
+ }
2481
+ info!("Marked {num_old_storages} old storages as dirty");
2482
+ }
2483
+
2465
2484
let mut dirty_stores = Vec::with_capacity(self.dirty_stores.len());
2466
2485
// find the oldest dirty slot
2467
2486
// we'll add logging if that append vec cannot be marked dead
@@ -2573,7 +2592,16 @@ impl AccountsDb {
2573
2592
2574
2593
/// Call clean_accounts() with the common parameters that tests/benches use.
2575
2594
pub fn clean_accounts_for_tests(&self) {
2576
- self.clean_accounts(None, false, &EpochSchedule::default())
2595
+ self.clean_accounts(
2596
+ None,
2597
+ false,
2598
+ &EpochSchedule::default(),
2599
+ if self.ancient_append_vec_offset.is_some() {
2600
+ OldStoragesPolicy::Leave
2601
+ } else {
2602
+ OldStoragesPolicy::Clean
2603
+ },
2604
+ )
2577
2605
}
2578
2606
2579
2607
/// called with cli argument to verify refcounts are correct on all accounts
@@ -2680,6 +2708,7 @@ impl AccountsDb {
2680
2708
max_clean_root_inclusive: Option<Slot>,
2681
2709
is_startup: bool,
2682
2710
epoch_schedule: &EpochSchedule,
2711
+ old_storages_policy: OldStoragesPolicy,
2683
2712
) {
2684
2713
if self.exhaustively_verify_refcounts {
2685
2714
self.exhaustively_verify_refcounts(max_clean_root_inclusive);
@@ -2701,6 +2730,7 @@ impl AccountsDb {
2701
2730
is_startup,
2702
2731
&mut key_timings,
2703
2732
epoch_schedule,
2733
+ old_storages_policy,
2704
2734
);
2705
2735
2706
2736
let num_candidates = Self::count_pubkeys(&candidates);
@@ -4561,7 +4591,15 @@ impl AccountsDb {
4561
4591
let maybe_clean = || {
4562
4592
if self.dirty_stores.len() > DIRTY_STORES_CLEANING_THRESHOLD {
4563
4593
let latest_full_snapshot_slot = self.latest_full_snapshot_slot();
4564
- self.clean_accounts(latest_full_snapshot_slot, is_startup, epoch_schedule);
4594
+ self.clean_accounts(
4595
+ latest_full_snapshot_slot,
4596
+ is_startup,
4597
+ epoch_schedule,
4598
+ // Leave any old storages alone for now. Once the validator is running
4599
+ // normal, calls to clean_accounts() will have the correct policy based
4600
+ // on if ancient storages are enabled or not.
4601
+ OldStoragesPolicy::Leave,
4602
+ );
4565
4603
}
4566
4604
};
4567
4605
@@ -6738,40 +6776,6 @@ impl AccountsDb {
6738
6776
true
6739
6777
}
6740
6778
6741
- /// storages are sorted by slot and have range info.
6742
- /// add all stores older than slots_per_epoch to dirty_stores so clean visits these slots
6743
- fn mark_old_slots_as_dirty(
6744
- &self,
6745
- storages: &SortedStorages,
6746
- slots_per_epoch: Slot,
6747
- stats: &mut crate::accounts_hash::HashStats,
6748
- ) {
6749
- // Nothing to do if ancient append vecs are enabled.
6750
- // Ancient slots will be visited by the ancient append vec code and dealt with correctly.
6751
- // we expect these ancient append vecs to be old and keeping accounts
6752
- // We can expect the normal processes will keep them cleaned.
6753
- // If we included them here then ALL accounts in ALL ancient append vecs will be visited by clean each time.
6754
- if self.ancient_append_vec_offset.is_some() {
6755
- return;
6756
- }
6757
-
6758
- let mut mark_time = Measure::start("mark_time");
6759
- let mut num_dirty_slots: usize = 0;
6760
- let max = storages.max_slot_inclusive();
6761
- let acceptable_straggler_slot_count = 100; // do nothing special for these old stores which will likely get cleaned up shortly
6762
- let sub = slots_per_epoch + acceptable_straggler_slot_count;
6763
- let in_epoch_range_start = max.saturating_sub(sub);
6764
- for (slot, storage) in storages.iter_range(&(..in_epoch_range_start)) {
6765
- if let Some(storage) = storage {
6766
- self.dirty_stores.insert(slot, storage.clone());
6767
- num_dirty_slots += 1;
6768
- }
6769
- }
6770
- mark_time.stop();
6771
- stats.mark_time_us = mark_time.as_us();
6772
- stats.num_dirty_slots = num_dirty_slots;
6773
- }
6774
-
6775
6779
pub fn calculate_accounts_hash_from(
6776
6780
&self,
6777
6781
data_source: CalcAccountsHashDataSource,
@@ -7112,8 +7116,6 @@ impl AccountsDb {
7112
7116
let storages_start_slot = storages.range().start;
7113
7117
stats.oldest_root = storages_start_slot;
7114
7118
7115
- self.mark_old_slots_as_dirty(storages, config.epoch_schedule.slots_per_epoch, &mut stats);
7116
-
7117
7119
let slot = storages.max_slot_inclusive();
7118
7120
let use_bg_thread_pool = config.use_bg_thread_pool;
7119
7121
let accounts_hash_cache_path = self.accounts_hash_cache_path.clone();
@@ -9080,6 +9082,20 @@ pub(crate) enum UpdateIndexThreadSelection {
9080
9082
PoolWithThreshold,
9081
9083
}
9082
9084
9085
+ /// How should old storages be handled in clean_accounts()?
9086
+ #[derive(Debug, Copy, Clone, Eq, PartialEq)]
9087
+ pub enum OldStoragesPolicy {
9088
+ /// Clean all old storages, even if they were not explictly marked as dirty.
9089
+ ///
9090
+ /// This is the default behavior when not skipping rewrites.
9091
+ Clean,
9092
+ /// Leave all old storages.
9093
+ ///
9094
+ /// When skipping rewrites, we intentionally will have ancient storages.
9095
+ /// Do not clean them up automatically in clean_accounts().
9096
+ Leave,
9097
+ }
9098
+
9083
9099
// These functions/fields are only usable from a dev context (i.e. tests and benches)
9084
9100
#[cfg(feature = "dev-context-only-utils")]
9085
9101
impl AccountStorageEntry {
@@ -11304,13 +11320,23 @@ pub mod tests {
11304
11320
// updates in later slots in slot 1
11305
11321
assert_eq!(accounts.alive_account_count_in_slot(0), 1);
11306
11322
assert_eq!(accounts.alive_account_count_in_slot(1), 1);
11307
- accounts.clean_accounts(Some(0), false, &EpochSchedule::default());
11323
+ accounts.clean_accounts(
11324
+ Some(0),
11325
+ false,
11326
+ &EpochSchedule::default(),
11327
+ OldStoragesPolicy::Leave,
11328
+ );
11308
11329
assert_eq!(accounts.alive_account_count_in_slot(0), 1);
11309
11330
assert_eq!(accounts.alive_account_count_in_slot(1), 1);
11310
11331
assert!(accounts.accounts_index.contains_with(&pubkey, None, None));
11311
11332
11312
11333
// Now the account can be cleaned up
11313
- accounts.clean_accounts(Some(1), false, &EpochSchedule::default());
11334
+ accounts.clean_accounts(
11335
+ Some(1),
11336
+ false,
11337
+ &EpochSchedule::default(),
11338
+ OldStoragesPolicy::Leave,
11339
+ );
11314
11340
assert_eq!(accounts.alive_account_count_in_slot(0), 0);
11315
11341
assert_eq!(accounts.alive_account_count_in_slot(1), 0);
11316
11342
@@ -12842,7 +12868,12 @@ pub mod tests {
12842
12868
db.add_root_and_flush_write_cache(1);
12843
12869
12844
12870
// Only clean zero lamport accounts up to slot 0
12845
- db.clean_accounts(Some(0), false, &EpochSchedule::default());
12871
+ db.clean_accounts(
12872
+ Some(0),
12873
+ false,
12874
+ &EpochSchedule::default(),
12875
+ OldStoragesPolicy::Leave,
12876
+ );
12846
12877
12847
12878
// Should still be able to find zero lamport account in slot 1
12848
12879
assert_eq!(
@@ -13996,7 +14027,12 @@ pub mod tests {
13996
14027
db.calculate_accounts_delta_hash(1);
13997
14028
13998
14029
// Clean to remove outdated entry from slot 0
13999
- db.clean_accounts(Some(1), false, &EpochSchedule::default());
14030
+ db.clean_accounts(
14031
+ Some(1),
14032
+ false,
14033
+ &EpochSchedule::default(),
14034
+ OldStoragesPolicy::Leave,
14035
+ );
14000
14036
14001
14037
// Shrink Slot 0
14002
14038
{
@@ -14015,7 +14051,12 @@ pub mod tests {
14015
14051
// Should be one store before clean for slot 0
14016
14052
db.get_and_assert_single_storage(0);
14017
14053
db.calculate_accounts_delta_hash(2);
14018
- db.clean_accounts(Some(2), false, &EpochSchedule::default());
14054
+ db.clean_accounts(
14055
+ Some(2),
14056
+ false,
14057
+ &EpochSchedule::default(),
14058
+ OldStoragesPolicy::Leave,
14059
+ );
14019
14060
14020
14061
// No stores should exist for slot 0 after clean
14021
14062
assert_no_storages_at_slot(&db, 0);
@@ -14862,15 +14903,30 @@ pub mod tests {
14862
14903
assert_eq!(accounts_db.ref_count_for_pubkey(&pubkey), 3);
14863
14904
14864
14905
accounts_db.set_latest_full_snapshot_slot(slot2);
14865
- accounts_db.clean_accounts(Some(slot2), false, &EpochSchedule::default());
14906
+ accounts_db.clean_accounts(
14907
+ Some(slot2),
14908
+ false,
14909
+ &EpochSchedule::default(),
14910
+ OldStoragesPolicy::Leave,
14911
+ );
14866
14912
assert_eq!(accounts_db.ref_count_for_pubkey(&pubkey), 2);
14867
14913
14868
14914
accounts_db.set_latest_full_snapshot_slot(slot2);
14869
- accounts_db.clean_accounts(None, false, &EpochSchedule::default());
14915
+ accounts_db.clean_accounts(
14916
+ None,
14917
+ false,
14918
+ &EpochSchedule::default(),
14919
+ OldStoragesPolicy::Leave,
14920
+ );
14870
14921
assert_eq!(accounts_db.ref_count_for_pubkey(&pubkey), 1);
14871
14922
14872
14923
accounts_db.set_latest_full_snapshot_slot(slot3);
14873
- accounts_db.clean_accounts(None, false, &EpochSchedule::default());
14924
+ accounts_db.clean_accounts(
14925
+ None,
14926
+ false,
14927
+ &EpochSchedule::default(),
14928
+ OldStoragesPolicy::Leave,
14929
+ );
14874
14930
assert_eq!(accounts_db.ref_count_for_pubkey(&pubkey), 0);
14875
14931
}
14876
14932
);
@@ -17189,7 +17245,12 @@ pub mod tests {
17189
17245
17190
17246
// calculate the full accounts hash
17191
17247
let full_accounts_hash = {
17192
- accounts_db.clean_accounts(Some(slot - 1), false, &EpochSchedule::default());
17248
+ accounts_db.clean_accounts(
17249
+ Some(slot - 1),
17250
+ false,
17251
+ &EpochSchedule::default(),
17252
+ OldStoragesPolicy::Leave,
17253
+ );
17193
17254
let (storages, _) = accounts_db.get_snapshot_storages(..=slot);
17194
17255
let storages = SortedStorages::new(&storages);
17195
17256
accounts_db.calculate_accounts_hash(
@@ -17255,7 +17316,12 @@ pub mod tests {
17255
17316
// calculate the incremental accounts hash
17256
17317
let incremental_accounts_hash = {
17257
17318
accounts_db.set_latest_full_snapshot_slot(full_accounts_hash_slot);
17258
- accounts_db.clean_accounts(Some(slot - 1), false, &EpochSchedule::default());
17319
+ accounts_db.clean_accounts(
17320
+ Some(slot - 1),
17321
+ false,
17322
+ &EpochSchedule::default(),
17323
+ OldStoragesPolicy::Leave,
17324
+ );
17259
17325
let (storages, _) =
17260
17326
accounts_db.get_snapshot_storages(full_accounts_hash_slot + 1..=slot);
17261
17327
let storages = SortedStorages::new(&storages);
0 commit comments