Skip to content

Commit 08a8a5d

Browse files
committed
refactor(upload): refactor profile-archive
1 parent 723fcec commit 08a8a5d

File tree

7 files changed

+113
-104
lines changed

7 files changed

+113
-104
lines changed

src/run/run_environment/local/provider.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::run::config::RepositoryOverride;
88
use crate::run::helpers::{GitRemote, parse_git_remote};
99
use crate::run::run_environment::{RunEnvironment, RunPart};
1010
use crate::run::runner::ExecutorName;
11-
use crate::run::uploader::{Runner, UploadMetadata};
11+
use crate::run::uploader::{ProfileArchive, Runner, UploadMetadata};
1212
use crate::run::{
1313
config::Config,
1414
helpers::find_repository_root,
@@ -147,8 +147,7 @@ impl RunEnvironmentProvider for LocalProvider {
147147
&self,
148148
config: &Config,
149149
system_info: &SystemInfo,
150-
archive_hash: &str,
151-
content_encoding: Option<String>,
150+
profile_archive: &ProfileArchive,
152151
executor_name: ExecutorName,
153152
) -> Result<UploadMetadata> {
154153
let run_environment_metadata = self.get_run_environment_metadata()?;
@@ -159,8 +158,8 @@ impl RunEnvironmentProvider for LocalProvider {
159158
repository_provider: self.get_repository_provider(),
160159
commit_hash: run_environment_metadata.ref_.clone(),
161160
run_environment_metadata,
162-
profile_md5: archive_hash.into(),
163-
content_encoding,
161+
profile_md5: profile_archive.hash.clone(),
162+
profile_encoding: profile_archive.content.encoding(),
164163
runner: Runner {
165164
name: "codspeed-runner".into(),
166165
version: crate::VERSION.into(),

src/run/run_environment/provider.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::prelude::*;
55
use crate::run::check_system::SystemInfo;
66
use crate::run::config::Config;
77
use crate::run::runner::ExecutorName;
8-
use crate::run::uploader::{Runner, UploadMetadata};
8+
use crate::run::uploader::{ProfileArchive, Runner, UploadMetadata};
99

1010
use super::interfaces::{RepositoryProvider, RunEnvironment, RunEnvironmentMetadata, RunPart};
1111

@@ -66,8 +66,7 @@ pub trait RunEnvironmentProvider {
6666
&self,
6767
config: &Config,
6868
system_info: &SystemInfo,
69-
archive_hash: &str,
70-
content_encoding: Option<String>,
69+
profile_archive: &ProfileArchive,
7170
executor_name: ExecutorName,
7271
) -> Result<UploadMetadata> {
7372
let run_environment_metadata = self.get_run_environment_metadata()?;
@@ -79,8 +78,8 @@ pub trait RunEnvironmentProvider {
7978
tokenless: config.token.is_none(),
8079
repository_provider: self.get_repository_provider(),
8180
run_environment_metadata,
82-
profile_md5: archive_hash.into(),
83-
content_encoding,
81+
profile_md5: profile_archive.hash.clone(),
82+
profile_encoding: profile_archive.content.encoding(),
8483
commit_hash,
8584
runner: Runner {
8685
name: "codspeed-runner".into(),

src/run/uploader/interfaces.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub struct UploadMetadata {
1414
pub version: Option<u32>,
1515
pub tokenless: bool,
1616
pub profile_md5: String,
17-
pub content_encoding: Option<String>,
17+
pub profile_encoding: Option<String>,
1818
pub runner: Runner,
1919
pub run_environment: RunEnvironment,
2020
pub run_part: Option<RunPart>,

src/run/uploader/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
mod interfaces;
2+
mod profile_archive;
23
mod upload;
34
mod upload_metadata;
45

56
pub use interfaces::*;
7+
pub use profile_archive::ProfileArchive;
68
pub use upload::upload;
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
use base64::{Engine, engine::general_purpose};
2+
3+
use crate::prelude::*;
4+
use std::path::PathBuf;
5+
6+
#[derive(Debug)]
7+
pub struct ProfileArchive {
8+
pub hash: String,
9+
pub content: ProfileArchiveContent,
10+
}
11+
12+
#[derive(Debug)]
13+
pub enum ProfileArchiveContent {
14+
CompressedInMemory { data: Vec<u8> },
15+
UncompressedOnDisk { path: PathBuf },
16+
}
17+
18+
impl ProfileArchive {
19+
pub fn new_compressed_in_memory(data: Vec<u8>) -> Self {
20+
let hash = general_purpose::STANDARD.encode(md5::compute(&data).0);
21+
ProfileArchive {
22+
hash,
23+
content: ProfileArchiveContent::CompressedInMemory { data },
24+
}
25+
}
26+
27+
pub fn new_uncompressed_on_disk(path: PathBuf) -> Result<Self> {
28+
let metadata = std::fs::metadata(&path)?;
29+
if !metadata.is_file() {
30+
return Err(anyhow!("The provided path is not a file"));
31+
}
32+
let mut file = std::fs::File::open(&path)?;
33+
let mut buffer = Vec::new();
34+
use std::io::Read;
35+
file.read_to_end(&mut buffer)?;
36+
let hash = general_purpose::STANDARD.encode(md5::compute(&buffer).0);
37+
Ok(ProfileArchive {
38+
hash,
39+
content: ProfileArchiveContent::UncompressedOnDisk { path },
40+
})
41+
}
42+
}
43+
44+
impl ProfileArchiveContent {
45+
pub async fn size(&self) -> Result<u64> {
46+
match &self {
47+
ProfileArchiveContent::CompressedInMemory { data } => Ok(data.len() as u64),
48+
ProfileArchiveContent::UncompressedOnDisk { path } => {
49+
let metadata = tokio::fs::metadata(path).await?;
50+
Ok(metadata.len())
51+
}
52+
}
53+
}
54+
55+
pub fn encoding(&self) -> Option<String> {
56+
match self {
57+
ProfileArchiveContent::CompressedInMemory { .. } => Some("gzip".to_string()),
58+
_ => None,
59+
}
60+
}
61+
}
62+
63+
impl Drop for ProfileArchiveContent {
64+
fn drop(&mut self) {
65+
if let ProfileArchiveContent::UncompressedOnDisk { path } = self {
66+
if path.exists() {
67+
let _ = std::fs::remove_file(path);
68+
}
69+
}
70+
}
71+
}

src/run/uploader/upload.rs

Lines changed: 29 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -2,59 +2,22 @@ use crate::run::{
22
check_system::SystemInfo,
33
config::Config,
44
run_environment::{RunEnvironment, RunEnvironmentProvider},
5-
runner::ExecutorName,
6-
runner::RunData,
7-
uploader::UploadError,
5+
runner::{ExecutorName, RunData},
6+
uploader::{UploadError, profile_archive::ProfileArchiveContent},
87
};
98
use crate::{
109
prelude::*,
1110
request_client::{REQUEST_CLIENT, STREAMING_CLIENT},
1211
};
1312
use async_compression::tokio::write::GzipEncoder;
14-
use base64::{Engine as _, engine::general_purpose};
1513
use console::style;
1614
use reqwest::StatusCode;
17-
use std::path::PathBuf;
1815
use tokio::fs::File;
19-
use tokio::io::{AsyncReadExt, AsyncWriteExt};
16+
use tokio::io::AsyncWriteExt;
2017
use tokio_tar::Builder;
2118

2219
use super::interfaces::{UploadData, UploadMetadata};
23-
24-
#[derive(Debug)]
25-
enum ProfileArchive {
26-
CompressedInMemory(Vec<u8>),
27-
UncompressedOnDisk(PathBuf),
28-
}
29-
30-
impl ProfileArchive {
31-
async fn size(&self) -> Result<u64> {
32-
match self {
33-
ProfileArchive::CompressedInMemory(data) => Ok(data.len() as u64),
34-
ProfileArchive::UncompressedOnDisk(path) => {
35-
let metadata = tokio::fs::metadata(path).await?;
36-
Ok(metadata.len())
37-
}
38-
}
39-
}
40-
41-
fn to_content_encoding(&self) -> Option<String> {
42-
match self {
43-
ProfileArchive::CompressedInMemory(_) => Some("gzip".to_string()),
44-
ProfileArchive::UncompressedOnDisk(_) => None,
45-
}
46-
}
47-
}
48-
49-
impl Drop for ProfileArchive {
50-
fn drop(&mut self) {
51-
if let ProfileArchive::UncompressedOnDisk(path) = self {
52-
if path.exists() {
53-
let _ = std::fs::remove_file(path);
54-
}
55-
}
56-
}
57-
}
20+
use super::profile_archive::ProfileArchive;
5821

5922
/// Create a profile archive from the profile folder and return its md5 hash encoded in base64
6023
///
@@ -63,7 +26,7 @@ impl Drop for ProfileArchive {
6326
async fn create_profile_archive(
6427
run_data: &RunData,
6528
executor_name: ExecutorName,
66-
) -> Result<(ProfileArchive, String)> {
29+
) -> Result<ProfileArchive> {
6730
let time_start = std::time::Instant::now();
6831
let profile_archive = match executor_name {
6932
ExecutorName::Valgrind => {
@@ -74,7 +37,8 @@ async fn create_profile_archive(
7437
.await?;
7538
let mut gzip_encoder = tar.into_inner().await?;
7639
gzip_encoder.shutdown().await?;
77-
ProfileArchive::CompressedInMemory(gzip_encoder.into_inner())
40+
let data = gzip_encoder.into_inner();
41+
ProfileArchive::new_compressed_in_memory(data)
7842
}
7943
ExecutorName::WallTime => {
8044
debug!("Creating uncompressed tar archive for WallTime on disk");
@@ -92,44 +56,18 @@ async fn create_profile_archive(
9256

9357
// Persist the temporary file to prevent deletion when temp_file goes out of scope
9458
let persistent_path = temp_file.into_temp_path().keep()?;
95-
ProfileArchive::UncompressedOnDisk(persistent_path)
96-
}
97-
};
9859

99-
let (archive_digest, archive_size) = match &profile_archive {
100-
ProfileArchive::CompressedInMemory(data) => {
101-
let digest = md5::compute(data.as_slice());
102-
(digest, data.len() as u64)
103-
}
104-
ProfileArchive::UncompressedOnDisk(path) => {
105-
let mut file = File::open(path).await.context(format!(
106-
"Failed to open uncompressed file at path: {}",
107-
path.display()
108-
))?;
109-
let mut hasher = md5::Context::new();
110-
let mut buffer = [0; 8192];
111-
let mut total_size = 0u64;
112-
113-
loop {
114-
let bytes_read = file.read(&mut buffer).await?;
115-
if bytes_read == 0 {
116-
break;
117-
}
118-
hasher.consume(&buffer[..bytes_read]);
119-
total_size += bytes_read as u64;
120-
}
121-
(hasher.compute(), total_size)
60+
ProfileArchive::new_uncompressed_on_disk(persistent_path)?
12261
}
12362
};
12463

125-
let archive_hash = general_purpose::STANDARD.encode(archive_digest.0);
12664
debug!(
12765
"Created archive ({} bytes) in {:.2?}",
128-
archive_size,
66+
profile_archive.content.size().await?,
12967
time_start.elapsed()
13068
);
13169

132-
Ok((profile_archive, archive_hash))
70+
Ok(profile_archive)
13371
}
13472

13573
async fn retrieve_upload_data(
@@ -187,23 +125,26 @@ async fn retrieve_upload_data(
187125
async fn upload_profile_archive(
188126
upload_data: &UploadData,
189127
profile_archive: ProfileArchive,
190-
archive_hash: &String,
191128
) -> Result<()> {
192-
let archive_size = profile_archive.size().await?;
129+
let archive_size = profile_archive.content.size().await?;
130+
let archive_hash = profile_archive.hash;
193131

194-
let response = match &profile_archive {
195-
ProfileArchive::CompressedInMemory(data) => {
132+
let response = match &profile_archive.content {
133+
ProfileArchiveContent::CompressedInMemory { data } => {
196134
// Use regular client with retry middleware for compressed data
197-
let request = REQUEST_CLIENT
135+
let mut request = REQUEST_CLIENT
198136
.put(upload_data.upload_url.clone())
199137
.header("Content-Type", "application/x-tar")
200138
.header("Content-Length", archive_size)
201-
.header("Content-MD5", archive_hash)
202-
.header("Content-Encoding", "gzip");
139+
.header("Content-MD5", archive_hash);
140+
141+
if let Some(encoding) = profile_archive.content.encoding() {
142+
request = request.header("Content-Encoding", encoding);
143+
}
203144

204145
request.body(data.clone()).send().await?
205146
}
206-
ProfileArchive::UncompressedOnDisk(path) => {
147+
ProfileArchiveContent::UncompressedOnDisk { path } => {
207148
// Use streaming client without retry middleware for file streams
208149
let file = File::open(path)
209150
.await
@@ -248,21 +189,15 @@ pub async fn upload(
248189
run_data: &RunData,
249190
executor_name: ExecutorName,
250191
) -> Result<UploadResult> {
251-
let (profile_archive, archive_hash) =
252-
create_profile_archive(run_data, executor_name.clone()).await?;
192+
let profile_archive = create_profile_archive(run_data, executor_name.clone()).await?;
253193

254194
debug!(
255195
"Run Environment provider detected: {:?}",
256196
provider.get_run_environment()
257197
);
258198

259-
let upload_metadata = provider.get_upload_metadata(
260-
config,
261-
system_info,
262-
&archive_hash,
263-
profile_archive.to_content_encoding(),
264-
executor_name,
265-
)?;
199+
let upload_metadata =
200+
provider.get_upload_metadata(config, system_info, &profile_archive, executor_name)?;
266201
debug!("Upload metadata: {upload_metadata:#?}");
267202
info!(
268203
"Linked repository: {}\n",
@@ -283,8 +218,11 @@ pub async fn upload(
283218
debug!("runId: {}", upload_data.run_id);
284219

285220
info!("Uploading performance data...");
286-
debug!("Uploading {} bytes...", profile_archive.size().await?);
287-
upload_profile_archive(&upload_data, profile_archive, &archive_hash).await?;
221+
debug!(
222+
"Uploading {} bytes...",
223+
profile_archive.content.size().await?
224+
);
225+
upload_profile_archive(&upload_data, profile_archive).await?;
288226
info!("Performance data uploaded");
289227

290228
Ok(UploadResult {

src/run/uploader/upload_metadata.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ mod tests {
3333
version: Some(7),
3434
tokenless: true,
3535
profile_md5: "jp/k05RKuqP3ERQuIIvx4Q==".into(),
36-
content_encoding: Some("gzip".into()),
36+
profile_encoding: Some("gzip".into()),
3737
runner: Runner {
3838
name: "codspeed-runner".into(),
3939
version: "2.1.0".into(),
@@ -77,7 +77,7 @@ mod tests {
7777
hash,
7878
// Caution: when changing this value, we need to ensure that
7979
// the related backend snapshot remains the same
80-
@"f827f6a834c26d39900c0a9e2dddfaaf22956494c8db911fc06fef72878b0c70"
80+
@"7275243b4457a8fa70215c084210bea7518a3994b863e4437fa33c5ae2bd219e"
8181
);
8282
assert_json_snapshot!(upload_metadata);
8383
}

0 commit comments

Comments
 (0)