Skip to content

Commit 563cc7d

Browse files
authored
Merge pull request #2214 from Jamesbarford/feat/mark-request-as-complete
Feat; mark benchmark requests as complete
2 parents 603527a + c9b32be commit 563cc7d

File tree

6 files changed

+410
-64
lines changed

6 files changed

+410
-64
lines changed

database/schema.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,3 +313,34 @@ Columns:
313313
execute.
314314
* **is_active** (`boolean NOT NULL`): For controlling whether the collector is
315315
active for use. Useful for adding/removing collectors.
316+
317+
### job_queue
318+
319+
This table stores ephemeral benchmark jobs, which specifically tell the
320+
collector which benchmarks it should execute. The jobs will be kept in the
321+
table for ~30 days after being completed, so that we can quickly figure out
322+
what master parent jobs we need to backfill when handling try builds.
323+
324+
Columns:
325+
326+
* **id** (`bigint` / `serial`): Primary*key identifier for the job row;
327+
auto*increments with each new job.
328+
* **request_tag** (`text`): References the parent benchmark request that
329+
spawned this job.
330+
* **target** (`text NOT NULL`): Hardware/ISA the benchmarks must run on
331+
(e.g. AArch64, x86_64).
332+
* **backend** (`text NOT NULL`): Code generation backend the collector should
333+
test (e.g. llvm, cranelift).
334+
* **benchmark_set** (`int NOT NULL`): ID of the predefined benchmark suite to
335+
execute.
336+
* **collector_name** (`text`): Name of the collector that claimed the job
337+
(populated once the job is started).
338+
* **created_at** (`timestamptz NOT NULL`): Datetime when the job was queued.
339+
* **started_at** (`timestamptz`): Datetime when the collector actually began
340+
running the benchmarks; NULL until the job is claimed.
341+
* **completed_at** (`timestampt`): Datetime when the collector finished
342+
(successfully or otherwise); used to purge rows after ~30 days.
343+
* **status** (`text NOT NULL`): Current job state. `queued`, `in_progress`,
344+
`success`, or `failure`.
345+
* **retry** (`int NOT NULL`): Number of times the job has been re*queued after
346+
a failure; 0 on the first attempt.

database/src/lib.rs

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -846,13 +846,6 @@ impl BenchmarkRequestStatus {
846846
_ => Err(anyhow!("Unknown BenchmarkRequestStatus `{text}`")),
847847
}
848848
}
849-
850-
pub(crate) fn completed_at(&self) -> Option<DateTime<Utc>> {
851-
match self {
852-
Self::Completed { completed_at } => Some(*completed_at),
853-
_ => None,
854-
}
855-
}
856849
}
857850

858851
impl fmt::Display for BenchmarkRequestStatus {
@@ -1026,6 +1019,10 @@ impl BenchmarkRequest {
10261019
.collect::<Result<Vec<_>, _>>()
10271020
.map_err(|e| anyhow::anyhow!("Invalid backend: {e}"))
10281021
}
1022+
1023+
pub fn is_completed(&self) -> bool {
1024+
matches!(self.status, BenchmarkRequestStatus::Completed { .. })
1025+
}
10291026
}
10301027

10311028
/// Cached information about benchmark requests in the DB
@@ -1047,6 +1044,11 @@ impl BenchmarkRequestIndex {
10471044
pub fn completed_requests(&self) -> &HashSet<String> {
10481045
&self.completed
10491046
}
1047+
1048+
pub fn add_tag(&mut self, tag: &str) {
1049+
self.all.insert(tag.to_string());
1050+
self.completed.insert(tag.to_string());
1051+
}
10501052
}
10511053

10521054
#[derive(Debug, Clone, PartialEq)]
@@ -1092,7 +1094,7 @@ impl fmt::Display for BenchmarkJobStatus {
10921094
}
10931095

10941096
#[derive(Debug, Clone, PartialEq)]
1095-
pub struct BenchmarkSet(u32);
1097+
pub struct BenchmarkSet(pub u32);
10961098

10971099
/// A single unit of work generated from a benchmark request. Split by profiles
10981100
/// and backends
@@ -1102,6 +1104,7 @@ pub struct BenchmarkSet(u32);
11021104
/// they are responsible for.
11031105
#[derive(Debug, Clone, PartialEq)]
11041106
pub struct BenchmarkJob {
1107+
id: u32,
11051108
target: Target,
11061109
backend: CodegenBackend,
11071110
profile: Profile,
@@ -1113,6 +1116,10 @@ pub struct BenchmarkJob {
11131116
}
11141117

11151118
impl BenchmarkJob {
1119+
pub fn id(&self) -> u32 {
1120+
self.id
1121+
}
1122+
11161123
pub fn target(&self) -> &Target {
11171124
&self.target
11181125
}
@@ -1142,6 +1149,22 @@ impl BenchmarkJob {
11421149
}
11431150
}
11441151

1152+
/// Describes the final state of a job
1153+
#[derive(Debug, Clone, PartialEq)]
1154+
pub enum BenchmarkJobConclusion {
1155+
Failure,
1156+
Success,
1157+
}
1158+
1159+
impl BenchmarkJobConclusion {
1160+
pub fn as_str(&self) -> &str {
1161+
match self {
1162+
BenchmarkJobConclusion::Failure => BENCHMARK_JOB_STATUS_FAILURE_STR,
1163+
BenchmarkJobConclusion::Success => BENCHMARK_JOB_STATUS_SUCCESS_STR,
1164+
}
1165+
}
1166+
}
1167+
11451168
/// The configuration for a collector
11461169
#[derive(Debug, PartialEq)]
11471170
pub struct CollectorConfig {

database/src/pool.rs

Lines changed: 163 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use crate::selector::CompileTestCase;
22
use crate::{
3-
ArtifactCollection, ArtifactId, ArtifactIdNumber, BenchmarkJob, BenchmarkRequest,
4-
BenchmarkRequestIndex, BenchmarkRequestStatus, BenchmarkSet, CodegenBackend, CollectorConfig,
5-
CompileBenchmark, Target,
3+
ArtifactCollection, ArtifactId, ArtifactIdNumber, BenchmarkJob, BenchmarkJobConclusion,
4+
BenchmarkRequest, BenchmarkRequestIndex, BenchmarkRequestStatus, BenchmarkSet, CodegenBackend,
5+
CollectorConfig, CompileBenchmark, Target,
66
};
77
use crate::{CollectionId, Index, Profile, QueuedCommit, Scenario, Step};
88
use chrono::{DateTime, Utc};
@@ -246,13 +246,25 @@ pub trait Connection: Send + Sync {
246246
/// Get the confiuguration for a collector by the name of the collector
247247
async fn get_collector_config(&self, collector_name: &str) -> anyhow::Result<CollectorConfig>;
248248

249-
/// Get the confiuguration for a collector by the name of the collector
249+
/// Dequeue benchmark job
250250
async fn dequeue_benchmark_job(
251251
&self,
252252
collector_name: &str,
253253
target: &Target,
254254
benchmark_set: &BenchmarkSet,
255255
) -> anyhow::Result<Option<BenchmarkJob>>;
256+
257+
/// Try and mark the benchmark_request as completed. Will return `true` if
258+
/// it has been marked as completed else `false` meaning there was no change
259+
async fn mark_benchmark_request_as_completed(&self, tag: &str) -> anyhow::Result<bool>;
260+
261+
/// Mark the job as completed. Sets the status to 'failed' or 'success'
262+
/// depending on the enum's completed state being a success
263+
async fn mark_benchmark_job_as_completed(
264+
&self,
265+
id: u32,
266+
benchmark_job_conculsion: &BenchmarkJobConclusion,
267+
) -> anyhow::Result<()>;
256268
}
257269

258270
#[async_trait::async_trait]
@@ -388,6 +400,45 @@ mod tests {
388400
}
389401
}
390402

403+
async fn complete_request(
404+
db: &dyn Connection,
405+
request_tag: &str,
406+
collector_name: &str,
407+
benchmark_set: u32,
408+
target: &Target,
409+
) {
410+
/* Create job for the request */
411+
db.enqueue_benchmark_job(
412+
request_tag,
413+
&target,
414+
&CodegenBackend::Llvm,
415+
&Profile::Opt,
416+
benchmark_set,
417+
)
418+
.await
419+
.unwrap();
420+
421+
let job = db
422+
.dequeue_benchmark_job(collector_name, &target, &BenchmarkSet(benchmark_set))
423+
.await
424+
.unwrap()
425+
.unwrap();
426+
427+
assert_eq!(job.request_tag(), request_tag);
428+
429+
/* Mark the job as complete */
430+
db.mark_benchmark_job_as_completed(job.id(), &BenchmarkJobConclusion::Success)
431+
.await
432+
.unwrap();
433+
434+
assert_eq!(
435+
db.mark_benchmark_request_as_completed(request_tag)
436+
.await
437+
.unwrap(),
438+
true
439+
);
440+
}
441+
391442
#[tokio::test]
392443
async fn pstat_returns_empty_vector_when_empty() {
393444
run_db_test(|ctx| async {
@@ -475,28 +526,31 @@ mod tests {
475526
#[tokio::test]
476527
async fn multiple_non_completed_try_requests() {
477528
run_postgres_test(|ctx| async {
478-
let db = ctx.db_client();
479-
let db = db.connection().await;
529+
let db = ctx.db_client().connection().await;
530+
let target = &Target::X86_64UnknownLinuxGnu;
531+
let collector_name = "collector-1";
532+
let benchmark_set = 1;
480533

481-
// Completed
534+
db.add_collector_config(collector_name, &target, benchmark_set, true)
535+
.await
536+
.unwrap();
537+
538+
// Complete parent
539+
let parent = BenchmarkRequest::create_release("sha-parent-1", Utc::now());
540+
// Complete
482541
let req_a = BenchmarkRequest::create_try_without_artifacts(42, Utc::now(), "", "");
483542
// WaitingForArtifacts
484543
let req_b = BenchmarkRequest::create_try_without_artifacts(42, Utc::now(), "", "");
485544
let req_c = BenchmarkRequest::create_try_without_artifacts(42, Utc::now(), "", "");
486545

546+
db.insert_benchmark_request(&parent).await.unwrap();
487547
db.insert_benchmark_request(&req_a).await.unwrap();
488548
db.attach_shas_to_try_benchmark_request(42, "sha1", "sha-parent-1")
489549
.await
490550
.unwrap();
491551

492-
db.update_benchmark_request_status(
493-
"sha1",
494-
BenchmarkRequestStatus::Completed {
495-
completed_at: Utc::now(),
496-
},
497-
)
498-
.await
499-
.unwrap();
552+
complete_request(&*db, "sha-parent-1", collector_name, benchmark_set, &target).await;
553+
complete_request(&*db, "sha1", collector_name, benchmark_set, &target).await;
500554

501555
// This should be fine, req_a was completed
502556
db.insert_benchmark_request(&req_b).await.unwrap();
@@ -543,8 +597,15 @@ mod tests {
543597
#[tokio::test]
544598
async fn load_pending_benchmark_requests() {
545599
run_postgres_test(|ctx| async {
546-
let db = ctx.db_client();
600+
let db = ctx.db_client().connection().await;
547601
let time = chrono::DateTime::from_str("2021-09-01T00:00:00.000Z").unwrap();
602+
let target = &Target::X86_64UnknownLinuxGnu;
603+
let collector_name = "collector-1";
604+
let benchmark_set = 1;
605+
606+
db.add_collector_config(collector_name, &target, benchmark_set, true)
607+
.await
608+
.unwrap();
548609

549610
// ArtifactsReady
550611
let req_a = BenchmarkRequest::create_master("sha-1", "parent-sha-1", 42, time);
@@ -555,24 +616,17 @@ mod tests {
555616
// InProgress
556617
let req_d = BenchmarkRequest::create_master("sha-2", "parent-sha-2", 51, time);
557618
// Completed
558-
let req_e = BenchmarkRequest::create_master("sha-3", "parent-sha-3", 52, time);
619+
let req_e = BenchmarkRequest::create_release("1.79.0", time);
559620

560-
let db = db.connection().await;
561621
for &req in &[&req_a, &req_b, &req_c, &req_d, &req_e] {
562622
db.insert_benchmark_request(req).await.unwrap();
563623
}
564624

625+
complete_request(&*db, "1.79.0", collector_name, benchmark_set, &target).await;
626+
565627
db.update_benchmark_request_status("sha-2", BenchmarkRequestStatus::InProgress)
566628
.await
567629
.unwrap();
568-
db.update_benchmark_request_status(
569-
"sha-3",
570-
BenchmarkRequestStatus::Completed {
571-
completed_at: Utc::now(),
572-
},
573-
)
574-
.await
575-
.unwrap();
576630

577631
let requests = db.load_pending_benchmark_requests().await.unwrap();
578632

@@ -837,4 +891,87 @@ mod tests {
837891
})
838892
.await;
839893
}
894+
895+
#[tokio::test]
896+
async fn mark_request_as_complete_empty() {
897+
run_postgres_test(|ctx| async {
898+
let db = ctx.db_client().connection().await;
899+
let time = chrono::DateTime::from_str("2021-09-01T00:00:00.000Z").unwrap();
900+
901+
let insert_result = db
902+
.add_collector_config("collector-1", &Target::X86_64UnknownLinuxGnu, 1, true)
903+
.await;
904+
assert!(insert_result.is_ok());
905+
906+
let benchmark_request =
907+
BenchmarkRequest::create_master("sha-1", "parent-sha-1", 42, time);
908+
db.insert_benchmark_request(&benchmark_request)
909+
.await
910+
.unwrap();
911+
assert_eq!(
912+
db.mark_benchmark_request_as_completed("sha-1")
913+
.await
914+
.unwrap(),
915+
true
916+
);
917+
Ok(ctx)
918+
})
919+
.await;
920+
}
921+
922+
#[tokio::test]
923+
async fn mark_request_as_complete() {
924+
run_postgres_test(|ctx| async {
925+
let db = ctx.db_client().connection().await;
926+
let time = chrono::DateTime::from_str("2021-09-01T00:00:00.000Z").unwrap();
927+
let benchmark_set = BenchmarkSet(0u32);
928+
let tag = "sha-1";
929+
let collector_name = "collector-1";
930+
let target = Target::X86_64UnknownLinuxGnu;
931+
932+
let insert_result = db
933+
.add_collector_config(collector_name, &target, 1, true)
934+
.await;
935+
assert!(insert_result.is_ok());
936+
937+
/* Create the request */
938+
let benchmark_request = BenchmarkRequest::create_release(tag, time);
939+
db.insert_benchmark_request(&benchmark_request)
940+
.await
941+
.unwrap();
942+
943+
/* Create job for the request */
944+
db.enqueue_benchmark_job(
945+
benchmark_request.tag().unwrap(),
946+
&target,
947+
&CodegenBackend::Llvm,
948+
&Profile::Opt,
949+
benchmark_set.0,
950+
)
951+
.await
952+
.unwrap();
953+
954+
let job = db
955+
.dequeue_benchmark_job(collector_name, &target, &benchmark_set)
956+
.await
957+
.unwrap()
958+
.unwrap();
959+
960+
assert_eq!(job.request_tag(), benchmark_request.tag().unwrap());
961+
962+
/* Mark the job as complete */
963+
db.mark_benchmark_job_as_completed(job.id(), &BenchmarkJobConclusion::Success)
964+
.await
965+
.unwrap();
966+
967+
db.mark_benchmark_request_as_completed(tag).await.unwrap();
968+
969+
let completed = db.load_benchmark_request_index().await.unwrap();
970+
971+
assert!(completed.contains_tag("sha-1"));
972+
973+
Ok(ctx)
974+
})
975+
.await;
976+
}
840977
}

0 commit comments

Comments
 (0)