feat: cleanup old binary files after successful update#3263
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a cleanup mechanism (cleanup_old_versions) to BinaryManager to remove old binary version directories and retain only the currently selected version, which is triggered during binary initialization in BinaryResolver. Feedback on the implementation points out several issues in cleanup_old_versions, including synchronous blocking I/O calls on Tokio threads, silent error suppression during directory traversal, unnecessary path allocations, and potential failures during concurrent deletions. A non-blocking, more robust implementation is suggested to address these concerns.
| pub async fn cleanup_old_versions(&self) -> Result<(), Error> { | ||
| let binary_folder = self.adapter.get_binary_folder()?; | ||
| if !binary_folder.exists() { | ||
| return Ok(()); | ||
| } | ||
|
|
||
| let current_version = &self.selected_version; | ||
|
|
||
| let mut entries = tokio::fs::read_dir(&binary_folder).await?; | ||
| while let Ok(Some(entry)) = entries.next_entry().await { | ||
| let path = entry.path(); | ||
| if !path.is_dir() { | ||
| continue; | ||
| } | ||
| if let Some(folder_name) = path.file_name().and_then(|n| n.to_str()) { | ||
| if folder_name != current_version { | ||
| info!(target: LOG_TARGET_APP_LOGIC, "Cleaning up old binary version directory: {}", path.display()); | ||
| if let Err(e) = tokio::fs::remove_dir_all(&path).await { | ||
| warn!(target: LOG_TARGET_APP_LOGIC, "Failed to remove old binary version directory {}: {}", path.display(), e); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| Ok(()) | ||
| } |
There was a problem hiding this comment.
Several issues exist in the cleanup_old_versions implementation:
- Synchronous I/O Blocking Tokio Threads:
binary_folder.exists()andpath.is_dir()are synchronous blocking calls. Calling them inside an async function blocks the Tokio executor thread, which can degrade application performance. Usingtokio::fs::read_direrror handling andentry.file_type().awaitis non-blocking and much more efficient. - Silent Error Suppression: The loop condition
while let Ok(Some(entry)) = entries.next_entry().awaitwill silently terminate ifnext_entry()returns anErr. It is safer to propagate the error using?. - Unnecessary Allocations:
entry.path()allocates a newPathBufon every iteration. We can useentry.file_name()to check the folder name first, and only allocate the full path viaentry.path()when we actually need to delete the directory. - Handling Concurrent Deletions: If multiple binary managers try to clean up the same folder concurrently,
tokio::fs::remove_dir_allmight fail withErrorKind::NotFound. We should ignoreNotFounderrors to avoid logging false-positive warnings.
pub async fn cleanup_old_versions(&self) -> Result<(), Error> {
let binary_folder = self.adapter.get_binary_folder()?;
let mut entries = match tokio::fs::read_dir(&binary_folder).await {
Ok(entries) => entries,
Err(ref e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(()),
Err(e) => return Err(e.into()),
};
let current_version = &self.selected_version;
while let Some(entry) = entries.next_entry().await? {
let file_type = match entry.file_type().await {
Ok(ft) => ft,
Err(_) => continue,
};
if !file_type.is_dir() {
continue;
}
if let Some(folder_name) = entry.file_name().to_str() {
if folder_name != current_version {
let path = entry.path();
info!(target: LOG_TARGET_APP_LOGIC, "Cleaning up old binary version directory: {}", path.display());
if let Err(e) = tokio::fs::remove_dir_all(&path).await {
if e.kind() != std::io::ErrorKind::NotFound {
warn!(target: LOG_TARGET_APP_LOGIC, "Failed to remove old binary version directory {}: {}", path.display(), e);
}
}
}
}
}
Ok(())
}bb48ba3 to
12068af
Compare
12068af to
ffeb01d
Compare
|
Hi team! Just checking in to see if you've had a chance to review this PR, or if there's any feedback or further changes needed to merge this. Thanks! |
Summary
Closes #3028
This PR implements the automatic cleanup of old binary files after a successful update and on startup.
Implementation Details
cleanup_old_versions()method inBinaryManagerthat iterates over the binary's root directory and removes all subdirectories that do not match the currentselected_version.cleanup_old_versions()fromBinaryResolver::initialize_binary()whenever the binary files are verified as existing, or after a new download successfully completes.