From 3819428302bb3a49379d0339400c91cdab58ea5c Mon Sep 17 00:00:00 2001 From: Nurseit Date: Sun, 14 Dec 2025 02:05:34 +0600 Subject: [PATCH] Store duration in database Duration type from database to struct. Extend update build Fix rebase issues Fix after PR review --- ...9a7edbfcfde9d75c3705ae826a66a444fdb2c.json | 8 ++++ ...4227fbcf7a0940a76df84d6871c698e400c4c.json | 8 ++++ ...a2d02019b90d07892917f78af8cdcfd08cb6.json} | 11 ++++- ...fe0ba1211800398b110f9cf2328e84360f29a.json | 15 ------- ...1dfd5ef45cacbc45094c2c66081efd30ae69.json} | 4 +- ...64fe0bdc27a305a089d9a3d29fafb140dfa9.json} | 11 +++-- ...01336f7766c434b6b094bc0ff8605b07f0ed8.json | 8 ++++ ...b6810bec060952afedde907f74f666e2e6c7b.json | 8 ++++ ...f70cc6ff39d6e32ef71813c98f15bd2bb7814.json | 8 ++++ ...3884efe6f48cac9e08d964ccfdc724d354bfa.json | 16 ++++++++ ...5273dda769fad346e4b8f8df96396683dff9c.json | 8 ++++ ...f6c68a93294d99f46722668553d087386469.json} | 4 +- ...60113181302_add_duration_to_build.down.sql | 2 + ...0260113181302_add_duration_to_build.up.sql | 2 + src/bors/build.rs | 2 +- src/bors/build_queue.rs | 18 +++++++-- src/bors/merge_queue.rs | 2 +- src/database/client.rs | 8 ++-- src/database/mod.rs | 40 ++++++++++++++++++- src/database/operations.rs | 31 +++++++++----- .../20260113181302_add_duration_to_build.sql | 17 ++++++++ 21 files changed, 187 insertions(+), 44 deletions(-) rename .sqlx/{query-c34bcda0288120bab553f7ca8bf0f84c02a320d4663394ed4f4ca04d73faf718.json => query-6b58cbbbd4a9c8cb85c2ac3ce232a2d02019b90d07892917f78af8cdcfd08cb6.json} (80%) delete mode 100644 .sqlx/query-712d1d8da977a960ecfe0da7cecfe0ba1211800398b110f9cf2328e84360f29a.json rename .sqlx/{query-0abf331d49412456f637308ffd57af315bfdfe13182eb0564e02ae24a1780662.json => query-9f4d783e93ab46b648d2e88bdd321dfd5ef45cacbc45094c2c66081efd30ae69.json} (85%) rename .sqlx/{query-41b428f19147925d886548386f70b9f5aaaa3f01446ec534242d42433dba62ae.json => query-b2763d7e6f9e8bede01a3623db1f64fe0bdc27a305a089d9a3d29fafb140dfa9.json} (82%) create mode 100644 .sqlx/query-f2654d5dd7232196754d25991cd3884efe6f48cac9e08d964ccfdc724d354bfa.json rename .sqlx/{query-0a04cfb0a86e79776e834b665aca52fec0229462cb548fd37a153b9470e54e95.json => query-fd8825baa794ca98c243078016ccf6c68a93294d99f46722668553d087386469.json} (84%) create mode 100644 migrations/20260113181302_add_duration_to_build.down.sql create mode 100644 migrations/20260113181302_add_duration_to_build.up.sql create mode 100644 tests/data/migrations/20260113181302_add_duration_to_build.sql diff --git a/.sqlx/query-132e2ed710158aa214e8a28d6349a7edbfcfde9d75c3705ae826a66a444fdb2c.json b/.sqlx/query-132e2ed710158aa214e8a28d6349a7edbfcfde9d75c3705ae826a66a444fdb2c.json index 5d836037..9a00443b 100644 --- a/.sqlx/query-132e2ed710158aa214e8a28d6349a7edbfcfde9d75c3705ae826a66a444fdb2c.json +++ b/.sqlx/query-132e2ed710158aa214e8a28d6349a7edbfcfde9d75c3705ae826a66a444fdb2c.json @@ -126,6 +126,10 @@ [ "kind", "Text" + ], + [ + "duration", + "Interval" ] ] } @@ -175,6 +179,10 @@ [ "kind", "Text" + ], + [ + "duration", + "Interval" ] ] } diff --git a/.sqlx/query-27babf080874701c1035b6827734227fbcf7a0940a76df84d6871c698e400c4c.json b/.sqlx/query-27babf080874701c1035b6827734227fbcf7a0940a76df84d6871c698e400c4c.json index dd6acac0..e0a31448 100644 --- a/.sqlx/query-27babf080874701c1035b6827734227fbcf7a0940a76df84d6871c698e400c4c.json +++ b/.sqlx/query-27babf080874701c1035b6827734227fbcf7a0940a76df84d6871c698e400c4c.json @@ -126,6 +126,10 @@ [ "kind", "Text" + ], + [ + "duration", + "Interval" ] ] } @@ -175,6 +179,10 @@ [ "kind", "Text" + ], + [ + "duration", + "Interval" ] ] } diff --git a/.sqlx/query-c34bcda0288120bab553f7ca8bf0f84c02a320d4663394ed4f4ca04d73faf718.json b/.sqlx/query-6b58cbbbd4a9c8cb85c2ac3ce232a2d02019b90d07892917f78af8cdcfd08cb6.json similarity index 80% rename from .sqlx/query-c34bcda0288120bab553f7ca8bf0f84c02a320d4663394ed4f4ca04d73faf718.json rename to .sqlx/query-6b58cbbbd4a9c8cb85c2ac3ce232a2d02019b90d07892917f78af8cdcfd08cb6.json index 396cabdd..fea412ac 100644 --- a/.sqlx/query-c34bcda0288120bab553f7ca8bf0f84c02a320d4663394ed4f4ca04d73faf718.json +++ b/.sqlx/query-6b58cbbbd4a9c8cb85c2ac3ce232a2d02019b90d07892917f78af8cdcfd08cb6.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\nSELECT\n id,\n repository as \"repository: GithubRepoName\",\n branch,\n kind as \"kind: BuildKind\",\n commit_sha,\n status as \"status: BuildStatus\",\n parent,\n created_at as \"created_at: DateTime\",\n check_run_id\nFROM build\nWHERE repository = $1\n AND status = $2\n", + "query": "\nSELECT\n id,\n repository as \"repository: GithubRepoName\",\n branch,\n kind as \"kind: BuildKind\",\n commit_sha,\n status as \"status: BuildStatus\",\n parent,\n created_at as \"created_at: DateTime\",\n check_run_id,\n duration as \"duration: PgDuration\"\nFROM build\nWHERE repository = $1\n AND branch = $2\n AND commit_sha = $3\n", "describe": { "columns": [ { @@ -47,10 +47,16 @@ "ordinal": 8, "name": "check_run_id", "type_info": "Int8" + }, + { + "ordinal": 9, + "name": "duration: PgDuration", + "type_info": "Interval" } ], "parameters": { "Left": [ + "Text", "Text", "Text" ] @@ -64,8 +70,9 @@ false, false, false, + true, true ] }, - "hash": "c34bcda0288120bab553f7ca8bf0f84c02a320d4663394ed4f4ca04d73faf718" + "hash": "6b58cbbbd4a9c8cb85c2ac3ce232a2d02019b90d07892917f78af8cdcfd08cb6" } diff --git a/.sqlx/query-712d1d8da977a960ecfe0da7cecfe0ba1211800398b110f9cf2328e84360f29a.json b/.sqlx/query-712d1d8da977a960ecfe0da7cecfe0ba1211800398b110f9cf2328e84360f29a.json deleted file mode 100644 index a7c43cbf..00000000 --- a/.sqlx/query-712d1d8da977a960ecfe0da7cecfe0ba1211800398b110f9cf2328e84360f29a.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE build SET status = $1 WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Int4" - ] - }, - "nullable": [] - }, - "hash": "712d1d8da977a960ecfe0da7cecfe0ba1211800398b110f9cf2328e84360f29a" -} diff --git a/.sqlx/query-0abf331d49412456f637308ffd57af315bfdfe13182eb0564e02ae24a1780662.json b/.sqlx/query-9f4d783e93ab46b648d2e88bdd321dfd5ef45cacbc45094c2c66081efd30ae69.json similarity index 85% rename from .sqlx/query-0abf331d49412456f637308ffd57af315bfdfe13182eb0564e02ae24a1780662.json rename to .sqlx/query-9f4d783e93ab46b648d2e88bdd321dfd5ef45cacbc45094c2c66081efd30ae69.json index 7e4a364f..621dd9ce 100644 --- a/.sqlx/query-0abf331d49412456f637308ffd57af315bfdfe13182eb0564e02ae24a1780662.json +++ b/.sqlx/query-9f4d783e93ab46b648d2e88bdd321dfd5ef45cacbc45094c2c66081efd30ae69.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\nSELECT\n workflow.id,\n workflow.name,\n workflow.url,\n workflow.run_id,\n workflow.type as \"workflow_type: WorkflowType\",\n workflow.status as \"status: WorkflowStatus\",\n workflow.created_at as \"created_at: DateTime\",\n (\n build.id,\n build.repository,\n build.branch,\n build.commit_sha,\n build.status,\n build.parent,\n build.created_at,\n build.check_run_id,\n build.kind\n ) AS \"build!: BuildModel\"\nFROM workflow\n LEFT JOIN build ON workflow.build_id = build.id\n", + "query": "\nSELECT\n workflow.id,\n workflow.name,\n workflow.url,\n workflow.run_id,\n workflow.type as \"workflow_type: WorkflowType\",\n workflow.status as \"status: WorkflowStatus\",\n workflow.created_at as \"created_at: DateTime\",\n (\n build.id,\n build.repository,\n build.branch,\n build.commit_sha,\n build.status,\n build.parent,\n build.created_at,\n build.check_run_id,\n build.kind,\n build.duration\n ) AS \"build!: BuildModel\"\nFROM workflow\n LEFT JOIN build ON workflow.build_id = build.id\n", "describe": { "columns": [ { @@ -58,5 +58,5 @@ null ] }, - "hash": "0abf331d49412456f637308ffd57af315bfdfe13182eb0564e02ae24a1780662" + "hash": "9f4d783e93ab46b648d2e88bdd321dfd5ef45cacbc45094c2c66081efd30ae69" } diff --git a/.sqlx/query-41b428f19147925d886548386f70b9f5aaaa3f01446ec534242d42433dba62ae.json b/.sqlx/query-b2763d7e6f9e8bede01a3623db1f64fe0bdc27a305a089d9a3d29fafb140dfa9.json similarity index 82% rename from .sqlx/query-41b428f19147925d886548386f70b9f5aaaa3f01446ec534242d42433dba62ae.json rename to .sqlx/query-b2763d7e6f9e8bede01a3623db1f64fe0bdc27a305a089d9a3d29fafb140dfa9.json index 52093a25..a9a2d1ae 100644 --- a/.sqlx/query-41b428f19147925d886548386f70b9f5aaaa3f01446ec534242d42433dba62ae.json +++ b/.sqlx/query-b2763d7e6f9e8bede01a3623db1f64fe0bdc27a305a089d9a3d29fafb140dfa9.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\nSELECT\n id,\n repository as \"repository: GithubRepoName\",\n branch,\n kind as \"kind: BuildKind\",\n commit_sha,\n status as \"status: BuildStatus\",\n parent,\n created_at as \"created_at: DateTime\",\n check_run_id\nFROM build\nWHERE repository = $1\n AND branch = $2\n AND commit_sha = $3\n", + "query": "\nSELECT\n id,\n repository as \"repository: GithubRepoName\",\n branch,\n kind as \"kind: BuildKind\",\n commit_sha,\n status as \"status: BuildStatus\",\n parent,\n created_at as \"created_at: DateTime\",\n check_run_id,\n duration as \"duration: PgDuration\"\nFROM build\nWHERE repository = $1\n AND status = $2\n", "describe": { "columns": [ { @@ -47,11 +47,15 @@ "ordinal": 8, "name": "check_run_id", "type_info": "Int8" + }, + { + "ordinal": 9, + "name": "duration: PgDuration", + "type_info": "Interval" } ], "parameters": { "Left": [ - "Text", "Text", "Text" ] @@ -65,8 +69,9 @@ false, false, false, + true, true ] }, - "hash": "41b428f19147925d886548386f70b9f5aaaa3f01446ec534242d42433dba62ae" + "hash": "b2763d7e6f9e8bede01a3623db1f64fe0bdc27a305a089d9a3d29fafb140dfa9" } diff --git a/.sqlx/query-b65196cec3ca280dd8d8169d8c801336f7766c434b6b094bc0ff8605b07f0ed8.json b/.sqlx/query-b65196cec3ca280dd8d8169d8c801336f7766c434b6b094bc0ff8605b07f0ed8.json index b225bd27..e1844c79 100644 --- a/.sqlx/query-b65196cec3ca280dd8d8169d8c801336f7766c434b6b094bc0ff8605b07f0ed8.json +++ b/.sqlx/query-b65196cec3ca280dd8d8169d8c801336f7766c434b6b094bc0ff8605b07f0ed8.json @@ -126,6 +126,10 @@ [ "kind", "Text" + ], + [ + "duration", + "Interval" ] ] } @@ -175,6 +179,10 @@ [ "kind", "Text" + ], + [ + "duration", + "Interval" ] ] } diff --git a/.sqlx/query-e10f93ba5d7bc2498244283fef0b6810bec060952afedde907f74f666e2e6c7b.json b/.sqlx/query-e10f93ba5d7bc2498244283fef0b6810bec060952afedde907f74f666e2e6c7b.json index d5ad977b..3a4bb621 100644 --- a/.sqlx/query-e10f93ba5d7bc2498244283fef0b6810bec060952afedde907f74f666e2e6c7b.json +++ b/.sqlx/query-e10f93ba5d7bc2498244283fef0b6810bec060952afedde907f74f666e2e6c7b.json @@ -126,6 +126,10 @@ [ "kind", "Text" + ], + [ + "duration", + "Interval" ] ] } @@ -175,6 +179,10 @@ [ "kind", "Text" + ], + [ + "duration", + "Interval" ] ] } diff --git a/.sqlx/query-e23431237a3585d44747b15c8fff70cc6ff39d6e32ef71813c98f15bd2bb7814.json b/.sqlx/query-e23431237a3585d44747b15c8fff70cc6ff39d6e32ef71813c98f15bd2bb7814.json index 0b0342f1..8b385545 100644 --- a/.sqlx/query-e23431237a3585d44747b15c8fff70cc6ff39d6e32ef71813c98f15bd2bb7814.json +++ b/.sqlx/query-e23431237a3585d44747b15c8fff70cc6ff39d6e32ef71813c98f15bd2bb7814.json @@ -126,6 +126,10 @@ [ "kind", "Text" + ], + [ + "duration", + "Interval" ] ] } @@ -175,6 +179,10 @@ [ "kind", "Text" + ], + [ + "duration", + "Interval" ] ] } diff --git a/.sqlx/query-f2654d5dd7232196754d25991cd3884efe6f48cac9e08d964ccfdc724d354bfa.json b/.sqlx/query-f2654d5dd7232196754d25991cd3884efe6f48cac9e08d964ccfdc724d354bfa.json new file mode 100644 index 00000000..e8abebf9 --- /dev/null +++ b/.sqlx/query-f2654d5dd7232196754d25991cd3884efe6f48cac9e08d964ccfdc724d354bfa.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "\nUPDATE build\nSET\n status = $1,\n duration = COALESCE($2, duration)\nWHERE id = $3\n", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Interval", + "Int4" + ] + }, + "nullable": [] + }, + "hash": "f2654d5dd7232196754d25991cd3884efe6f48cac9e08d964ccfdc724d354bfa" +} diff --git a/.sqlx/query-f916961671f0e795084ed7355b65273dda769fad346e4b8f8df96396683dff9c.json b/.sqlx/query-f916961671f0e795084ed7355b65273dda769fad346e4b8f8df96396683dff9c.json index ef6d04ec..d1e44509 100644 --- a/.sqlx/query-f916961671f0e795084ed7355b65273dda769fad346e4b8f8df96396683dff9c.json +++ b/.sqlx/query-f916961671f0e795084ed7355b65273dda769fad346e4b8f8df96396683dff9c.json @@ -126,6 +126,10 @@ [ "kind", "Text" + ], + [ + "duration", + "Interval" ] ] } @@ -175,6 +179,10 @@ [ "kind", "Text" + ], + [ + "duration", + "Interval" ] ] } diff --git a/.sqlx/query-0a04cfb0a86e79776e834b665aca52fec0229462cb548fd37a153b9470e54e95.json b/.sqlx/query-fd8825baa794ca98c243078016ccf6c68a93294d99f46722668553d087386469.json similarity index 84% rename from .sqlx/query-0a04cfb0a86e79776e834b665aca52fec0229462cb548fd37a153b9470e54e95.json rename to .sqlx/query-fd8825baa794ca98c243078016ccf6c68a93294d99f46722668553d087386469.json index cabeda2e..1231c570 100644 --- a/.sqlx/query-0a04cfb0a86e79776e834b665aca52fec0229462cb548fd37a153b9470e54e95.json +++ b/.sqlx/query-fd8825baa794ca98c243078016ccf6c68a93294d99f46722668553d087386469.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\nSELECT\n workflow.id,\n workflow.name,\n workflow.url,\n workflow.run_id,\n workflow.type as \"workflow_type: WorkflowType\",\n workflow.status as \"status: WorkflowStatus\",\n workflow.created_at as \"created_at: DateTime\",\n (\n build.id,\n build.repository,\n build.branch,\n build.commit_sha,\n build.status,\n build.parent,\n build.created_at,\n build.check_run_id,\n build.kind\n ) AS \"build!: BuildModel\"\nFROM workflow\n LEFT JOIN build ON workflow.build_id = build.id\nWHERE build.id = $1\n", + "query": "\nSELECT\n workflow.id,\n workflow.name,\n workflow.url,\n workflow.run_id,\n workflow.type as \"workflow_type: WorkflowType\",\n workflow.status as \"status: WorkflowStatus\",\n workflow.created_at as \"created_at: DateTime\",\n (\n build.id,\n build.repository,\n build.branch,\n build.commit_sha,\n build.status,\n build.parent,\n build.created_at,\n build.check_run_id,\n build.kind,\n build.duration\n ) AS \"build!: BuildModel\"\nFROM workflow\n LEFT JOIN build ON workflow.build_id = build.id\nWHERE build.id = $1\n", "describe": { "columns": [ { @@ -60,5 +60,5 @@ null ] }, - "hash": "0a04cfb0a86e79776e834b665aca52fec0229462cb548fd37a153b9470e54e95" + "hash": "fd8825baa794ca98c243078016ccf6c68a93294d99f46722668553d087386469" } diff --git a/migrations/20260113181302_add_duration_to_build.down.sql b/migrations/20260113181302_add_duration_to_build.down.sql new file mode 100644 index 00000000..f9219dcb --- /dev/null +++ b/migrations/20260113181302_add_duration_to_build.down.sql @@ -0,0 +1,2 @@ +-- Add down migration script here +ALTER TABLE build DROP COLUMN duration; diff --git a/migrations/20260113181302_add_duration_to_build.up.sql b/migrations/20260113181302_add_duration_to_build.up.sql new file mode 100644 index 00000000..7c1860f3 --- /dev/null +++ b/migrations/20260113181302_add_duration_to_build.up.sql @@ -0,0 +1,2 @@ +-- Add up migration script here +ALTER TABLE build ADD COLUMN duration INTERVAL; diff --git a/src/bors/build.rs b/src/bors/build.rs index 40f9e0f1..84b1cd5c 100644 --- a/src/bors/build.rs +++ b/src/bors/build.rs @@ -50,7 +50,7 @@ pub async fn cancel_build( CancelBuildConclusion::Timeout => BuildStatus::Timeouted, CancelBuildConclusion::Cancel => BuildStatus::Cancelled, }; - db.update_build_status(build, status) + db.update_build_column(build, status, None) .await .map_err(CancelBuildError::FailedToMarkBuildAsCancelled)?; diff --git a/src/bors/build_queue.rs b/src/bors/build_queue.rs index 0029293b..264864f0 100644 --- a/src/bors/build_queue.rs +++ b/src/bors/build_queue.rs @@ -95,8 +95,16 @@ pub async fn handle_build_queue_event( // First try to complete builds, and only then timeout then // Because if the bot was offline for some time, we want to first attempt to // actually finish the build, otherwise it might get instantly timeouted. - if !maybe_complete_build(&repo, db, &build, &pr, &merge_queue_tx, None) - .await? + if !maybe_complete_build( + &repo, + db, + &build, + &pr, + &merge_queue_tx, + None, + None, + ) + .await? { maybe_timeout_build(&repo, db, &build, &pr, timeout).await?; } @@ -113,7 +121,7 @@ pub async fn handle_build_queue_event( tracing::warn!( "Detected orphaned pending without a PR, marking it as timeouted: {build:?}" ); - db.update_build_status(&build, BuildStatus::Timeouted) + db.update_build_column(&build, BuildStatus::Timeouted, None) .await?; } anyhow::Ok(()) @@ -148,6 +156,7 @@ pub async fn handle_build_queue_event( &pr, &merge_queue_tx, Some(CompletionTrigger { error_context }), + event.running_time, ) .await?; } @@ -219,6 +228,7 @@ async fn maybe_complete_build( pr: &PullRequestModel, merge_queue_tx: &MergeQueueSender, completion_trigger: Option, + running_time: Option, ) -> anyhow::Result { assert_eq!( build.status, @@ -299,7 +309,7 @@ async fn maybe_complete_build( }), }; - db.update_build_status(build, status).await?; + db.update_build_column(build, status, running_time).await?; if let Some(trigger) = trigger { let pr = repo.client.get_pull_request(pr_num).await?; handle_label_trigger(repo, &pr, trigger).await?; diff --git a/src/bors/merge_queue.rs b/src/bors/merge_queue.rs index 32e4ab64..06115782 100644 --- a/src/bors/merge_queue.rs +++ b/src/bors/merge_queue.rs @@ -268,7 +268,7 @@ async fn handle_successful_build( if let Some(error_comment) = error_comment { ctx.db - .update_build_status(auto_build, BuildStatus::Failure) + .update_build_column(auto_build, BuildStatus::Failure, None) .await?; repo.client .post_comment(pr_num, error_comment, &ctx.db) diff --git a/src/database/client.rs b/src/database/client.rs index 3a7ae6ac..ad80e314 100644 --- a/src/database/client.rs +++ b/src/database/client.rs @@ -6,7 +6,7 @@ use super::operations::{ get_workflows_for_build, insert_repo_if_not_exists, record_tagged_bot_comment, set_pr_assignees, set_pr_mergeability_state, set_pr_priority, set_pr_rollup, set_pr_status, set_stale_mergeability_status_by_base_branch, unapprove_pull_request, undelegate_pull_request, - update_build_check_run_id, update_build_status, update_pr_try_build_id, update_workflow_status, + update_build_check_run_id, update_build_column, update_pr_try_build_id, update_workflow_status, upsert_pull_request, upsert_repository, }; use super::{ApprovalInfo, DelegatedPermission, MergeableState, RunId, UpsertPullRequestParams}; @@ -20,6 +20,7 @@ use crate::database::{ use crate::github::PullRequestNumber; use crate::github::{CommitSha, GithubRepoName}; use anyhow::Context; +use chrono::Duration; use itertools::Either; use sqlx::PgPool; use sqlx::postgres::PgAdvisoryLock; @@ -256,12 +257,13 @@ impl PgDbClient { get_pending_builds(&self.pool, repo).await } - pub async fn update_build_status( + pub async fn update_build_column( &self, build: &BuildModel, status: BuildStatus, + duration: Option, ) -> anyhow::Result<()> { - update_build_status(&self.pool, build.id, status).await + update_build_column(&self.pool, build.id, status, duration).await } pub async fn update_build_check_run_id( diff --git a/src/database/mod.rs b/src/database/mod.rs index 31d437fe..0e90eb57 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -10,11 +10,11 @@ use crate::{ bors::{PullRequestStatus, RollupMode}, github::{GithubRepoName, PullRequest, PullRequestNumber}, }; -use chrono::{DateTime, Utc}; +use chrono::{DateTime, Duration, Utc}; pub use client::{ExclusiveLockProof, ExclusiveOperationOutcome, PgDbClient}; pub use octocrab::models::pulls::MergeableState as OctocrabMergeableState; use sqlx::error::BoxDynError; -use sqlx::{Database, Postgres}; +use sqlx::{Database, Postgres, postgres::types::PgInterval}; mod client; pub(crate) mod operations; @@ -325,6 +325,15 @@ impl Display for BuildStatus { } } +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct PgDuration(pub Duration); + +impl PgDuration { + pub fn inner(&self) -> Duration { + self.0 + } +} + /// Represents a single (merged) commit. #[derive(Debug, PartialEq, sqlx::Type)] #[sqlx(type_name = "build")] @@ -345,6 +354,8 @@ pub struct BuildModel { pub check_run_id: Option, /// What kind of build this is (`try` or `auto`). pub kind: BuildKind, + /// Build time to finish CI workflow. + pub duration: Option, } impl sqlx::Type for BuildKind { @@ -376,6 +387,31 @@ impl sqlx::Decode<'_, sqlx::Postgres> for BuildKind { } } +impl sqlx::Type for PgDuration { + fn type_info() -> sqlx::postgres::PgTypeInfo { + >::type_info() + } +} + +impl sqlx::Encode<'_, sqlx::Postgres> for PgDuration { + fn encode_by_ref( + &self, + buf: &mut sqlx::postgres::PgArgumentBuffer, + ) -> Result { + self.0.encode_by_ref(buf) + } +} + +impl sqlx::Decode<'_, sqlx::Postgres> for PgDuration { + fn decode(value: sqlx::postgres::PgValueRef<'_>) -> Result { + let interval = PgInterval::decode(value)?; + let days_us = interval.days as i64 * 86_400_000_000; + let total_us = interval.microseconds + days_us; + + Ok(PgDuration(Duration::microseconds(total_us))) + } +} + /// Represents a pull request. #[derive(Debug)] pub struct PullRequestModel { diff --git a/src/database/operations.rs b/src/database/operations.rs index 28c6f183..81dee193 100644 --- a/src/database/operations.rs +++ b/src/database/operations.rs @@ -1,5 +1,5 @@ -use chrono::DateTime; use chrono::Utc; +use chrono::{DateTime, Duration}; use sqlx::postgres::PgExecutor; use super::ApprovalInfo; @@ -20,6 +20,7 @@ use crate::bors::RollupMode; use crate::bors::comment::CommentTag; use crate::database::BuildKind; use crate::database::BuildStatus; +use crate::database::PgDuration; use crate::database::RepoModel; use crate::database::WorkflowModel; use crate::github::CommitSha; @@ -594,7 +595,8 @@ SELECT status as "status: BuildStatus", parent, created_at as "created_at: DateTime", - check_run_id + check_run_id, + duration as "duration: PgDuration" FROM build WHERE repository = $1 AND branch = $2 @@ -628,7 +630,8 @@ SELECT status as "status: BuildStatus", parent, created_at as "created_at: DateTime", - check_run_id + check_run_id, + duration as "duration: PgDuration" FROM build WHERE repository = $1 AND status = $2 @@ -643,15 +646,23 @@ WHERE repository = $1 .await } -pub(crate) async fn update_build_status( +pub(crate) async fn update_build_column( executor: impl PgExecutor<'_>, build_id: i32, status: BuildStatus, + duration: Option, ) -> anyhow::Result<()> { - measure_db_query("update_build_status", || async { + measure_db_query("update_build_column", || async { sqlx::query!( - "UPDATE build SET status = $1 WHERE id = $2", - status as _, + r#" +UPDATE build +SET + status = $1, + duration = COALESCE($2, duration) +WHERE id = $3 +"#, + status as BuildStatus, + duration as Option<_>, build_id ) .execute(executor) @@ -789,7 +800,8 @@ SELECT build.parent, build.created_at, build.check_run_id, - build.kind + build.kind, + build.duration ) AS "build!: BuildModel" FROM workflow LEFT JOIN build ON workflow.build_id = build.id @@ -850,7 +862,8 @@ SELECT build.parent, build.created_at, build.check_run_id, - build.kind + build.kind, + build.duration ) AS "build!: BuildModel" FROM workflow LEFT JOIN build ON workflow.build_id = build.id diff --git a/tests/data/migrations/20260113181302_add_duration_to_build.sql b/tests/data/migrations/20260113181302_add_duration_to_build.sql new file mode 100644 index 00000000..1b1a90b2 --- /dev/null +++ b/tests/data/migrations/20260113181302_add_duration_to_build.sql @@ -0,0 +1,17 @@ +UPDATE build +SET + duration = '3h 8m 37s' +WHERE + id = 1; + +UPDATE build +SET + duration = '3h 11m 36s' +WHERE + id = 2; + +UPDATE build +SET + duration = '3h 12m 15s' +WHERE + id = 3;