Skip to content
This repository was archived by the owner on Jan 22, 2025. It is now read-only.

Commit dd28fd5

Browse files
v1.17: Cleans up stale accounts hash cache files (backport of #34933) (#34937)
* Cleans up stale accounts hash cache files (#34933) (cherry picked from commit 5898b9a) # Conflicts: # accounts-db/src/cache_hash_data.rs * resolves merge conflicts --------- Co-authored-by: Brooks <[email protected]>
1 parent de8415c commit dd28fd5

File tree

2 files changed

+110
-16
lines changed

2 files changed

+110
-16
lines changed

accounts-db/src/accounts_db.rs

+11-5
Original file line numberDiff line numberDiff line change
@@ -7637,6 +7637,7 @@ impl AccountsDb {
76377637
config: &CalcAccountsHashConfig<'_>,
76387638
kind: CalcAccountsHashKind,
76397639
slot: Slot,
7640+
storages_start_slot: Slot,
76407641
) -> CacheHashData {
76417642
let accounts_hash_cache_path = if !config.store_detailed_debug_info_on_failure {
76427643
accounts_hash_cache_path
@@ -7648,7 +7649,10 @@ impl AccountsDb {
76487649
_ = std::fs::remove_dir_all(&failed_dir);
76497650
failed_dir
76507651
};
7651-
CacheHashData::new(accounts_hash_cache_path, kind == CalcAccountsHashKind::Full)
7652+
CacheHashData::new(
7653+
accounts_hash_cache_path,
7654+
(kind == CalcAccountsHashKind::Incremental).then_some(storages_start_slot),
7655+
)
76527656
}
76537657

76547658
// modeled after calculate_accounts_delta_hash
@@ -7707,7 +7711,8 @@ impl AccountsDb {
77077711
) -> Result<(AccountsHashKind, u64), AccountsHashVerificationError> {
77087712
let total_time = Measure::start("");
77097713
let _guard = self.active_stats.activate(ActiveStatItem::Hash);
7710-
stats.oldest_root = storages.range().start;
7714+
let storages_start_slot = storages.range().start;
7715+
stats.oldest_root = storages_start_slot;
77117716

77127717
self.mark_old_slots_as_dirty(storages, config.epoch_schedule.slots_per_epoch, &mut stats);
77137718

@@ -7723,7 +7728,8 @@ impl AccountsDb {
77237728
accounts_hash_cache_path,
77247729
config,
77257730
kind,
7726-
slot
7731+
slot,
7732+
storages_start_slot,
77277733
));
77287734
stats.cache_hash_data_us += cache_hash_data_us;
77297735

@@ -9963,7 +9969,7 @@ pub mod tests {
99639969
let temp_dir = TempDir::new().unwrap();
99649970
let accounts_hash_cache_path = temp_dir.path().to_path_buf();
99659971
self.scan_snapshot_stores_with_cache(
9966-
&CacheHashData::new(accounts_hash_cache_path, true),
9972+
&CacheHashData::new(accounts_hash_cache_path, None),
99679973
storage,
99689974
stats,
99699975
bins,
@@ -11083,7 +11089,7 @@ pub mod tests {
1108311089
};
1108411090

1108511091
let result = accounts_db.scan_account_storage_no_bank(
11086-
&CacheHashData::new(accounts_hash_cache_path, true),
11092+
&CacheHashData::new(accounts_hash_cache_path, None),
1108711093
&CalcAccountsHashConfig::default(),
1108811094
&get_storage_refs(&[storage]),
1108911095
test_scan,

accounts-db/src/cache_hash_data.rs

+99-11
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use {
66
bytemuck::{Pod, Zeroable},
77
memmap2::MmapMut,
88
solana_measure::measure::Measure,
9+
solana_sdk::clock::Slot,
910
std::{
1011
collections::HashSet,
1112
fs::{self, remove_file, File, OpenOptions},
@@ -192,40 +193,59 @@ impl CacheHashDataFile {
192193
pub(crate) struct CacheHashData {
193194
cache_dir: PathBuf,
194195
pre_existing_cache_files: Arc<Mutex<HashSet<PathBuf>>>,
195-
should_delete_old_cache_files_on_drop: bool,
196+
/// Decides which old cache files to delete. See `delete_old_cache_files()` for more info.
197+
storages_start_slot: Option<Slot>,
196198
pub stats: Arc<CacheHashDataStats>,
197199
}
198200

199201
impl Drop for CacheHashData {
200202
fn drop(&mut self) {
201-
if self.should_delete_old_cache_files_on_drop {
202-
self.delete_old_cache_files();
203-
}
203+
self.delete_old_cache_files();
204204
self.stats.report();
205205
}
206206
}
207207

208208
impl CacheHashData {
209-
pub(crate) fn new(
210-
cache_dir: PathBuf,
211-
should_delete_old_cache_files_on_drop: bool,
212-
) -> CacheHashData {
209+
pub(crate) fn new(cache_dir: PathBuf, storages_start_slot: Option<Slot>) -> CacheHashData {
213210
std::fs::create_dir_all(&cache_dir).unwrap_or_else(|err| {
214211
panic!("error creating cache dir {}: {err}", cache_dir.display())
215212
});
216213

217214
let result = CacheHashData {
218215
cache_dir,
219216
pre_existing_cache_files: Arc::new(Mutex::new(HashSet::default())),
220-
should_delete_old_cache_files_on_drop,
217+
storages_start_slot,
221218
stats: Arc::default(),
222219
};
223220

224221
result.get_cache_files();
225222
result
226223
}
224+
225+
/// delete all pre-existing files that will not be used
227226
fn delete_old_cache_files(&self) {
228-
let old_cache_files = std::mem::take(&mut *self.pre_existing_cache_files.lock().unwrap());
227+
// all the renaming files in `pre_existing_cache_files` were *not* used for this
228+
// accounts hash calculation
229+
let mut old_cache_files =
230+
std::mem::take(&mut *self.pre_existing_cache_files.lock().unwrap());
231+
232+
// If `storages_start_slot` is None, we're doing a full accounts hash calculation, and thus
233+
// all unused cache files can be deleted.
234+
// If `storages_start_slot` is Some, we're doing an incremental accounts hash calculation,
235+
// and we only want to delete the unused cache files *that IAH considered*.
236+
if let Some(storages_start_slot) = self.storages_start_slot {
237+
old_cache_files.retain(|old_cache_file| {
238+
let Some(parsed_filename) = parse_filename(old_cache_file) else {
239+
// if parsing the cache filename fails, we *do* want to delete it
240+
return true;
241+
};
242+
243+
// if the old cache file is in the incremental accounts hash calculation range,
244+
// then delete it
245+
parsed_filename.slot_range_start >= storages_start_slot
246+
});
247+
}
248+
229249
if !old_cache_files.is_empty() {
230250
self.stats
231251
.unused_cache_files
@@ -356,6 +376,39 @@ impl CacheHashData {
356376
}
357377
}
358378

379+
/// The values of each part of a cache hash data filename
380+
#[derive(Debug)]
381+
pub struct ParsedFilename {
382+
pub slot_range_start: Slot,
383+
pub slot_range_end: Slot,
384+
pub bin_range_start: u64,
385+
pub bin_range_end: u64,
386+
pub hash: u64,
387+
}
388+
389+
/// Parses a cache hash data filename into its parts
390+
///
391+
/// Returns None if the filename is invalid
392+
fn parse_filename(cache_filename: impl AsRef<Path>) -> Option<ParsedFilename> {
393+
let filename = cache_filename.as_ref().to_string_lossy().to_string();
394+
let parts: Vec<_> = filename.split('.').collect(); // The parts are separated by a `.`
395+
if parts.len() != 5 {
396+
return None;
397+
}
398+
let slot_range_start = parts.first()?.parse().ok()?;
399+
let slot_range_end = parts.get(1)?.parse().ok()?;
400+
let bin_range_start = parts.get(2)?.parse().ok()?;
401+
let bin_range_end = parts.get(3)?.parse().ok()?;
402+
let hash = u64::from_str_radix(parts.get(4)?, 16).ok()?; // the hash is in hex
403+
Some(ParsedFilename {
404+
slot_range_start,
405+
slot_range_end,
406+
bin_range_start,
407+
bin_range_end,
408+
hash,
409+
})
410+
}
411+
359412
#[cfg(test)]
360413
mod tests {
361414
use {super::*, rand::Rng};
@@ -423,7 +476,7 @@ mod tests {
423476
data_this_pass.push(this_bin_data);
424477
}
425478
}
426-
let cache = CacheHashData::new(cache_dir.clone(), true);
479+
let cache = CacheHashData::new(cache_dir.clone(), None);
427480
let file_name = PathBuf::from("test");
428481
cache.save(&file_name, &data_this_pass).unwrap();
429482
cache.get_cache_files();
@@ -513,4 +566,39 @@ mod tests {
513566
ct,
514567
)
515568
}
569+
570+
#[test]
571+
fn test_parse_filename() {
572+
let good_filename = "123.456.0.65536.537d65697d9b2baa";
573+
let parsed_filename = parse_filename(good_filename).unwrap();
574+
assert_eq!(parsed_filename.slot_range_start, 123);
575+
assert_eq!(parsed_filename.slot_range_end, 456);
576+
assert_eq!(parsed_filename.bin_range_start, 0);
577+
assert_eq!(parsed_filename.bin_range_end, 65536);
578+
assert_eq!(parsed_filename.hash, 0x537d65697d9b2baa);
579+
580+
let bad_filenames = [
581+
// bad separator
582+
"123-456-0-65536.537d65697d9b2baa",
583+
// bad values
584+
"abc.456.0.65536.537d65697d9b2baa",
585+
"123.xyz.0.65536.537d65697d9b2baa",
586+
"123.456.?.65536.537d65697d9b2baa",
587+
"123.456.0.@#$%^.537d65697d9b2baa",
588+
"123.456.0.65536.base19shouldfail",
589+
"123.456.0.65536.123456789012345678901234567890",
590+
// missing values
591+
"123.456.0.65536.",
592+
"123.456.0.65536",
593+
// extra junk
594+
"123.456.0.65536.537d65697d9b2baa.42",
595+
"123.456.0.65536.537d65697d9b2baa.",
596+
"123.456.0.65536.537d65697d9b2baa/",
597+
".123.456.0.65536.537d65697d9b2baa",
598+
"/123.456.0.65536.537d65697d9b2baa",
599+
];
600+
for bad_filename in bad_filenames {
601+
assert!(parse_filename(bad_filename).is_none());
602+
}
603+
}
516604
}

0 commit comments

Comments
 (0)