Skip to content

Commit 7658080

Browse files
authored
Merge pull request #2226 from Kobzol/benchmark-job-queue
Add job benchmark loop
2 parents bf9a49c + 33f721f commit 7658080

File tree

9 files changed

+499
-127
lines changed

9 files changed

+499
-127
lines changed

collector/src/bin/collector.rs

Lines changed: 309 additions & 57 deletions
Large diffs are not rendered by default.

collector/src/compile/benchmark/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ impl std::fmt::Display for BenchmarkName {
114114
}
115115
}
116116

117+
#[derive(Clone)]
117118
pub struct Benchmark {
118119
pub name: BenchmarkName,
119120
pub path: PathBuf,

collector/src/compile/benchmark/profile.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,25 @@ impl Profile {
4545
impl From<Profile> for database::Profile {
4646
fn from(value: Profile) -> Self {
4747
match value {
48-
Profile::Check => database::Profile::Check,
49-
Profile::Debug => database::Profile::Debug,
50-
Profile::Doc => database::Profile::Doc,
51-
Profile::DocJson => database::Profile::DocJson,
52-
Profile::Opt => database::Profile::Opt,
53-
Profile::Clippy => database::Profile::Clippy,
48+
Profile::Check => Self::Check,
49+
Profile::Debug => Self::Debug,
50+
Profile::Doc => Self::Doc,
51+
Profile::DocJson => Self::DocJson,
52+
Profile::Opt => Self::Opt,
53+
Profile::Clippy => Self::Clippy,
54+
}
55+
}
56+
}
57+
58+
impl From<database::Profile> for Profile {
59+
fn from(value: database::Profile) -> Self {
60+
match value {
61+
database::Profile::Check => Self::Check,
62+
database::Profile::Debug => Self::Debug,
63+
database::Profile::Doc => Self::Doc,
64+
database::Profile::DocJson => Self::DocJson,
65+
database::Profile::Opt => Self::Opt,
66+
database::Profile::Clippy => Self::Clippy,
5467
}
5568
}
5669
}

collector/src/toolchain.rs

Lines changed: 128 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,93 @@ use crate::compile::benchmark::codegen_backend::CodegenBackend;
22
use crate::compile::benchmark::profile::Profile;
33
use anyhow::{anyhow, Context};
44
use log::debug;
5+
use reqwest::StatusCode;
56
use std::ffi::OsStr;
6-
use std::fs::{self, File};
7+
use std::fs;
78
use std::io::{BufReader, Read};
89
use std::path::{Path, PathBuf};
910
use std::process::Command;
1011
use std::{fmt, str};
1112
use tar::Archive;
1213
use xz2::bufread::XzDecoder;
1314

15+
pub enum SysrootDownloadError {
16+
SysrootShaNotFound,
17+
IO(anyhow::Error),
18+
}
19+
20+
impl SysrootDownloadError {
21+
pub fn as_anyhow_error(self) -> anyhow::Error {
22+
match self {
23+
SysrootDownloadError::SysrootShaNotFound => {
24+
anyhow::anyhow!("Sysroot was not found on CI")
25+
}
26+
SysrootDownloadError::IO(error) => error,
27+
}
28+
}
29+
}
30+
1431
/// Sysroot downloaded from CI.
1532
pub struct Sysroot {
16-
pub sha: String,
33+
sha: String,
1734
pub components: ToolchainComponents,
18-
pub triple: String,
19-
pub preserve: bool,
35+
triple: String,
36+
preserve: bool,
2037
}
2138

2239
impl Sysroot {
2340
pub async fn install(
41+
cache_directory: &Path,
2442
sha: String,
2543
triple: &str,
2644
backends: &[CodegenBackend],
27-
) -> anyhow::Result<Self> {
28-
let unpack_into = "cache";
29-
30-
fs::create_dir_all(unpack_into)?;
45+
) -> Result<Self, SysrootDownloadError> {
46+
let cache_directory = cache_directory.join(triple).join(&sha);
47+
fs::create_dir_all(&cache_directory).map_err(|e| SysrootDownloadError::IO(e.into()))?;
3148

3249
let download = SysrootDownload {
33-
directory: unpack_into.into(),
34-
rust_sha: sha,
50+
cache_directory: cache_directory.clone(),
51+
rust_sha: sha.clone(),
3552
triple: triple.to_owned(),
3653
};
3754

38-
download.get_and_extract(Component::Rustc).await?;
39-
download.get_and_extract(Component::Std).await?;
40-
download.get_and_extract(Component::Cargo).await?;
41-
download.get_and_extract(Component::RustSrc).await?;
42-
if backends.contains(&CodegenBackend::Cranelift) {
43-
download.get_and_extract(Component::Cranelift).await?;
55+
let requires_cranelift = backends.contains(&CodegenBackend::Cranelift);
56+
57+
let stamp = SysrootStamp::load_from_dir(&cache_directory);
58+
match stamp {
59+
Ok(stamp) => {
60+
log::debug!("Found existing stamp for {sha}/{triple}: {stamp:?}");
61+
// We should already have a complete sysroot present on disk, check if we need to
62+
// download optional components
63+
if requires_cranelift && !stamp.cranelift {
64+
download.get_and_extract(Component::Cranelift).await?;
65+
}
66+
}
67+
Err(_) => {
68+
log::debug!(
69+
"No existing stamp found for {sha}/{triple}, downloading a fresh sysroot"
70+
);
71+
72+
// There is no stamp, download everything
73+
download.get_and_extract(Component::Rustc).await?;
74+
download.get_and_extract(Component::Std).await?;
75+
download.get_and_extract(Component::Cargo).await?;
76+
download.get_and_extract(Component::RustSrc).await?;
77+
if requires_cranelift {
78+
download.get_and_extract(Component::Cranelift).await?;
79+
}
80+
}
4481
}
4582

46-
let sysroot = download.into_sysroot()?;
83+
// Update the stamp
84+
let stamp = SysrootStamp {
85+
cranelift: requires_cranelift,
86+
};
87+
stamp
88+
.store_to_dir(&cache_directory)
89+
.map_err(SysrootDownloadError::IO)?;
90+
91+
let sysroot = download.into_sysroot().map_err(SysrootDownloadError::IO)?;
4792

4893
Ok(sysroot)
4994
}
@@ -68,9 +113,32 @@ impl Drop for Sysroot {
68113
}
69114
}
70115

116+
const SYSROOT_STAMP_FILENAME: &str = ".sysroot-stamp.json";
117+
118+
/// Stores a proof on disk that we have downloaded a complete sysroot.
119+
/// It is used to avoid redownloading a sysroot if it already exists on disk.
120+
#[derive(serde::Serialize, serde::Deserialize, Debug)]
121+
struct SysrootStamp {
122+
/// Was Cranelift downloaded as a part of the sysroot?
123+
cranelift: bool,
124+
}
125+
126+
impl SysrootStamp {
127+
fn load_from_dir(dir: &Path) -> anyhow::Result<Self> {
128+
let data = std::fs::read(dir.join(SYSROOT_STAMP_FILENAME))?;
129+
let stamp: SysrootStamp = serde_json::from_slice(&data)?;
130+
Ok(stamp)
131+
}
132+
133+
fn store_to_dir(&self, dir: &Path) -> anyhow::Result<()> {
134+
let file = std::fs::File::create(dir.join(SYSROOT_STAMP_FILENAME))?;
135+
Ok(serde_json::to_writer(file, self)?)
136+
}
137+
}
138+
71139
#[derive(Debug, Clone)]
72140
struct SysrootDownload {
73-
directory: PathBuf,
141+
cache_directory: PathBuf,
74142
rust_sha: String,
75143
triple: String,
76144
}
@@ -118,7 +186,7 @@ impl Component {
118186

119187
impl SysrootDownload {
120188
fn into_sysroot(self) -> anyhow::Result<Sysroot> {
121-
let sysroot_bin_dir = self.directory.join(&self.rust_sha).join("bin");
189+
let sysroot_bin_dir = self.cache_directory.join("bin");
122190
let sysroot_bin = |name| {
123191
let path = sysroot_bin_dir.join(name);
124192
path.canonicalize().with_context(|| {
@@ -134,7 +202,7 @@ impl SysrootDownload {
134202
Some(sysroot_bin("rustdoc")?),
135203
sysroot_bin("clippy-driver").ok(),
136204
sysroot_bin("cargo")?,
137-
&self.directory.join(&self.rust_sha).join("lib"),
205+
&self.cache_directory.join("lib"),
138206
)?;
139207

140208
Ok(Sysroot {
@@ -145,54 +213,58 @@ impl SysrootDownload {
145213
})
146214
}
147215

148-
async fn get_and_extract(&self, component: Component) -> anyhow::Result<()> {
149-
let archive_path = self.directory.join(format!(
150-
"{}-{}-{}.tar.xz",
151-
self.rust_sha, self.triple, component,
152-
));
153-
if archive_path.exists() {
154-
let reader = BufReader::new(File::open(&archive_path)?);
155-
let decompress = XzDecoder::new(reader);
156-
let extract = self.extract(component, decompress);
157-
match extract {
158-
Ok(()) => return Ok(()),
159-
Err(err) => {
160-
log::warn!("extracting {} failed: {:?}", archive_path.display(), err);
161-
fs::remove_file(&archive_path).context("removing archive_path")?;
162-
}
163-
}
164-
}
165-
216+
async fn get_and_extract(&self, component: Component) -> Result<(), SysrootDownloadError> {
166217
// We usually have nightlies but we want to avoid breaking down if we
167218
// accidentally end up with a beta or stable commit.
168219
let urls = [
169220
component.url("nightly", self, &self.triple),
170221
component.url("beta", self, &self.triple),
171222
component.url("stable", self, &self.triple),
172223
];
224+
225+
// Did we see any other error than 404?
226+
let mut found_error_that_is_not_404 = false;
173227
for url in &urls {
174228
log::debug!("requesting: {}", url);
175-
let resp = reqwest::get(url).await?;
176-
log::debug!("{}", resp.status());
177-
if resp.status().is_success() {
178-
let bytes: Vec<u8> = resp.bytes().await?.into();
179-
let reader = XzDecoder::new(BufReader::new(bytes.as_slice()));
180-
match self.extract(component, reader) {
181-
Ok(()) => return Ok(()),
182-
Err(err) => {
183-
log::warn!("extracting {} failed: {:?}", url, err);
229+
let resp = reqwest::get(url)
230+
.await
231+
.map_err(|e| SysrootDownloadError::IO(e.into()))?;
232+
log::debug!("response status: {}", resp.status());
233+
234+
match resp.status() {
235+
s if s.is_success() => {
236+
let bytes: Vec<u8> = resp
237+
.bytes()
238+
.await
239+
.map_err(|e| SysrootDownloadError::IO(e.into()))?
240+
.into();
241+
let reader = XzDecoder::new(BufReader::new(bytes.as_slice()));
242+
match self.extract(component, reader) {
243+
Ok(()) => return Ok(()),
244+
Err(err) => {
245+
log::warn!("extracting {url} failed: {err:?}");
246+
found_error_that_is_not_404 = true;
247+
}
184248
}
185249
}
250+
StatusCode::NOT_FOUND => {}
251+
_ => {
252+
log::error!("response body: {}", resp.text().await.unwrap_or_default());
253+
found_error_that_is_not_404 = true
254+
}
186255
}
187256
}
188257

189-
Err(anyhow!(
190-
"unable to download sha {} triple {} module {} from any of {:?}",
191-
self.rust_sha,
192-
self.triple,
193-
component,
194-
urls
195-
))
258+
if !found_error_that_is_not_404 {
259+
// The only errors we saw were 404, so we assume that the toolchain is simply not on CI
260+
Err(SysrootDownloadError::SysrootShaNotFound)
261+
} else {
262+
Err(SysrootDownloadError::IO(anyhow!(
263+
"unable to download sha {} triple {} module {component} from any of {urls:?}",
264+
self.rust_sha,
265+
self.triple,
266+
)))
267+
}
196268
}
197269

198270
fn extract<T: Read>(&self, component: Component, reader: T) -> anyhow::Result<()> {
@@ -203,7 +275,7 @@ impl SysrootDownload {
203275
_ => component.to_string(),
204276
};
205277

206-
let unpack_into = self.directory.join(&self.rust_sha);
278+
let unpack_into = &self.cache_directory;
207279

208280
for entry in archive.entries()? {
209281
let mut entry = entry?;
@@ -617,6 +689,7 @@ fn get_lib_dir_from_rustc(rustc: &Path) -> anyhow::Result<PathBuf> {
617689
#[cfg(test)]
618690
mod tests {
619691
use super::*;
692+
use std::fs::File;
620693

621694
#[test]
622695
fn fill_libraries() {

database/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,6 +1107,10 @@ impl BenchmarkSet {
11071107
pub fn new(id: u32) -> Self {
11081108
Self(id)
11091109
}
1110+
1111+
pub fn get_id(&self) -> u32 {
1112+
self.0
1113+
}
11101114
}
11111115

11121116
/// A single unit of work generated from a benchmark request. Split by profiles

database/src/pool.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -269,13 +269,16 @@ pub trait Connection: Send + Sync {
269269
async fn mark_benchmark_job_as_completed(
270270
&self,
271271
id: u32,
272-
benchmark_job_conculsion: BenchmarkJobConclusion,
272+
conclusion: BenchmarkJobConclusion,
273273
) -> anyhow::Result<()>;
274274

275275
async fn get_status_page_data(&self) -> anyhow::Result<PartialStatusPageData>;
276276

277277
/// Get all of the configuration for all of the collectors
278278
async fn get_collector_configs(&self) -> anyhow::Result<Vec<CollectorConfig>>;
279+
280+
/// Updates the last known heartbeat of a collector to the current time.
281+
async fn update_collector_heartbeat(&self, collector_name: &str) -> anyhow::Result<()>;
279282
}
280283

281284
#[async_trait::async_trait]
@@ -1171,11 +1174,11 @@ mod tests {
11711174

11721175
assert_eq!(collector_configs[0].name(), collector_name_one);
11731176
assert_eq!(collector_configs[0].benchmark_set(), benchmark_set_one);
1174-
assert_eq!(collector_configs[0].is_active(), true);
1177+
assert!(collector_configs[0].is_active());
11751178

11761179
assert_eq!(collector_configs[1].name(), collector_name_two);
11771180
assert_eq!(collector_configs[1].benchmark_set(), benchmark_set_two);
1178-
assert_eq!(collector_configs[1].is_active(), true);
1181+
assert!(collector_configs[1].is_active());
11791182

11801183
Ok(ctx)
11811184
})

0 commit comments

Comments
 (0)