Skip to content

Commit 46c724c

Browse files
committed
Status page query and tests boilerplate
1 parent 75137f2 commit 46c724c

File tree

4 files changed

+260
-75
lines changed

4 files changed

+260
-75
lines changed

database/src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1212,3 +1212,12 @@ impl CollectorConfig {
12121212
self.date_added
12131213
}
12141214
}
1215+
1216+
/// The data that can be retrived from the database directly to populate the
1217+
/// status page
1218+
#[derive(Debug, PartialEq)]
1219+
pub struct PartialStatusPageData {
1220+
pub completed_requests: Vec<(BenchmarkRequest, u64, Vec<String>)>,
1221+
pub in_progress_jobs: Vec<BenchmarkJob>,
1222+
pub in_progress_requests: Vec<BenchmarkRequest>,
1223+
}

database/src/pool.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::selector::CompileTestCase;
22
use crate::{
33
ArtifactCollection, ArtifactId, ArtifactIdNumber, BenchmarkJob, BenchmarkJobConclusion,
44
BenchmarkRequest, BenchmarkRequestIndex, BenchmarkRequestStatus, BenchmarkSet, CodegenBackend,
5-
CollectorConfig, CompileBenchmark, Target,
5+
CollectorConfig, CompileBenchmark, PartialStatusPageData, Target,
66
};
77
use crate::{CollectionId, Index, Profile, QueuedCommit, Scenario, Step};
88
use chrono::{DateTime, Utc};
@@ -271,6 +271,8 @@ pub trait Connection: Send + Sync {
271271
id: u32,
272272
benchmark_job_conculsion: BenchmarkJobConclusion,
273273
) -> anyhow::Result<()>;
274+
275+
async fn get_status_page_data(&self) -> anyhow::Result<PartialStatusPageData>;
274276
}
275277

276278
#[async_trait::async_trait]
@@ -977,6 +979,17 @@ mod tests {
977979
let completed = db.load_benchmark_request_index().await.unwrap();
978980

979981
assert!(completed.contains_tag("sha-1"));
982+
Ok(ctx)
983+
})
984+
.await;
985+
}
986+
987+
async fn get_status_page_data() {
988+
run_postgres_test(|ctx| async {
989+
let db = ctx.db_client().connection().await;
990+
db.add_collector_config("collector-1", &Target::X86_64UnknownLinuxGnu, 1, true)
991+
.await
992+
.unwrap();
980993

981994
Ok(ctx)
982995
})

database/src/pool/postgres.rs

Lines changed: 231 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ use crate::{
44
ArtifactCollection, ArtifactId, ArtifactIdNumber, Benchmark, BenchmarkJob,
55
BenchmarkJobConclusion, BenchmarkJobStatus, BenchmarkRequest, BenchmarkRequestIndex,
66
BenchmarkRequestStatus, BenchmarkRequestType, BenchmarkSet, CodegenBackend, CollectionId,
7-
CollectorConfig, Commit, CommitType, CompileBenchmark, Date, Index, Profile, QueuedCommit,
8-
Scenario, Target, BENCHMARK_JOB_STATUS_FAILURE_STR, BENCHMARK_JOB_STATUS_IN_PROGRESS_STR,
9-
BENCHMARK_JOB_STATUS_QUEUED_STR, BENCHMARK_JOB_STATUS_SUCCESS_STR,
10-
BENCHMARK_REQUEST_MASTER_STR, BENCHMARK_REQUEST_RELEASE_STR,
7+
CollectorConfig, Commit, CommitType, CompileBenchmark, Date, Index, PartialStatusPageData,
8+
Profile, QueuedCommit, Scenario, Target, BENCHMARK_JOB_STATUS_FAILURE_STR,
9+
BENCHMARK_JOB_STATUS_IN_PROGRESS_STR, BENCHMARK_JOB_STATUS_QUEUED_STR,
10+
BENCHMARK_JOB_STATUS_SUCCESS_STR, BENCHMARK_REQUEST_MASTER_STR, BENCHMARK_REQUEST_RELEASE_STR,
1111
BENCHMARK_REQUEST_STATUS_ARTIFACTS_READY_STR, BENCHMARK_REQUEST_STATUS_COMPLETED_STR,
1212
BENCHMARK_REQUEST_STATUS_IN_PROGRESS_STR, BENCHMARK_REQUEST_STATUS_WAITING_FOR_ARTIFACTS_STR,
1313
BENCHMARK_REQUEST_TRY_STR,
@@ -21,8 +21,8 @@ use std::str::FromStr;
2121
use std::sync::Arc;
2222
use std::time::Duration;
2323
use tokio::sync::Mutex;
24-
use tokio_postgres::GenericClient;
2524
use tokio_postgres::Statement;
25+
use tokio_postgres::{GenericClient, Row};
2626

2727
pub struct Postgres(String, std::sync::Once);
2828

@@ -666,6 +666,9 @@ impl PostgresConnection {
666666
}
667667
}
668668

669+
const BENCHMARK_REQUEST_COLUMNS: &str =
670+
"tag, parent_sha, pr, commit_type, status, created_at, completed_at, backends, profiles, commit_date";
671+
669672
#[async_trait::async_trait]
670673
impl<P> Connection for P
671674
where
@@ -1602,17 +1605,7 @@ where
16021605
async fn load_pending_benchmark_requests(&self) -> anyhow::Result<Vec<BenchmarkRequest>> {
16031606
let query = format!(
16041607
r#"
1605-
SELECT
1606-
tag,
1607-
parent_sha,
1608-
pr,
1609-
commit_type,
1610-
status,
1611-
created_at,
1612-
completed_at,
1613-
backends,
1614-
profiles,
1615-
commit_date
1608+
SELECT {BENCHMARK_REQUEST_COLUMNS}
16161609
FROM benchmark_request
16171610
WHERE status IN('{BENCHMARK_REQUEST_STATUS_ARTIFACTS_READY_STR}', '{BENCHMARK_REQUEST_STATUS_IN_PROGRESS_STR}')"#
16181611
);
@@ -1625,63 +1618,7 @@ where
16251618

16261619
let requests = rows
16271620
.into_iter()
1628-
.map(|row| {
1629-
let tag = row.get::<_, Option<String>>(0);
1630-
let parent_sha = row.get::<_, Option<String>>(1);
1631-
let pr = row.get::<_, Option<i32>>(2);
1632-
let commit_type = row.get::<_, &str>(3);
1633-
let status = row.get::<_, &str>(4);
1634-
let created_at = row.get::<_, DateTime<Utc>>(5);
1635-
let completed_at = row.get::<_, Option<DateTime<Utc>>>(6);
1636-
let backends = row.get::<_, String>(7);
1637-
let profiles = row.get::<_, String>(8);
1638-
let commit_date = row.get::<_, Option<DateTime<Utc>>>(9);
1639-
1640-
let pr = pr.map(|v| v as u32);
1641-
1642-
let status =
1643-
BenchmarkRequestStatus::from_str_and_completion_date(status, completed_at)
1644-
.expect("Invalid BenchmarkRequestStatus data in the database");
1645-
1646-
match commit_type {
1647-
BENCHMARK_REQUEST_TRY_STR => BenchmarkRequest {
1648-
commit_type: BenchmarkRequestType::Try {
1649-
sha: tag,
1650-
parent_sha,
1651-
pr: pr.expect("Try commit in the DB without a PR"),
1652-
},
1653-
commit_date,
1654-
created_at,
1655-
status,
1656-
backends,
1657-
profiles,
1658-
},
1659-
BENCHMARK_REQUEST_MASTER_STR => BenchmarkRequest {
1660-
commit_type: BenchmarkRequestType::Master {
1661-
sha: tag.expect("Master commit in the DB without a SHA"),
1662-
parent_sha: parent_sha
1663-
.expect("Master commit in the DB without a parent SHA"),
1664-
pr: pr.expect("Master commit in the DB without a PR"),
1665-
},
1666-
commit_date,
1667-
created_at,
1668-
status,
1669-
backends,
1670-
profiles,
1671-
},
1672-
BENCHMARK_REQUEST_RELEASE_STR => BenchmarkRequest {
1673-
commit_type: BenchmarkRequestType::Release {
1674-
tag: tag.expect("Release commit in the DB without a SHA"),
1675-
},
1676-
commit_date,
1677-
created_at,
1678-
status,
1679-
backends,
1680-
profiles,
1681-
},
1682-
_ => panic!("Invalid `commit_type` for `BenchmarkRequest` {commit_type}",),
1683-
}
1684-
})
1621+
.map(|it| row_to_benchmark_request(&it))
16851622
.collect();
16861623
Ok(requests)
16871624
}
@@ -2017,6 +1954,227 @@ where
20171954
.context("Failed to mark benchmark job as completed")?;
20181955
Ok(())
20191956
}
1957+
1958+
async fn get_status_page_data(&self) -> anyhow::Result<PartialStatusPageData> {
1959+
let max_completed_requests = 7;
1960+
let in_progress_requests_query = format!(
1961+
"
1962+
SELECT {BENCHMARK_REQUEST_COLUMNS}
1963+
FROM benchmark_requests
1964+
WHERE status = '{BENCHMARK_REQUEST_STATUS_IN_PROGRESS_STR}'
1965+
"
1966+
);
1967+
// Gets requests along with how long the request took (latest job finish
1968+
// - earliest job start) and associated errors with the request
1969+
let completed_requests_query = format!(
1970+
"
1971+
WITH completed AS (
1972+
SELECT
1973+
{BENCHMARK_REQUEST_COLUMNS}
1974+
FROM
1975+
benchmark_request
1976+
WHERE
1977+
status = '{BENCHMARK_REQUEST_STATUS_COMPLETED_STR}'
1978+
ORDER BY
1979+
completed_at
1980+
DESC LIMIT {max_completed_requests}
1981+
), jobs AS (
1982+
SELECT
1983+
completed.tag,
1984+
job_queue.started_at,
1985+
job_queue.completed_at
1986+
FROM
1987+
job_queue
1988+
LEFT JOIN completed ON job_queue.request_tag = completed.tag
1989+
), stats AS (
1990+
SELECT
1991+
tag,
1992+
MAX(jobs.completed_at - jobs.started_at) AS duration
1993+
FROM
1994+
jobs
1995+
GROUP BY
1996+
tag
1997+
), artifacts AS (
1998+
SELECT
1999+
artifact.id,
2000+
\"name\"
2001+
FROM
2002+
artifact
2003+
LEFT JOIN completed ON artifact.name = completed.tag
2004+
), errors AS (
2005+
SELECT
2006+
artifacts.name AS tag,
2007+
ARRAY_AGG(error) AS errors
2008+
FROM
2009+
error
2010+
LEFT JOIN
2011+
artifacts ON error.aid = artifacts.id
2012+
GROUP BY
2013+
tag
2014+
)
2015+
SELECT
2016+
completed.*,
2017+
stats.*,
2018+
errors.errors AS errors
2019+
FROM
2020+
completed
2021+
LEFT JOIN stats ON stats.tag = completed.tag
2022+
LEFT JOIN errors ON errors.tag = completed.tag;
2023+
"
2024+
);
2025+
2026+
let in_progress_requests: Vec<BenchmarkRequest> = self
2027+
.conn()
2028+
.query(&in_progress_requests_query, &[])
2029+
.await?
2030+
.iter()
2031+
.map(row_to_benchmark_request)
2032+
.collect();
2033+
2034+
let completed_requests: Vec<(BenchmarkRequest, u64, Vec<String>)> = self
2035+
.conn()
2036+
.query(&completed_requests_query, &[])
2037+
.await?
2038+
.iter()
2039+
.map(|it| {
2040+
(
2041+
row_to_benchmark_request(it),
2042+
it.get::<_, i64>(9) as u64,
2043+
it.get::<_, Vec<String>>(10),
2044+
)
2045+
})
2046+
.collect();
2047+
2048+
let in_progress_tags: Vec<&str> = in_progress_requests
2049+
.iter()
2050+
.map(|it| it.tag().unwrap())
2051+
.collect();
2052+
2053+
// We don't do a status check on the jobs as we want to return all jobs,
2054+
// irrespective of status, that are attached to an inprogress
2055+
// benchmark_request
2056+
let rows = self
2057+
.conn()
2058+
.query(
2059+
"SELECT
2060+
id,
2061+
target,
2062+
backend,
2063+
profile,
2064+
request_tag,
2065+
benchmark_set,
2066+
created_at,
2067+
status,
2068+
started_at,
2069+
collector_name,
2070+
completed_at,
2071+
retry
2072+
FROM
2073+
job_queue WHERE job_queue.tag IN ($1);",
2074+
&[&in_progress_tags],
2075+
)
2076+
.await?;
2077+
2078+
let mut in_progress_jobs = vec![];
2079+
for row in rows {
2080+
let status_str = row.get::<_, &str>(7);
2081+
let status = match status_str {
2082+
BENCHMARK_JOB_STATUS_QUEUED_STR => BenchmarkJobStatus::Queued,
2083+
BENCHMARK_JOB_STATUS_IN_PROGRESS_STR => BenchmarkJobStatus::InProgress {
2084+
started_at: row.get::<_, DateTime<Utc>>(8),
2085+
collector_name: row.get::<_, String>(9),
2086+
},
2087+
BENCHMARK_JOB_STATUS_SUCCESS_STR | BENCHMARK_JOB_STATUS_FAILURE_STR => {
2088+
BenchmarkJobStatus::Completed {
2089+
started_at: row.get::<_, DateTime<Utc>>(8),
2090+
collector_name: row.get::<_, String>(9),
2091+
success: status_str == BENCHMARK_JOB_STATUS_SUCCESS_STR,
2092+
completed_at: row.get::<_, DateTime<Utc>>(10),
2093+
}
2094+
}
2095+
_ => panic!("Invalid benchmark job status: {status_str}"),
2096+
};
2097+
2098+
let job = BenchmarkJob {
2099+
id: row.get::<_, i32>(0) as u32,
2100+
target: Target::from_str(row.get::<_, &str>(1)).map_err(|e| anyhow::anyhow!(e))?,
2101+
backend: CodegenBackend::from_str(row.get::<_, &str>(2))
2102+
.map_err(|e| anyhow::anyhow!(e))?,
2103+
profile: Profile::from_str(row.get::<_, &str>(3))
2104+
.map_err(|e| anyhow::anyhow!(e))?,
2105+
request_tag: row.get::<_, String>(4),
2106+
benchmark_set: BenchmarkSet(row.get::<_, i32>(5) as u32),
2107+
created_at: row.get::<_, DateTime<Utc>>(6),
2108+
// The job is now in an in_progress state
2109+
status,
2110+
deque_counter: row.get::<_, i32>(11) as u32,
2111+
};
2112+
2113+
in_progress_jobs.push(job);
2114+
}
2115+
2116+
Ok(PartialStatusPageData {
2117+
completed_requests,
2118+
in_progress_jobs,
2119+
in_progress_requests,
2120+
})
2121+
}
2122+
}
2123+
2124+
fn row_to_benchmark_request(row: &Row) -> BenchmarkRequest {
2125+
let tag = row.get::<_, Option<String>>(0);
2126+
let parent_sha = row.get::<_, Option<String>>(1);
2127+
let pr = row.get::<_, Option<i32>>(2);
2128+
let commit_type = row.get::<_, &str>(3);
2129+
let status = row.get::<_, &str>(4);
2130+
let created_at = row.get::<_, DateTime<Utc>>(5);
2131+
let completed_at = row.get::<_, Option<DateTime<Utc>>>(6);
2132+
let backends = row.get::<_, String>(7);
2133+
let profiles = row.get::<_, String>(8);
2134+
let commit_date = row.get::<_, Option<DateTime<Utc>>>(9);
2135+
2136+
let pr = pr.map(|v| v as u32);
2137+
2138+
let status = BenchmarkRequestStatus::from_str_and_completion_date(status, completed_at)
2139+
.expect("Invalid BenchmarkRequestStatus data in the database");
2140+
2141+
match commit_type {
2142+
BENCHMARK_REQUEST_TRY_STR => BenchmarkRequest {
2143+
commit_type: BenchmarkRequestType::Try {
2144+
sha: tag,
2145+
parent_sha,
2146+
pr: pr.expect("Try commit in the DB without a PR"),
2147+
},
2148+
commit_date,
2149+
created_at,
2150+
status,
2151+
backends,
2152+
profiles,
2153+
},
2154+
BENCHMARK_REQUEST_MASTER_STR => BenchmarkRequest {
2155+
commit_type: BenchmarkRequestType::Master {
2156+
sha: tag.expect("Master commit in the DB without a SHA"),
2157+
parent_sha: parent_sha.expect("Master commit in the DB without a parent SHA"),
2158+
pr: pr.expect("Master commit in the DB without a PR"),
2159+
},
2160+
commit_date,
2161+
created_at,
2162+
status,
2163+
backends,
2164+
profiles,
2165+
},
2166+
BENCHMARK_REQUEST_RELEASE_STR => BenchmarkRequest {
2167+
commit_type: BenchmarkRequestType::Release {
2168+
tag: tag.expect("Release commit in the DB without a SHA"),
2169+
},
2170+
commit_date,
2171+
created_at,
2172+
status,
2173+
backends,
2174+
profiles,
2175+
},
2176+
_ => panic!("Invalid `commit_type` for `BenchmarkRequest` {commit_type}",),
2177+
}
20202178
}
20212179

20222180
fn parse_artifact_id(ty: &str, sha: &str, date: Option<DateTime<Utc>>) -> ArtifactId {

0 commit comments

Comments
 (0)