Skip to content

Commit 04b6f5d

Browse files
authored
Merge pull request #481 from Kobzol/refresh-ext
Make refresh checks more robust
2 parents df442c4 + 0c8bf96 commit 04b6f5d

7 files changed

+69
-58
lines changed

.sqlx/query-0f0acf10f909aa60beaedde032c1770566948a06c2b2e1f07806e948d83803e1.json

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.sqlx/query-3cc3661fb1e1ff945e415f80637e7d7614ffac5358b73ff039220ffed15c4fce.json

Lines changed: 0 additions & 15 deletions
This file was deleted.

src/bors/handlers/refresh.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use chrono::{DateTime, Utc};
66
use octocrab::params::checks::CheckRunConclusion;
77
use std::collections::BTreeMap;
88

9-
use crate::bors::PullRequestStatus;
109
use crate::bors::RepositoryState;
1110
use crate::bors::comment::build_timed_out_comment;
1211
use crate::bors::handlers::workflow::{CancelBuildError, cancel_build};
@@ -177,12 +176,18 @@ pub async fn sync_pull_requests_state(
177176
// but bors does the merging so it should not happen.
178177
for pr_num in nonclosed_db_prs_num.keys() {
179178
if !nonclosed_gh_prs_num.contains_key(pr_num) {
179+
let gh_pr = repo
180+
.client
181+
.get_pull_request(*pr_num)
182+
.await
183+
.with_context(|| {
184+
anyhow::anyhow!("Cannot fetch PR {}#{pr_num}", repo.repository())
185+
})?;
180186
tracing::debug!(
181-
"PR {} not found in open/draft prs in GitHub, closing it in DB",
182-
pr_num
187+
"PR {pr_num} not found in open/draft prs in GitHub, marking it as {} in DB",
188+
gh_pr.status,
183189
);
184-
db.set_pr_status(repo_name, *pr_num, PullRequestStatus::Closed)
185-
.await?;
190+
db.set_pr_status(repo_name, *pr_num, gh_pr.status).await?;
186191
}
187192
}
188193

src/bors/merge_queue.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ async fn handle_start_auto_build(
230230
tracing::debug!("Failed to start auto build for PR {pr_num} due to merge conflict");
231231

232232
ctx.db
233-
.update_pr_mergeable_state(pr, MergeableState::Unknown)
233+
.set_pr_mergeable_state(repo.repository(), pr.number, MergeableState::Unknown)
234234
.await?;
235235
repo.client
236236
.post_comment(pr.number, merge_conflict_comment(&gh_pr.head.name))

src/bors/mergeability_queue.rs

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ enum QueueMessage {
5353
Item(MergeabilityQueueItem),
5454
}
5555

56-
#[derive(PartialEq, Eq)]
56+
#[derive(PartialEq, Eq, Debug)]
5757
struct Item {
5858
/// When to process item (None = immediate is ordered before Some).
5959
expiration: Option<Instant>,
@@ -152,6 +152,19 @@ impl MergeabilityQueueSender {
152152
fn insert_item(&self, item: MergeabilityQueueItem, expiration: Option<Instant>) {
153153
let mut queue = self.inner.queue.lock().unwrap();
154154

155+
// Make sure that we don't ever put the same pull request twice into the queue
156+
// This might seem a bit inefficient, but linearly iterating through e.g. 1000 PRs should
157+
// be fine.
158+
// We could maybe reset the attempt counter of the PR if it's "refreshed" from the outside,
159+
// but that would require using e.g. Cell to mutate the attempt counter through &, which
160+
// doesn't seem necessary at the moment.
161+
if queue.iter().any(|entry| match &entry.0.inner {
162+
QueueMessage::Shutdown => false,
163+
QueueMessage::Item(i) => i.pull_request == item.pull_request,
164+
}) {
165+
return;
166+
}
167+
155168
// Notify when:
156169
// 1. The current item expires sooner than the head of the queue
157170
let has_earlier_expiration =
@@ -297,19 +310,12 @@ pub async fn check_mergeability(
297310
}
298311

299312
// We know the mergeability status, so update it in the DB
300-
let pr_model = match ctx
301-
.db
302-
.get_pull_request(&pull_request.repo, pull_request.pr_number)
303-
.await?
304-
{
305-
Some(model) => model,
306-
None => {
307-
return Err(anyhow::anyhow!("PR not found in database: {pull_request}"));
308-
}
309-
};
310-
311313
ctx.db
312-
.update_pr_mergeable_state(&pr_model, new_mergeable_state.clone().into())
314+
.set_pr_mergeable_state(
315+
repo_state.repository(),
316+
fetched_pr.number,
317+
new_mergeable_state.into(),
318+
)
313319
.await?;
314320

315321
Ok(())
@@ -318,11 +324,10 @@ pub async fn check_mergeability(
318324
#[cfg(test)]
319325
mod tests {
320326
use crate::bors::mergeability_queue::{
321-
MergeabilityQueueItem, QueuedPullRequest, create_mergeability_queue,
327+
BASE_DELAY, MergeabilityQueueItem, QueuedPullRequest, create_mergeability_queue,
322328
};
323329
use crate::github::PullRequestNumber;
324330
use crate::tests::default_repo_name;
325-
use std::time::Duration;
326331

327332
#[tokio::test]
328333
async fn order_by_pr_number() {
@@ -342,12 +347,10 @@ mod tests {
342347
#[tokio::test]
343348
async fn immediate_before_delayed() {
344349
let (tx, rx) = create_mergeability_queue();
345-
tx.enqueue_retry_later(item(5, 1));
346-
tx.enqueue_pr(default_repo_name(), 1u64.into());
347-
tokio::time::sleep(Duration::from_millis(100)).await;
348350
tx.enqueue_retry_later(item(10, 1));
351+
tx.enqueue_pr(default_repo_name(), 2u64.into());
349352

350-
for expected in [1, 5, 10] {
353+
for expected in [2, 10] {
351354
assert_eq!(
352355
rx.dequeue().await.unwrap().0.pull_request.pr_number.0,
353356
expected
@@ -356,17 +359,16 @@ mod tests {
356359
}
357360

358361
#[tokio::test]
359-
async fn duplicated_pr() {
362+
async fn deduplicate_duplicated_pr() {
360363
let (tx, rx) = create_mergeability_queue();
361-
// Handle duplicated PRs, still keep ordering by time
362-
tx.enqueue_retry_later(item(5, 1)); // this attempt will be bumped to 2
364+
// Make sure that we don't handle the same PR multiple times
365+
tx.enqueue_retry_later(item(5, 1));
366+
tx.enqueue_pr(default_repo_name(), 5u64.into());
363367
tx.enqueue_pr(default_repo_name(), 5u64.into());
364368

365-
for (pr, attempt) in [(5, 1), (5, 2)] {
366-
let item = rx.dequeue().await.unwrap().0;
367-
assert_eq!(item.pull_request.pr_number.0, pr);
368-
assert_eq!(item.attempt, attempt);
369-
}
369+
rx.dequeue().await.unwrap();
370+
let res = tokio::time::timeout(BASE_DELAY * 2, rx.dequeue()).await;
371+
assert!(res.is_err());
370372
}
371373

372374
fn item(pr_number: u64, attempt: u32) -> MergeabilityQueueItem {

src/database/client.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ use super::operations::{
1616
get_nonclosed_pull_requests, get_pending_builds, get_prs_with_unknown_mergeability_state,
1717
get_pull_request, get_repository, get_repository_by_name, get_tagged_bot_comments,
1818
get_workflow_urls_for_build, get_workflows_for_build, insert_repo_if_not_exists,
19-
record_tagged_bot_comment, set_pr_assignees, set_pr_priority, set_pr_rollup, set_pr_status,
20-
unapprove_pull_request, undelegate_pull_request, update_build_check_run_id,
21-
update_build_status, update_mergeable_states_by_base_branch, update_pr_mergeability_state,
19+
record_tagged_bot_comment, set_pr_assignees, set_pr_mergeability_state, set_pr_priority,
20+
set_pr_rollup, set_pr_status, unapprove_pull_request, undelegate_pull_request,
21+
update_build_check_run_id, update_build_status, update_mergeable_states_by_base_branch,
2222
update_pr_try_build_id, update_workflow_status, upsert_pull_request, upsert_repository,
2323
};
2424
use super::{ApprovalInfo, DelegatedPermission, MergeableState, RunId, UpsertPullRequestParams};
@@ -69,12 +69,13 @@ impl PgDbClient {
6969
undelegate_pull_request(&self.pool, pr.id).await
7070
}
7171

72-
pub async fn update_pr_mergeable_state(
72+
pub async fn set_pr_mergeable_state(
7373
&self,
74-
pr: &PullRequestModel,
74+
repo: &GithubRepoName,
75+
pr_number: PullRequestNumber,
7576
mergeable_state: MergeableState,
7677
) -> anyhow::Result<()> {
77-
update_pr_mergeability_state(&self.pool, pr.id, mergeable_state).await
78+
set_pr_mergeability_state(&self.pool, repo, pr_number, mergeable_state).await
7879
}
7980

8081
pub async fn set_pr_assignees(

src/database/operations.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -235,16 +235,18 @@ pub(crate) async fn get_nonclosed_pull_requests(
235235
.await
236236
}
237237

238-
pub(crate) async fn update_pr_mergeability_state(
238+
pub(crate) async fn set_pr_mergeability_state(
239239
executor: impl PgExecutor<'_>,
240-
pr_id: i32,
240+
repo: &GithubRepoName,
241+
pr_number: PullRequestNumber,
241242
mergeability_state: MergeableState,
242243
) -> anyhow::Result<()> {
243244
measure_db_query("update_pr_mergeability_state", || async {
244245
sqlx::query!(
245-
"UPDATE pull_request SET mergeable_state = $1 WHERE id = $2",
246+
"UPDATE pull_request SET mergeable_state = $3 WHERE repository = $1 AND number = $2",
247+
repo as &GithubRepoName,
248+
pr_number.0 as i32,
246249
mergeability_state as _,
247-
pr_id
248250
)
249251
.execute(executor)
250252
.await?;

0 commit comments

Comments
 (0)