Skip to content

Commit 3f720df

Browse files
committed
Status page query and tests boilerplate
1 parent 563cc7d commit 3f720df

File tree

4 files changed

+256
-70
lines changed

4 files changed

+256
-70
lines changed

database/src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,3 +1201,12 @@ impl CollectorConfig {
12011201
self.date_added
12021202
}
12031203
}
1204+
1205+
/// The data that can be retrived from the database directly to populate the
1206+
/// status page
1207+
#[derive(Debug, PartialEq)]
1208+
pub struct PartialStatusPageData {
1209+
pub completed_requests: Vec<(BenchmarkRequest, u64, Vec<String>)>,
1210+
pub in_progress_jobs: Vec<BenchmarkJob>,
1211+
pub in_progress_requests: Vec<BenchmarkRequest>,
1212+
}

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};
@@ -265,6 +265,8 @@ pub trait Connection: Send + Sync {
265265
id: u32,
266266
benchmark_job_conculsion: &BenchmarkJobConclusion,
267267
) -> anyhow::Result<()>;
268+
269+
async fn get_status_page_data(&self) -> anyhow::Result<PartialStatusPageData>;
268270
}
269271

270272
#[async_trait::async_trait]
@@ -969,6 +971,17 @@ mod tests {
969971
let completed = db.load_benchmark_request_index().await.unwrap();
970972

971973
assert!(completed.contains_tag("sha-1"));
974+
Ok(ctx)
975+
})
976+
.await;
977+
}
978+
979+
async fn get_status_page_data() {
980+
run_postgres_test(|ctx| async {
981+
let db = ctx.db_client().connection().await;
982+
db.add_collector_config("collector-1", &Target::X86_64UnknownLinuxGnu, 1, true)
983+
.await
984+
.unwrap();
972985

973986
Ok(ctx)
974987
})

database/src/pool/postgres.rs

Lines changed: 227 additions & 68 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

@@ -663,6 +663,9 @@ impl PostgresConnection {
663663
}
664664
}
665665

666+
const BENCHMARK_REQUEST_COLUMNS: &str =
667+
"tag, parent_sha, pr, commit_type, status, created_at, completed_at, backends, profiles";
668+
666669
#[async_trait::async_trait]
667670
impl<P> Connection for P
668671
where
@@ -1594,16 +1597,7 @@ where
15941597
async fn load_pending_benchmark_requests(&self) -> anyhow::Result<Vec<BenchmarkRequest>> {
15951598
let query = format!(
15961599
r#"
1597-
SELECT
1598-
tag,
1599-
parent_sha,
1600-
pr,
1601-
commit_type,
1602-
status,
1603-
created_at,
1604-
completed_at,
1605-
backends,
1606-
profiles
1600+
SELECT {BENCHMARK_REQUEST_COLUMNS}
16071601
FROM benchmark_request
16081602
WHERE status IN('{BENCHMARK_REQUEST_STATUS_ARTIFACTS_READY_STR}', '{BENCHMARK_REQUEST_STATUS_IN_PROGRESS_STR}')"#
16091603
);
@@ -1616,59 +1610,7 @@ where
16161610

16171611
let requests = rows
16181612
.into_iter()
1619-
.map(|row| {
1620-
let tag = row.get::<_, Option<String>>(0);
1621-
let parent_sha = row.get::<_, Option<String>>(1);
1622-
let pr = row.get::<_, Option<i32>>(2);
1623-
let commit_type = row.get::<_, &str>(3);
1624-
let status = row.get::<_, &str>(4);
1625-
let created_at = row.get::<_, DateTime<Utc>>(5);
1626-
let completed_at = row.get::<_, Option<DateTime<Utc>>>(6);
1627-
let backends = row.get::<_, String>(7);
1628-
let profiles = row.get::<_, String>(8);
1629-
1630-
let pr = pr.map(|v| v as u32);
1631-
1632-
let status =
1633-
BenchmarkRequestStatus::from_str_and_completion_date(status, completed_at)
1634-
.expect("Invalid BenchmarkRequestStatus data in the database");
1635-
1636-
match commit_type {
1637-
BENCHMARK_REQUEST_TRY_STR => BenchmarkRequest {
1638-
commit_type: BenchmarkRequestType::Try {
1639-
sha: tag,
1640-
parent_sha,
1641-
pr: pr.expect("Try commit in the DB without a PR"),
1642-
},
1643-
created_at,
1644-
status,
1645-
backends,
1646-
profiles,
1647-
},
1648-
BENCHMARK_REQUEST_MASTER_STR => BenchmarkRequest {
1649-
commit_type: BenchmarkRequestType::Master {
1650-
sha: tag.expect("Master commit in the DB without a SHA"),
1651-
parent_sha: parent_sha
1652-
.expect("Master commit in the DB without a parent SHA"),
1653-
pr: pr.expect("Master commit in the DB without a PR"),
1654-
},
1655-
created_at,
1656-
status,
1657-
backends,
1658-
profiles,
1659-
},
1660-
BENCHMARK_REQUEST_RELEASE_STR => BenchmarkRequest {
1661-
commit_type: BenchmarkRequestType::Release {
1662-
tag: tag.expect("Release commit in the DB without a SHA"),
1663-
},
1664-
created_at,
1665-
status,
1666-
backends,
1667-
profiles,
1668-
},
1669-
_ => panic!("Invalid `commit_type` for `BenchmarkRequest` {commit_type}",),
1670-
}
1671-
})
1613+
.map(|it| row_to_benchmark_request(&it))
16721614
.collect();
16731615
Ok(requests)
16741616
}
@@ -1970,6 +1912,223 @@ where
19701912
.context("Failed to mark benchmark job as completed")?;
19711913
Ok(())
19721914
}
1915+
1916+
async fn get_status_page_data(&self) -> anyhow::Result<PartialStatusPageData> {
1917+
let max_completed_requests = 7;
1918+
let in_progress_requests_query = format!(
1919+
"
1920+
SELECT {BENCHMARK_REQUEST_COLUMNS}
1921+
FROM benchmark_requests
1922+
WHERE status = '{BENCHMARK_REQUEST_STATUS_IN_PROGRESS_STR}'
1923+
"
1924+
);
1925+
// Gets requests along with how long the request took (latest job finish
1926+
// - earliest job start) and associated errors with the request
1927+
let completed_requests_query = format!(
1928+
"
1929+
WITH completed AS (
1930+
SELECT
1931+
{BENCHMARK_REQUEST_COLUMNS}
1932+
FROM
1933+
benchmark_request
1934+
WHERE
1935+
status = '{BENCHMARK_REQUEST_STATUS_COMPLETED_STR}'
1936+
ORDER BY
1937+
completed_at
1938+
DESC LIMIT {max_completed_requests}
1939+
), jobs AS (
1940+
SELECT
1941+
completed.tag,
1942+
job_queue.started_at,
1943+
job_queue.completed_at
1944+
FROM
1945+
job_queue
1946+
LEFT JOIN completed ON job_queue.request_tag = completed.tag
1947+
), stats AS (
1948+
SELECT
1949+
tag,
1950+
MAX(jobs.completed_at - jobs.started_at) AS duration
1951+
FROM
1952+
jobs
1953+
GROUP BY
1954+
tag
1955+
), artifacts AS (
1956+
SELECT
1957+
artifact.id,
1958+
\"name\"
1959+
FROM
1960+
artifact
1961+
LEFT JOIN completed ON artifact.name = completed.tag
1962+
), errors AS (
1963+
SELECT
1964+
artifacts.name AS tag,
1965+
ARRAY_AGG(error) AS errors
1966+
FROM
1967+
error
1968+
LEFT JOIN
1969+
artifacts ON error.aid = artifacts.id
1970+
GROUP BY
1971+
tag
1972+
)
1973+
SELECT
1974+
completed.*,
1975+
stats.*,
1976+
errors.errors AS errors
1977+
FROM
1978+
completed
1979+
LEFT JOIN stats ON stats.tag = completed.tag
1980+
LEFT JOIN errors ON errors.tag = completed.tag;
1981+
"
1982+
);
1983+
1984+
let in_progress_requests: Vec<BenchmarkRequest> = self
1985+
.conn()
1986+
.query(&in_progress_requests_query, &[])
1987+
.await?
1988+
.iter()
1989+
.map(row_to_benchmark_request)
1990+
.collect();
1991+
1992+
let completed_requests: Vec<(BenchmarkRequest, u64, Vec<String>)> = self
1993+
.conn()
1994+
.query(&completed_requests_query, &[])
1995+
.await?
1996+
.iter()
1997+
.map(|it| {
1998+
(
1999+
row_to_benchmark_request(it),
2000+
it.get::<_, i64>(9) as u64,
2001+
it.get::<_, Vec<String>>(10),
2002+
)
2003+
})
2004+
.collect();
2005+
2006+
let in_progress_tags: Vec<&str> = in_progress_requests
2007+
.iter()
2008+
.map(|it| it.tag().unwrap())
2009+
.collect();
2010+
2011+
// We don't do a status check on the jobs as we want to return all jobs,
2012+
// irrespective of status, that are attached to an inprogress
2013+
// benchmark_request
2014+
let rows = self
2015+
.conn()
2016+
.query(
2017+
"SELECT
2018+
id,
2019+
target,
2020+
backend,
2021+
profile,
2022+
request_tag,
2023+
benchmark_set,
2024+
created_at,
2025+
status,
2026+
started_at,
2027+
collector_name,
2028+
completed_at,
2029+
retry
2030+
FROM
2031+
job_queue WHERE job_queue.tag IN ($1);",
2032+
&[&in_progress_tags],
2033+
)
2034+
.await?;
2035+
2036+
let mut in_progress_jobs = vec![];
2037+
for row in rows {
2038+
let status_str = row.get::<_, &str>(7);
2039+
let status = match status_str {
2040+
BENCHMARK_JOB_STATUS_QUEUED_STR => BenchmarkJobStatus::Queued,
2041+
BENCHMARK_JOB_STATUS_IN_PROGRESS_STR => BenchmarkJobStatus::InProgress {
2042+
started_at: row.get::<_, DateTime<Utc>>(8),
2043+
collector_name: row.get::<_, String>(9),
2044+
},
2045+
BENCHMARK_JOB_STATUS_SUCCESS_STR | BENCHMARK_JOB_STATUS_FAILURE_STR => {
2046+
BenchmarkJobStatus::Completed {
2047+
started_at: row.get::<_, DateTime<Utc>>(8),
2048+
collector_name: row.get::<_, String>(9),
2049+
success: status_str == BENCHMARK_JOB_STATUS_SUCCESS_STR,
2050+
completed_at: row.get::<_, DateTime<Utc>>(10),
2051+
}
2052+
}
2053+
_ => panic!("Invalid benchmark job status: {status_str}"),
2054+
};
2055+
2056+
let job = BenchmarkJob {
2057+
id: row.get::<_, i32>(0) as u32,
2058+
target: Target::from_str(row.get::<_, &str>(1)).map_err(|e| anyhow::anyhow!(e))?,
2059+
backend: CodegenBackend::from_str(row.get::<_, &str>(2))
2060+
.map_err(|e| anyhow::anyhow!(e))?,
2061+
profile: Profile::from_str(row.get::<_, &str>(3))
2062+
.map_err(|e| anyhow::anyhow!(e))?,
2063+
request_tag: row.get::<_, String>(4),
2064+
benchmark_set: BenchmarkSet(row.get::<_, i32>(5) as u32),
2065+
created_at: row.get::<_, DateTime<Utc>>(6),
2066+
// The job is now in an in_progress state
2067+
status,
2068+
retry: row.get::<_, i32>(11) as u32,
2069+
};
2070+
2071+
in_progress_jobs.push(job);
2072+
}
2073+
2074+
Ok(PartialStatusPageData {
2075+
completed_requests,
2076+
in_progress_jobs,
2077+
in_progress_requests,
2078+
})
2079+
}
2080+
}
2081+
2082+
fn row_to_benchmark_request(row: &Row) -> BenchmarkRequest {
2083+
let tag = row.get::<_, Option<String>>(0);
2084+
let parent_sha = row.get::<_, Option<String>>(1);
2085+
let pr = row.get::<_, Option<i32>>(2);
2086+
let commit_type = row.get::<_, &str>(3);
2087+
let status = row.get::<_, &str>(4);
2088+
let created_at = row.get::<_, DateTime<Utc>>(5);
2089+
let completed_at = row.get::<_, Option<DateTime<Utc>>>(6);
2090+
let backends = row.get::<_, String>(7);
2091+
let profiles = row.get::<_, String>(8);
2092+
2093+
let pr = pr.map(|v| v as u32);
2094+
2095+
let status = BenchmarkRequestStatus::from_str_and_completion_date(status, completed_at)
2096+
.expect("Invalid BenchmarkRequestStatus data in the database");
2097+
2098+
match commit_type {
2099+
BENCHMARK_REQUEST_TRY_STR => BenchmarkRequest {
2100+
commit_type: BenchmarkRequestType::Try {
2101+
sha: tag,
2102+
parent_sha,
2103+
pr: pr.expect("Try commit in the DB without a PR"),
2104+
},
2105+
created_at,
2106+
status,
2107+
backends,
2108+
profiles,
2109+
},
2110+
BENCHMARK_REQUEST_MASTER_STR => BenchmarkRequest {
2111+
commit_type: BenchmarkRequestType::Master {
2112+
sha: tag.expect("Master commit in the DB without a SHA"),
2113+
parent_sha: parent_sha.expect("Master commit in the DB without a parent SHA"),
2114+
pr: pr.expect("Master commit in the DB without a PR"),
2115+
},
2116+
created_at,
2117+
status,
2118+
backends,
2119+
profiles,
2120+
},
2121+
BENCHMARK_REQUEST_RELEASE_STR => BenchmarkRequest {
2122+
commit_type: BenchmarkRequestType::Release {
2123+
tag: tag.expect("Release commit in the DB without a SHA"),
2124+
},
2125+
created_at,
2126+
status,
2127+
backends,
2128+
profiles,
2129+
},
2130+
_ => panic!("Invalid `commit_type` for `BenchmarkRequest` {commit_type}",),
2131+
}
19732132
}
19742133

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

0 commit comments

Comments
 (0)