Skip to content

Commit 036f1ed

Browse files
committed
[GITEA] avoid superfluous synchronized pull_request run when opening a PR (go-gitea#2236)
* Split TestPullRequest out of AddTestPullRequestTask * Before scheduling the task, AddTestPullRequestTask stores the max index of the repository * When the task runs, it does not take into account pull requests that have an index higher than the recorded max index When AddTestPullRequestTask is called with isSync == true, it is the direct consequence of a new commit being pushed. Forgejo knows nothing of this new commit yet. If a PR is created later and its head references the new commit, it will have an index that is higher and must not be taken into account. It would be acting and triggering a notification for a PR based on an event that happened before it existed. Refs: https://codeberg.org/forgejo/forgejo/issues/2009 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/2236 Reviewed-by: Gusted <[email protected]> Co-authored-by: Earl Warren <[email protected]> Co-committed-by: Earl Warren <[email protected]> (cherry picked from commit b3be895a30b32bfae4acfa32db54406e1dd1dc21)
1 parent c335d07 commit 036f1ed

File tree

9 files changed

+331
-89
lines changed

9 files changed

+331
-89
lines changed

models/issues/issue_index.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ import (
99
"code.gitea.io/gitea/models/db"
1010
)
1111

12+
func GetMaxIssueIndexForRepo(ctx context.Context, repoID int64) (int64, error) {
13+
var max int64
14+
if _, err := db.GetEngine(ctx).Select("MAX(`index`)").Table("issue").Where("repo_id=?", repoID).Get(&max); err != nil {
15+
return 0, err
16+
}
17+
return max, nil
18+
}
19+
1220
// RecalculateIssueIndexForRepo create issue_index for repo if not exist and
1321
// update it based on highest index of existing issues assigned to a repo
1422
func RecalculateIssueIndexForRepo(ctx context.Context, repoID int64) error {
@@ -18,8 +26,8 @@ func RecalculateIssueIndexForRepo(ctx context.Context, repoID int64) error {
1826
}
1927
defer committer.Close()
2028

21-
var max int64
22-
if _, err = db.GetEngine(ctx).Select(" MAX(`index`)").Table("issue").Where("repo_id=?", repoID).Get(&max); err != nil {
29+
max, err := GetMaxIssueIndexForRepo(ctx, repoID)
30+
if err != nil {
2331
return err
2432
}
2533

models/issues/issue_index_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright 2024 The Forgejo Authors
2+
// SPDX-License-Identifier: MIT
3+
4+
package issues_test
5+
6+
import (
7+
"testing"
8+
9+
"code.gitea.io/gitea/models/db"
10+
issues_model "code.gitea.io/gitea/models/issues"
11+
repo_model "code.gitea.io/gitea/models/repo"
12+
"code.gitea.io/gitea/models/unittest"
13+
14+
"github.com/stretchr/testify/assert"
15+
)
16+
17+
func TestGetMaxIssueIndexForRepo(t *testing.T) {
18+
assert.NoError(t, unittest.PrepareTestDatabase())
19+
20+
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
21+
22+
maxPR, err := issues_model.GetMaxIssueIndexForRepo(db.DefaultContext, repo.ID)
23+
assert.NoError(t, err)
24+
25+
issue := testCreateIssue(t, repo.ID, repo.OwnerID, "title1", "content1", false)
26+
assert.Greater(t, issue.Index, maxPR)
27+
28+
maxPR, err = issues_model.GetMaxIssueIndexForRepo(db.DefaultContext, repo.ID)
29+
assert.NoError(t, err)
30+
31+
pull := testCreateIssue(t, repo.ID, repo.OwnerID, "title2", "content2", true)
32+
assert.Greater(t, pull.Index, maxPR)
33+
34+
maxPR, err = issues_model.GetMaxIssueIndexForRepo(db.DefaultContext, repo.ID)
35+
assert.NoError(t, err)
36+
37+
assert.Equal(t, maxPR, pull.Index)
38+
}

models/issues/pull_list.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ func listPullRequestStatement(ctx context.Context, baseRepoID int64, opts *PullR
5050
return sess, nil
5151
}
5252

53+
func GetUnmergedPullRequestsByHeadInfoMax(ctx context.Context, repoID, maxIndex int64, branch string) ([]*PullRequest, error) {
54+
prs := make([]*PullRequest, 0, 2)
55+
sess := db.GetEngine(ctx).
56+
Join("INNER", "issue", "issue.id = `pull_request`.issue_id").
57+
Where("`pull_request`.head_repo_id = ? AND `pull_request`.head_branch = ? AND `pull_request`.has_merged = ? AND `issue`.is_closed = ? AND `pull_request`.flow = ? AND `issue`.`index` <= ?", repoID, branch, false, false, PullRequestFlowGithub, maxIndex)
58+
return prs, sess.Find(&prs)
59+
}
60+
5361
// GetUnmergedPullRequestsByHeadInfo returns all pull requests that are open and has not been merged
5462
func GetUnmergedPullRequestsByHeadInfo(ctx context.Context, repoID int64, branch string) ([]*PullRequest, error) {
5563
prs := make([]*PullRequest, 0, 2)

models/issues/pull_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package issues_test
55

66
import (
7+
"fmt"
78
"testing"
89

910
"code.gitea.io/gitea/models/db"
@@ -158,6 +159,91 @@ func TestGetUnmergedPullRequestsByHeadInfo(t *testing.T) {
158159
}
159160
}
160161

162+
func TestGetUnmergedPullRequestsByHeadInfoMax(t *testing.T) {
163+
assert.NoError(t, unittest.PrepareTestDatabase())
164+
165+
repoID := int64(1)
166+
maxPR := int64(0)
167+
prs, err := issues_model.GetUnmergedPullRequestsByHeadInfoMax(db.DefaultContext, repoID, maxPR, "branch2")
168+
assert.NoError(t, err)
169+
assert.Len(t, prs, 0)
170+
maxPR, err = issues_model.GetMaxIssueIndexForRepo(db.DefaultContext, repoID)
171+
assert.NoError(t, err)
172+
prs, err = issues_model.GetUnmergedPullRequestsByHeadInfoMax(db.DefaultContext, repoID, maxPR, "branch2")
173+
assert.NoError(t, err)
174+
assert.Len(t, prs, 1)
175+
for _, pr := range prs {
176+
assert.Equal(t, int64(1), pr.HeadRepoID)
177+
assert.Equal(t, "branch2", pr.HeadBranch)
178+
}
179+
pr := prs[0]
180+
181+
for _, testCase := range []struct {
182+
table string
183+
field string
184+
id int64
185+
match any
186+
nomatch any
187+
}{
188+
{
189+
table: "issue",
190+
field: "is_closed",
191+
id: pr.IssueID,
192+
match: false,
193+
nomatch: true,
194+
},
195+
{
196+
table: "pull_request",
197+
field: "flow",
198+
id: pr.ID,
199+
match: issues_model.PullRequestFlowGithub,
200+
nomatch: issues_model.PullRequestFlowAGit,
201+
},
202+
{
203+
table: "pull_request",
204+
field: "head_repo_id",
205+
id: pr.ID,
206+
match: pr.HeadRepoID,
207+
nomatch: 0,
208+
},
209+
{
210+
table: "pull_request",
211+
field: "head_branch",
212+
id: pr.ID,
213+
match: pr.HeadBranch,
214+
nomatch: "something else",
215+
},
216+
{
217+
table: "pull_request",
218+
field: "has_merged",
219+
id: pr.ID,
220+
match: false,
221+
nomatch: true,
222+
},
223+
} {
224+
t.Run(testCase.field, func(t *testing.T) {
225+
update := fmt.Sprintf("UPDATE `%s` SET `%s` = ? WHERE `id` = ?", testCase.table, testCase.field)
226+
227+
// expect no match
228+
_, err = db.GetEngine(db.DefaultContext).Exec(update, testCase.nomatch, testCase.id)
229+
assert.NoError(t, err)
230+
prs, err = issues_model.GetUnmergedPullRequestsByHeadInfoMax(db.DefaultContext, repoID, maxPR, "branch2")
231+
assert.NoError(t, err)
232+
assert.Len(t, prs, 0)
233+
234+
// expect one match
235+
_, err = db.GetEngine(db.DefaultContext).Exec(update, testCase.match, testCase.id)
236+
assert.NoError(t, err)
237+
prs, err = issues_model.GetUnmergedPullRequestsByHeadInfoMax(db.DefaultContext, repoID, maxPR, "branch2")
238+
assert.NoError(t, err)
239+
assert.Len(t, prs, 1)
240+
241+
// identical to the known PR
242+
assert.Equal(t, pr.ID, prs[0].ID)
243+
})
244+
}
245+
}
246+
161247
func TestGetUnmergedPullRequestsByBaseInfo(t *testing.T) {
162248
assert.NoError(t, unittest.PrepareTestDatabase())
163249
prs, err := issues_model.GetUnmergedPullRequestsByBaseInfo(db.DefaultContext, 1, "master")

services/pull/merge.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U
181181
}
182182

183183
defer func() {
184-
go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false, "", "")
184+
AddTestPullRequestTask(ctx, doer, pr.BaseRepo.ID, pr.BaseBranch, false, "", "")
185185
}()
186186

187187
pr.MergedCommitID, err = doMergeAndPush(ctx, pr, doer, mergeStyle, expectedHeadCommitID, message)

0 commit comments

Comments
 (0)