Skip to content

Commit 0e6aff8

Browse files
authored
Merge pull request #1011 from dlachaumepalo/damien/983/clean-pending-snapshot-directory
Clean pending_snapshot directory of aggregator
2 parents 5e53a67 + 17d42e0 commit 0e6aff8

File tree

5 files changed

+163
-33
lines changed

5 files changed

+163
-33
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mithril-aggregator/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mithril-aggregator"
3-
version = "0.3.40"
3+
version = "0.3.41"
44
description = "A Mithril Aggregator server"
55
authors = { workspace = true }
66
edition = { workspace = true }

mithril-aggregator/src/artifact_builder/cardano_immutable_files_full.rs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ impl CardanoImmutableFilesFullArtifactBuilder {
8181
let location = self
8282
.snapshot_uploader
8383
.upload_snapshot(ongoing_snapshot.get_file_path())
84-
.await?;
84+
.await;
8585

8686
if let Err(error) = tokio::fs::remove_file(ongoing_snapshot.get_file_path()).await {
8787
warn!(
@@ -90,7 +90,7 @@ impl CardanoImmutableFilesFullArtifactBuilder {
9090
);
9191
}
9292

93-
Ok(vec![location])
93+
Ok(vec![location?])
9494
}
9595

9696
async fn create_snapshot(
@@ -152,7 +152,7 @@ mod tests {
152152

153153
use super::*;
154154

155-
use crate::{DumbSnapshotUploader, DumbSnapshotter};
155+
use crate::{snapshot_uploaders::MockSnapshotUploader, DumbSnapshotUploader, DumbSnapshotter};
156156

157157
#[tokio::test]
158158
async fn should_compute_valid_artifact() {
@@ -249,4 +249,32 @@ mod tests {
249249
ongoing_snapshot.get_file_path()
250250
);
251251
}
252+
253+
#[tokio::test]
254+
async fn remove_snapshot_archive_after_upload_even_if_an_error_occured() {
255+
let file = NamedTempFile::new().unwrap();
256+
let file_path = file.path();
257+
let snapshot = OngoingSnapshot::new(file_path.to_path_buf(), 7331);
258+
let mut snapshot_uploader = MockSnapshotUploader::new();
259+
snapshot_uploader
260+
.expect_upload_snapshot()
261+
.return_once(|_| Err("an error".to_string()))
262+
.once();
263+
264+
let cardano_immutable_files_full_artifact_builder =
265+
CardanoImmutableFilesFullArtifactBuilder::new(
266+
Arc::new(DumbSnapshotter::new()),
267+
Arc::new(snapshot_uploader),
268+
);
269+
270+
cardano_immutable_files_full_artifact_builder
271+
.upload_snapshot_archive(&snapshot)
272+
.await
273+
.expect_err("Snapshot upload should have failed");
274+
275+
assert!(
276+
!file_path.exists(),
277+
"Ongoing snapshot file should have been removed even after upload failure"
278+
);
279+
}
252280
}

mithril-aggregator/src/dependency_injection/builder.rs

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -574,23 +574,10 @@ impl DependenciesBuilder {
574574
.snapshot_directory
575575
.join("pending_snapshot");
576576

577-
// **TODO** this code should be in the snapshotter constructor.
578-
if !ongoing_snapshot_directory.exists() {
579-
std::fs::create_dir(&ongoing_snapshot_directory).map_err(|e| {
580-
DependenciesBuilderError::Initialization {
581-
message: format!(
582-
"Cannot create snapshotter directory '{}'.",
583-
ongoing_snapshot_directory.display()
584-
),
585-
error: Some(e.into()),
586-
}
587-
})?;
588-
}
589-
590577
Arc::new(GzipSnapshotter::new(
591578
self.configuration.db_directory.clone(),
592579
ongoing_snapshot_directory,
593-
))
580+
)?)
594581
}
595582
_ => Arc::new(DumbSnapshotter::new()),
596583
};

mithril-aggregator/src/snapshotter.rs

Lines changed: 129 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
use flate2::write::GzEncoder;
22
use flate2::Compression;
3-
use slog_scope::info;
3+
use mithril_common::StdResult;
4+
use slog_scope::{info, warn};
45
use std::error::Error as StdError;
56
use std::fs::File;
67
use std::io;
78
use std::path::{Path, PathBuf};
89
use std::sync::RwLock;
910
use thiserror::Error;
1011

12+
use crate::dependency_injection::DependenciesBuilderError;
13+
1114
/// Define the ability to create snapshots.
1215
pub trait Snapshotter: Sync + Send {
1316
/// Create a new snapshot with the given archive name.
@@ -33,6 +36,7 @@ impl OngoingSnapshot {
3336
pub fn new(filepath: PathBuf, filesize: u64) -> Self {
3437
Self { filepath, filesize }
3538
}
39+
3640
pub fn get_file_path(&self) -> &PathBuf {
3741
&self.filepath
3842
}
@@ -60,33 +64,69 @@ pub enum SnapshotError {
6064

6165
impl Snapshotter for GzipSnapshotter {
6266
fn snapshot(&self, archive_name: &str) -> Result<OngoingSnapshot, SnapshotError> {
63-
let filepath = self.create_archive(archive_name)?;
64-
let filesize = std::fs::metadata(&filepath)
65-
.map_err(|e| SnapshotError::GeneralError(e.to_string()))?
66-
.len();
67+
let archive_path = self.ongoing_snapshot_directory.join(archive_name);
68+
let filesize = self.create_archive(&archive_path).map_err(|err| {
69+
if archive_path.exists() {
70+
if let Err(remove_error) = std::fs::remove_file(&archive_path) {
71+
warn!(
72+
" > Post snapshotter.snapshot failure, could not remove temporary archive at path: path:{}, err: {}",
73+
archive_path.display(),
74+
remove_error
75+
);
76+
}
77+
}
78+
79+
err
80+
})?;
6781

68-
Ok(OngoingSnapshot { filepath, filesize })
82+
Ok(OngoingSnapshot {
83+
filepath: archive_path,
84+
filesize,
85+
})
6986
}
7087
}
7188

7289
impl GzipSnapshotter {
7390
/// Snapshotter factory
74-
pub fn new(db_directory: PathBuf, ongoing_snapshot_directory: PathBuf) -> Self {
75-
Self {
91+
pub fn new(
92+
db_directory: PathBuf,
93+
ongoing_snapshot_directory: PathBuf,
94+
) -> StdResult<GzipSnapshotter> {
95+
if ongoing_snapshot_directory.exists() {
96+
std::fs::remove_dir_all(&ongoing_snapshot_directory)?;
97+
}
98+
99+
std::fs::create_dir(&ongoing_snapshot_directory).map_err(|e| {
100+
DependenciesBuilderError::Initialization {
101+
message: format!(
102+
"Cannot create snapshotter directory '{}'.",
103+
ongoing_snapshot_directory.display()
104+
),
105+
error: Some(e.into()),
106+
}
107+
})?;
108+
109+
Ok(Self {
76110
db_directory,
77111
ongoing_snapshot_directory,
78-
}
112+
})
113+
}
114+
115+
fn get_file_size(filepath: &Path) -> Result<u64, SnapshotError> {
116+
let res = std::fs::metadata(filepath)
117+
.map_err(|e| SnapshotError::GeneralError(e.to_string()))?
118+
.len();
119+
Ok(res)
79120
}
80121

81-
fn create_archive(&self, archive_name: &str) -> Result<PathBuf, SnapshotError> {
82-
let path = self.ongoing_snapshot_directory.join(archive_name);
122+
fn create_archive(&self, archive_path: &Path) -> Result<u64, SnapshotError> {
83123
info!(
84124
"compressing {} into {}",
85125
self.db_directory.display(),
86-
path.display()
126+
archive_path.display()
87127
);
88128

89-
let tar_gz = File::create(&path).map_err(SnapshotError::CreateArchiveError)?;
129+
let tar_gz = File::create(archive_path).map_err(SnapshotError::CreateArchiveError)?;
90130
let enc = GzEncoder::new(tar_gz, Compression::default());
91131
let mut tar = tar::Builder::new(enc);
92132

@@ -98,7 +138,9 @@ impl GzipSnapshotter {
98138
.map_err(SnapshotError::CreateArchiveError)?;
99139
gz.try_finish().map_err(SnapshotError::CreateArchiveError)?;
100140

101-
Ok(path)
141+
let filesize = Self::get_file_size(archive_path)?;
142+
143+
Ok(filesize)
102144
}
103145
}
104146

@@ -154,8 +196,23 @@ impl Snapshotter for DumbSnapshotter {
154196

155197
#[cfg(test)]
156198
mod tests {
199+
use std::sync::Arc;
200+
157201
use super::*;
158202

203+
fn get_test_directory(dir_name: &str) -> PathBuf {
204+
let test_dir = std::env::temp_dir()
205+
.join("mithril_test")
206+
.join("snapshotter")
207+
.join(dir_name);
208+
if test_dir.exists() {
209+
std::fs::remove_dir_all(&test_dir).unwrap();
210+
}
211+
std::fs::create_dir_all(&test_dir).unwrap();
212+
213+
test_dir
214+
}
215+
159216
#[test]
160217
fn test_dumb_snapshotter() {
161218
let snapshotter = DumbSnapshotter::new();
@@ -174,4 +231,62 @@ mod tests {
174231
)
175232
);
176233
}
234+
235+
#[test]
236+
fn should_create_directory_if_does_not_exist() {
237+
let test_dir = get_test_directory("should_create_directory_if_does_not_exist");
238+
let pending_snapshot_directory = test_dir.join("pending_snapshot");
239+
let db_directory = std::env::temp_dir().join("whatever");
240+
241+
Arc::new(GzipSnapshotter::new(db_directory, pending_snapshot_directory.clone()).unwrap());
242+
243+
assert!(pending_snapshot_directory.is_dir());
244+
}
245+
246+
#[test]
247+
fn should_clean_pending_snapshot_directory_if_already_exists() {
248+
let test_dir =
249+
get_test_directory("should_clean_pending_snapshot_directory_if_already_exists");
250+
let pending_snapshot_directory = test_dir.join("pending_snapshot");
251+
let db_directory = std::env::temp_dir().join("whatever");
252+
253+
std::fs::create_dir_all(&pending_snapshot_directory).unwrap();
254+
255+
std::fs::File::create(pending_snapshot_directory.join("whatever.txt")).unwrap();
256+
257+
Arc::new(GzipSnapshotter::new(db_directory, pending_snapshot_directory.clone()).unwrap());
258+
259+
assert_eq!(
260+
0,
261+
std::fs::read_dir(pending_snapshot_directory)
262+
.unwrap()
263+
.count()
264+
);
265+
}
266+
267+
#[test]
268+
fn should_delete_tmp_file_in_pending_snapshot_directory_if_snapshotting_fail() {
269+
let test_dir = get_test_directory(
270+
"should_delete_tmp_file_in_pending_snapshot_directory_if_snapshotting_fail",
271+
);
272+
let pending_snapshot_directory = test_dir.join("pending_snapshot");
273+
let db_directory = test_dir.join("db");
274+
275+
let snapshotter = Arc::new(
276+
GzipSnapshotter::new(db_directory, pending_snapshot_directory.clone()).unwrap(),
277+
);
278+
279+
// this file should not be deleted by the archive creation
280+
std::fs::File::create(pending_snapshot_directory.join("other-process.file")).unwrap();
281+
282+
let _ = snapshotter
283+
.snapshot("whatever.tar.gz")
284+
.expect_err("Snapshotter::snapshot should fail if the db is empty.");
285+
let remaining_files: Vec<String> = std::fs::read_dir(&pending_snapshot_directory)
286+
.unwrap()
287+
.map(|f| f.unwrap().file_name().to_str().unwrap().to_owned())
288+
.collect();
289+
290+
assert_eq!(vec!["other-process.file".to_string()], remaining_files);
291+
}
177292
}

0 commit comments

Comments
 (0)