solana_accounts_db/
utils.rs1use {
2 lazy_static,
3 log::*,
4 solana_measure::measure_time,
5 std::{
6 collections::HashSet,
7 fs, io,
8 path::{Path, PathBuf},
9 sync::Mutex,
10 thread,
11 },
12};
13
14pub const ACCOUNTS_RUN_DIR: &str = "run";
15pub const ACCOUNTS_SNAPSHOT_DIR: &str = "snapshot";
16
17pub fn create_all_accounts_run_and_snapshot_dirs(
21 account_paths: &[PathBuf],
22) -> std::io::Result<(Vec<PathBuf>, Vec<PathBuf>)> {
23 let mut run_dirs = Vec::with_capacity(account_paths.len());
24 let mut snapshot_dirs = Vec::with_capacity(account_paths.len());
25 for account_path in account_paths {
26 let (run_dir, snapshot_dir) = create_accounts_run_and_snapshot_dirs(account_path)?;
28 run_dirs.push(run_dir);
29 snapshot_dirs.push(snapshot_dir);
30 }
31 Ok((run_dirs, snapshot_dirs))
32}
33
34pub fn create_accounts_run_and_snapshot_dirs(
40 account_dir: impl AsRef<Path>,
41) -> std::io::Result<(PathBuf, PathBuf)> {
42 let run_path = account_dir.as_ref().join(ACCOUNTS_RUN_DIR);
43 let snapshot_path = account_dir.as_ref().join(ACCOUNTS_SNAPSHOT_DIR);
44 if (!run_path.is_dir()) || (!snapshot_path.is_dir()) {
45 if fs::remove_dir_all(&account_dir).is_err() {
53 delete_contents_of_path(&account_dir);
54 }
55 fs::create_dir_all(&run_path)?;
56 fs::create_dir_all(&snapshot_path)?;
57 }
58
59 Ok((run_path, snapshot_path))
60}
61
62pub fn move_and_async_delete_path_contents(path: impl AsRef<Path>) {
65 move_and_async_delete_path(&path);
66 _ = std::fs::create_dir(path);
70}
71
72pub fn move_and_async_delete_path(path: impl AsRef<Path>) {
78 lazy_static! {
79 static ref IN_PROGRESS_DELETES: Mutex<HashSet<PathBuf>> = Mutex::new(HashSet::new());
80 };
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); delete_contents_of_path(&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!(fs::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 delete_contents_of_path(path: impl AsRef<Path>) {
143 match fs::read_dir(&path) {
144 Err(err) => {
145 warn!(
146 "Failed to delete contents of '{}': could not read dir: {err}",
147 path.as_ref().display(),
148 )
149 }
150 Ok(dir_entries) => {
151 for entry in dir_entries.flatten() {
152 let sub_path = entry.path();
153 let result = if sub_path.is_dir() {
154 fs::remove_dir_all(&sub_path)
155 } else {
156 fs::remove_file(&sub_path)
157 };
158 if let Err(err) = result {
159 warn!(
160 "Failed to delete contents of '{}': {err}",
161 sub_path.display(),
162 );
163 }
164 }
165 }
166 }
167}
168
169pub fn create_and_canonicalize_directories(
171 directories: impl IntoIterator<Item = impl AsRef<Path>>,
172) -> io::Result<Vec<PathBuf>> {
173 directories
174 .into_iter()
175 .map(create_and_canonicalize_directory)
176 .collect()
177}
178
179pub fn create_and_canonicalize_directory(directory: impl AsRef<Path>) -> io::Result<PathBuf> {
181 fs::create_dir_all(&directory)?;
182 fs::canonicalize(directory)
183}
184
185#[cfg(test)]
186mod tests {
187 use {super::*, tempfile::TempDir};
188
189 #[test]
190 pub fn test_create_all_accounts_run_and_snapshot_dirs() {
191 let (_tmp_dirs, account_paths): (Vec<TempDir>, Vec<PathBuf>) = (0..4)
192 .map(|_| {
193 let tmp_dir = tempfile::TempDir::new().unwrap();
194 let account_path = tmp_dir.path().join("accounts");
195 (tmp_dir, account_path)
196 })
197 .unzip();
198
199 let (account_run_paths, account_snapshot_paths) =
201 create_all_accounts_run_and_snapshot_dirs(&account_paths).unwrap();
202 account_run_paths.iter().all(|path| path.is_dir());
203 account_snapshot_paths.iter().all(|path| path.is_dir());
204
205 let account_path_first = account_paths.first().unwrap();
207 delete_contents_of_path(account_path_first);
208 assert!(account_path_first.exists());
209 assert!(!account_path_first.join(ACCOUNTS_RUN_DIR).exists());
210 assert!(!account_path_first.join(ACCOUNTS_SNAPSHOT_DIR).exists());
211
212 _ = create_all_accounts_run_and_snapshot_dirs(&account_paths).unwrap();
213 account_run_paths.iter().all(|path| path.is_dir());
214 account_snapshot_paths.iter().all(|path| path.is_dir());
215 }
216}