solana_accounts_db/
utils.rs1use {
2 agave_fs::dirs,
3 log::*,
4 solana_account::{AccountSharedData, ReadableAccount, WritableAccount},
5 solana_measure::measure_time,
6 std::{
7 collections::HashSet,
8 fs, io,
9 path::{Path, PathBuf},
10 sync::Mutex,
11 thread,
12 },
13};
14
15pub const ACCOUNTS_RUN_DIR: &str = "run";
16pub const ACCOUNTS_SNAPSHOT_DIR: &str = "snapshot";
17
18pub fn create_all_accounts_run_and_snapshot_dirs(
22 account_paths: &[PathBuf],
23) -> std::io::Result<(Vec<PathBuf>, Vec<PathBuf>)> {
24 let mut run_dirs = Vec::with_capacity(account_paths.len());
25 let mut snapshot_dirs = Vec::with_capacity(account_paths.len());
26 for account_path in account_paths {
27 let (run_dir, snapshot_dir) = create_accounts_run_and_snapshot_dirs(account_path)?;
29 run_dirs.push(run_dir);
30 snapshot_dirs.push(snapshot_dir);
31 }
32 Ok((run_dirs, snapshot_dirs))
33}
34
35pub fn create_accounts_run_and_snapshot_dirs(
41 account_dir: impl AsRef<Path>,
42) -> std::io::Result<(PathBuf, PathBuf)> {
43 let run_path = account_dir.as_ref().join(ACCOUNTS_RUN_DIR);
44 let snapshot_path = account_dir.as_ref().join(ACCOUNTS_SNAPSHOT_DIR);
45 if (!run_path.is_dir()) || (!snapshot_path.is_dir()) {
46 if fs::remove_dir_all(&account_dir).is_err() {
54 dirs::remove_dir_contents(&account_dir);
55 }
56 fs::create_dir_all(&run_path)?;
57 fs::create_dir_all(&snapshot_path)?;
58 }
59
60 Ok((run_path, snapshot_path))
61}
62
63pub fn move_and_async_delete_path_contents(path: impl AsRef<Path>) {
66 move_and_async_delete_path(&path);
67 _ = std::fs::create_dir(path);
71}
72
73pub fn move_and_async_delete_path(path: impl AsRef<Path>) {
79 static IN_PROGRESS_DELETES: std::sync::LazyLock<Mutex<HashSet<PathBuf>>> =
80 std::sync::LazyLock::new(|| Mutex::new(HashSet::new()));
81
82 let mut lock = IN_PROGRESS_DELETES.lock().unwrap();
84
85 if !path.as_ref().exists() {
87 return;
88 }
89
90 if lock.contains(path.as_ref()) {
93 return;
94 }
95
96 let mut path_delete = path.as_ref().to_path_buf();
97 path_delete.set_file_name(format!(
98 "{}{}",
99 path_delete.file_name().unwrap().to_str().unwrap(),
100 "_to_be_deleted"
101 ));
102 if let Err(err) = fs::rename(&path, &path_delete) {
103 warn!(
104 "Cannot async delete, retrying in sync mode: failed to rename '{}' to '{}': {err}",
105 path.as_ref().display(),
106 path_delete.display(),
107 );
108 lock.insert(path.as_ref().to_path_buf());
111 drop(lock); dirs::remove_dir_contents(&path);
114 IN_PROGRESS_DELETES.lock().unwrap().remove(path.as_ref());
115 return;
116 }
117
118 lock.insert(path_delete.clone());
119 drop(lock);
120 thread::Builder::new()
121 .name("solDeletePath".to_string())
122 .spawn(move || {
123 trace!("background deleting {}...", path_delete.display());
124 let (result, measure_delete) = measure_time!(dirs::remove_dir_all(&path_delete));
125 if let Err(err) = result {
126 panic!("Failed to async delete '{}': {err}", path_delete.display());
127 }
128 trace!(
129 "background deleting {}... Done, and{measure_delete}",
130 path_delete.display()
131 );
132
133 IN_PROGRESS_DELETES.lock().unwrap().remove(&path_delete);
134 })
135 .expect("spawn background delete thread");
136}
137
138pub fn create_and_canonicalize_directories(
140 directories: impl IntoIterator<Item = impl AsRef<Path>>,
141) -> io::Result<Vec<PathBuf>> {
142 directories
143 .into_iter()
144 .map(create_and_canonicalize_directory)
145 .collect()
146}
147
148pub fn create_and_canonicalize_directory(directory: impl AsRef<Path>) -> io::Result<PathBuf> {
150 fs::create_dir_all(&directory)?;
151 fs::canonicalize(directory)
152}
153
154pub fn create_account_shared_data(account: &impl ReadableAccount) -> AccountSharedData {
157 AccountSharedData::create(
158 account.lamports(),
159 account.data().to_vec(),
160 *account.owner(),
161 account.executable(),
162 account.rent_epoch(),
163 )
164}
165
166#[cfg(test)]
167mod tests {
168 use {super::*, tempfile::TempDir};
169
170 #[test]
171 pub fn test_create_all_accounts_run_and_snapshot_dirs() {
172 let (_tmp_dirs, account_paths): (Vec<TempDir>, Vec<PathBuf>) = (0..4)
173 .map(|_| {
174 let tmp_dir = tempfile::TempDir::new().unwrap();
175 let account_path = tmp_dir.path().join("accounts");
176 (tmp_dir, account_path)
177 })
178 .unzip();
179
180 let (account_run_paths, account_snapshot_paths) =
182 create_all_accounts_run_and_snapshot_dirs(&account_paths).unwrap();
183 account_run_paths.iter().all(|path| path.is_dir());
184 account_snapshot_paths.iter().all(|path| path.is_dir());
185
186 let account_path_first = account_paths.first().unwrap();
188 dirs::remove_dir_contents(account_path_first);
189 assert!(account_path_first.exists());
190 assert!(!account_path_first.join(ACCOUNTS_RUN_DIR).exists());
191 assert!(!account_path_first.join(ACCOUNTS_SNAPSHOT_DIR).exists());
192
193 _ = create_all_accounts_run_and_snapshot_dirs(&account_paths).unwrap();
194 account_run_paths.iter().all(|path| path.is_dir());
195 account_snapshot_paths.iter().all(|path| path.is_dir());
196 }
197}