Skip to content

Commit d3c010b

Browse files
authored
Merge pull request #2201 from Kobzol/cron-job-batch
Refactor benchmark requests
2 parents 88078cd + 52a4efa commit d3c010b

File tree

9 files changed

+955
-978
lines changed

9 files changed

+955
-978
lines changed

database/schema.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,3 +258,41 @@ aid benchmark error
258258
---------- --- -----
259259
1 syn-1.0.89 Failed to compile...
260260
```
261+
262+
263+
## New benchmarking design
264+
We are currently implementing a new design for dispatching benchmarks to collector(s) and storing
265+
them in the database. It will support new use-cases, like backfilling of new benchmarks into a parent
266+
commit and primarily benchmarking with multiple collectors (and multiple hardware architectures) in
267+
parallel.
268+
269+
The tables below are a part of the new scheme.
270+
271+
### benchmark_request
272+
273+
Represents a single request for performing a benchmark collection. Each request can be one of three types:
274+
275+
* Master: benchmark a merged master commit
276+
* Release: benchmark a published stable or beta compiler toolchain
277+
* Try: benchmark a try build on a PR
278+
279+
Columns:
280+
281+
* **id** (`int`): Unique ID.
282+
* **tag** (`text`): Identifier of the compiler toolchain that should be benchmarked.
283+
* Commit SHA for master/try requests or release name (e.g. `1.80.0`) for release requests.
284+
* Can be `NULL` for try requests that were queued for a perf. run, but their compiler artifacts haven't been built yet.
285+
* **parent_sha** (`text`): Parent SHA of the benchmarked commit.
286+
* Can be `NULL` for try requests without compiler artifacts.
287+
* **commit_type** (`text NOT NULL`): One of `master`, `try` or `release`.
288+
* **pr** (`int`): Pull request number associated with the master/try commit.
289+
* `NULL` for release requests.
290+
* **created_at** (`timestamptz NOT NULL`): Datetime when the request was created.
291+
* **completed_at** (`timestamptz`): Datetime when the request was completed.
292+
* **status** (`text NOT NULL`): Current status of the benchmark request.
293+
* `waiting-for-artifacts`: A try request waiting for compiler artifacts.
294+
* `artifacts-ready`: Request that has compiler artifacts ready and can be benchmarked.
295+
* `in-progress`: Request that is currently being benchmarked.
296+
* `completed`: Completed request.
297+
* **backends** (`text NOT NULL`): Comma-separated list of codegen backends to benchmark. If empty, the default set of codegen backends will be benchmarked.
298+
* **profiles** (`text NOT NULL`): Comma-separated list of profiles to benchmark. If empty, the default set of profiles will be benchmarked.

database/src/lib.rs

Lines changed: 105 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use chrono::offset::TimeZone;
22
use chrono::{DateTime, Utc};
3-
use hashbrown::HashMap;
3+
use hashbrown::{HashMap, HashSet};
44
use intern::intern;
55
use serde::{Deserialize, Serialize};
66
use std::fmt;
@@ -309,6 +309,7 @@ impl Scenario {
309309
}
310310
}
311311

312+
use anyhow::anyhow;
312313
use std::cmp::Ordering;
313314
use std::str::FromStr;
314315

@@ -797,53 +798,64 @@ pub struct ArtifactCollection {
797798
pub end_time: DateTime<Utc>,
798799
}
799800

800-
#[derive(Debug, Clone, PartialEq)]
801+
#[derive(Debug, Copy, Clone, PartialEq)]
801802
pub enum BenchmarkRequestStatus {
802803
WaitingForArtifacts,
803804
ArtifactsReady,
804805
InProgress,
805-
Completed,
806+
Completed { completed_at: DateTime<Utc> },
806807
}
807808

809+
const BENCHMARK_REQUEST_STATUS_WAITING_FOR_ARTIFACTS_STR: &str = "waiting_for_artifacts";
810+
const BENCHMARK_REQUEST_STATUS_ARTIFACTS_READY_STR: &str = "artifacts_ready";
811+
const BENCHMARK_REQUEST_STATUS_IN_PROGRESS_STR: &str = "in_progress";
812+
const BENCHMARK_REQUEST_STATUS_COMPLETED_STR: &str = "completed";
813+
808814
impl BenchmarkRequestStatus {
809-
pub fn as_str(&self) -> &str {
815+
pub(crate) fn as_str(&self) -> &str {
810816
match self {
811-
BenchmarkRequestStatus::WaitingForArtifacts => "waiting_for_artifacts",
812-
BenchmarkRequestStatus::ArtifactsReady => "artifacts_ready",
813-
BenchmarkRequestStatus::InProgress => "in_progress",
814-
BenchmarkRequestStatus::Completed => "completed",
817+
Self::WaitingForArtifacts => BENCHMARK_REQUEST_STATUS_WAITING_FOR_ARTIFACTS_STR,
818+
Self::ArtifactsReady => BENCHMARK_REQUEST_STATUS_ARTIFACTS_READY_STR,
819+
Self::InProgress => BENCHMARK_REQUEST_STATUS_IN_PROGRESS_STR,
820+
Self::Completed { .. } => BENCHMARK_REQUEST_STATUS_COMPLETED_STR,
815821
}
816822
}
817-
}
818823

819-
impl fmt::Display for BenchmarkRequestStatus {
820-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
821-
f.write_str(self.as_str())
824+
pub(crate) fn from_str_and_completion_date(
825+
text: &str,
826+
completion_date: Option<DateTime<Utc>>,
827+
) -> anyhow::Result<Self> {
828+
match text {
829+
BENCHMARK_REQUEST_STATUS_WAITING_FOR_ARTIFACTS_STR => Ok(Self::WaitingForArtifacts),
830+
BENCHMARK_REQUEST_STATUS_ARTIFACTS_READY_STR => Ok(Self::ArtifactsReady),
831+
BENCHMARK_REQUEST_STATUS_IN_PROGRESS_STR => Ok(Self::InProgress),
832+
BENCHMARK_REQUEST_STATUS_COMPLETED_STR => Ok(Self::Completed {
833+
completed_at: completion_date.ok_or_else(|| {
834+
anyhow!("No completion date for a completed BenchmarkRequestStatus")
835+
})?,
836+
}),
837+
_ => Err(anyhow!("Unknown BenchmarkRequestStatus `{text}`")),
838+
}
822839
}
823-
}
824840

825-
impl<'a> tokio_postgres::types::FromSql<'a> for BenchmarkRequestStatus {
826-
fn from_sql(
827-
ty: &tokio_postgres::types::Type,
828-
raw: &'a [u8],
829-
) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
830-
// Decode raw bytes into &str with Postgres' own text codec
831-
let s: &str = <&str as tokio_postgres::types::FromSql>::from_sql(ty, raw)?;
832-
833-
match s {
834-
x if x == Self::WaitingForArtifacts.as_str() => Ok(Self::WaitingForArtifacts),
835-
x if x == Self::ArtifactsReady.as_str() => Ok(Self::ArtifactsReady),
836-
x if x == Self::InProgress.as_str() => Ok(Self::InProgress),
837-
x if x == Self::Completed.as_str() => Ok(Self::Completed),
838-
other => Err(format!("unknown benchmark_request_status '{other}'").into()),
841+
pub(crate) fn completed_at(&self) -> Option<DateTime<Utc>> {
842+
match self {
843+
Self::Completed { completed_at } => Some(*completed_at),
844+
_ => None,
839845
}
840846
}
847+
}
841848

842-
fn accepts(ty: &tokio_postgres::types::Type) -> bool {
843-
<&str as tokio_postgres::types::FromSql>::accepts(ty)
849+
impl fmt::Display for BenchmarkRequestStatus {
850+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
851+
f.write_str(self.as_str())
844852
}
845853
}
846854

855+
const BENCHMARK_REQUEST_TRY_STR: &str = "try";
856+
const BENCHMARK_REQUEST_MASTER_STR: &str = "master";
857+
const BENCHMARK_REQUEST_RELEASE_STR: &str = "release";
858+
847859
#[derive(Debug, Clone, PartialEq)]
848860
pub enum BenchmarkRequestType {
849861
/// A Try commit
@@ -865,86 +877,68 @@ pub enum BenchmarkRequestType {
865877
impl fmt::Display for BenchmarkRequestType {
866878
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
867879
match self {
868-
BenchmarkRequestType::Try { .. } => write!(f, "try"),
869-
BenchmarkRequestType::Master { .. } => write!(f, "master"),
870-
BenchmarkRequestType::Release { .. } => write!(f, "release"),
880+
BenchmarkRequestType::Try { .. } => write!(f, "{BENCHMARK_REQUEST_TRY_STR}"),
881+
BenchmarkRequestType::Master { .. } => write!(f, "{BENCHMARK_REQUEST_MASTER_STR}"),
882+
BenchmarkRequestType::Release { .. } => write!(f, "{BENCHMARK_REQUEST_RELEASE_STR}"),
871883
}
872884
}
873885
}
874886

875887
#[derive(Debug, Clone, PartialEq)]
876888
pub struct BenchmarkRequest {
877-
pub commit_type: BenchmarkRequestType,
878-
pub created_at: DateTime<Utc>,
879-
pub completed_at: Option<DateTime<Utc>>,
880-
pub status: BenchmarkRequestStatus,
881-
pub backends: String,
882-
pub profiles: String,
889+
commit_type: BenchmarkRequestType,
890+
created_at: DateTime<Utc>,
891+
status: BenchmarkRequestStatus,
892+
backends: String,
893+
profiles: String,
883894
}
884895

885896
impl BenchmarkRequest {
886-
pub fn create_release(
887-
tag: &str,
888-
created_at: DateTime<Utc>,
889-
status: BenchmarkRequestStatus,
890-
backends: &str,
891-
profiles: &str,
892-
) -> Self {
897+
/// Create a release benchmark request that is in the `ArtifactsReady` status.
898+
pub fn create_release(tag: &str, created_at: DateTime<Utc>) -> Self {
893899
Self {
894900
commit_type: BenchmarkRequestType::Release {
895901
tag: tag.to_string(),
896902
},
897903
created_at,
898-
completed_at: None,
899-
status,
900-
backends: backends.to_string(),
901-
profiles: profiles.to_string(),
904+
status: BenchmarkRequestStatus::ArtifactsReady,
905+
backends: String::new(),
906+
profiles: String::new(),
902907
}
903908
}
904909

905-
pub fn create_try(
906-
sha: Option<&str>,
907-
parent_sha: Option<&str>,
910+
/// Create a try request that is in the `WaitingForArtifacts` status.
911+
pub fn create_try_without_artifacts(
908912
pr: u32,
909913
created_at: DateTime<Utc>,
910-
status: BenchmarkRequestStatus,
911914
backends: &str,
912915
profiles: &str,
913916
) -> Self {
914917
Self {
915918
commit_type: BenchmarkRequestType::Try {
916919
pr,
917-
sha: sha.map(|it| it.to_string()),
918-
parent_sha: parent_sha.map(|it| it.to_string()),
920+
sha: None,
921+
parent_sha: None,
919922
},
920923
created_at,
921-
completed_at: None,
922-
status,
924+
status: BenchmarkRequestStatus::WaitingForArtifacts,
923925
backends: backends.to_string(),
924926
profiles: profiles.to_string(),
925927
}
926928
}
927929

928-
pub fn create_master(
929-
sha: &str,
930-
parent_sha: &str,
931-
pr: u32,
932-
created_at: DateTime<Utc>,
933-
status: BenchmarkRequestStatus,
934-
backends: &str,
935-
profiles: &str,
936-
) -> Self {
930+
/// Create a master benchmark request that is in the `ArtifactsReady` status.
931+
pub fn create_master(sha: &str, parent_sha: &str, pr: u32, created_at: DateTime<Utc>) -> Self {
937932
Self {
938933
commit_type: BenchmarkRequestType::Master {
939934
pr,
940935
sha: sha.to_string(),
941936
parent_sha: parent_sha.to_string(),
942937
},
943938
created_at,
944-
completed_at: None,
945-
status,
946-
backends: backends.to_string(),
947-
profiles: profiles.to_string(),
939+
status: BenchmarkRequestStatus::ArtifactsReady,
940+
backends: String::new(),
941+
profiles: String::new(),
948942
}
949943
}
950944

@@ -974,4 +968,45 @@ impl BenchmarkRequest {
974968
BenchmarkRequestType::Release { .. } => None,
975969
}
976970
}
971+
972+
pub fn status(&self) -> BenchmarkRequestStatus {
973+
self.status
974+
}
975+
976+
pub fn created_at(&self) -> DateTime<Utc> {
977+
self.created_at
978+
}
979+
980+
pub fn is_master(&self) -> bool {
981+
matches!(self.commit_type, BenchmarkRequestType::Master { .. })
982+
}
983+
984+
pub fn is_try(&self) -> bool {
985+
matches!(self.commit_type, BenchmarkRequestType::Try { .. })
986+
}
987+
988+
pub fn is_release(&self) -> bool {
989+
matches!(self.commit_type, BenchmarkRequestType::Release { .. })
990+
}
991+
}
992+
993+
/// Cached information about benchmark requests in the DB
994+
/// FIXME: only store non-try requests here
995+
pub struct BenchmarkRequestIndex {
996+
/// Tags (SHA or release name) of all known benchmark requests
997+
all: HashSet<String>,
998+
/// Tags (SHA or release name) of all benchmark requests in the completed status
999+
completed: HashSet<String>,
1000+
}
1001+
1002+
impl BenchmarkRequestIndex {
1003+
/// Do we already have a benchmark request for the passed `tag`?
1004+
pub fn contains_tag(&self, tag: &str) -> bool {
1005+
self.all.contains(tag)
1006+
}
1007+
1008+
/// Return tags of already completed benchmark requests.
1009+
pub fn completed_requests(&self) -> &HashSet<String> {
1010+
&self.completed
1011+
}
9771012
}

0 commit comments

Comments
 (0)