-
-
Notifications
You must be signed in to change notification settings - Fork 5.8k
Add Unique Queue infrastructure and move TestPullRequests to this #9856
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Codecov Report
@@ Coverage Diff @@
## master #9856 +/- ##
==========================================
+ Coverage 43.39% 43.39% +<.01%
==========================================
Files 575 575
Lines 79636 79636
==========================================
+ Hits 34557 34560 +3
+ Misses 40798 40795 -3
Partials 4281 4281
Continue to review full report at Codecov.
|
There is a slight change of semantics here. The modules/sync.UniqueQueue will block whilst I'm not sure if we want that? |
|
@guillep2k I've ended up implementing the Flush command - and wiring it in here. This finally appears to fix the deadlock in the tests. I don't think it's masking the issue - I think rather that the deadlock was happening due to previous tests still running over. |
dc9d8ff
to
2c4350b
Compare
Have forcibly rebased this on #10001 |
a2f44af
to
6ca8430
Compare
@zeripath Pleace rebase :) |
This adds functionality for Unique Queues
6ca8430
to
92c2207
Compare
Is there any particular reason why you didn't implement this as a wrapper that adds uniqueness semantics? Something like (pardon my French):
I feel like there's a lot of repeated code, and maintenance will be a problem. |
You definitely can't just stick a wrapper around the queues as the uniqueness semantics is deep not shallow. However for level (and possibly redis) I guess I can make an interface that represents a common nosqldb queue - and can have a readToChan (Run/Process?) function that does our work - the trouble is that interface ends up looking a lot like queue.Queue +- Flushable! We need something like this if we want to have share nosqldb connections anyway - as at present our configuration is going to lead to a proliferation of redisclients/leveldbs as more things get moved to queues. This will not reduce the amount of code but it will reduce the amount of code duplication. The wrapper is the most excessive reuse of code - some of it might be possible to be moved into delayed starter(?). |
// pullRequestQueue represents a queue to handle update pull request tests | ||
var pullRequestQueue = sync.NewUniqueQueue(setting.Repository.PullRequestQueueLength) | ||
// prQueue represents a queue to handle update pull request tests | ||
var prQueue queue.UniqueQueue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know you wrote about why this required a unique queue somewhere, but I just can't find your comment. Was it because PRs were checked right after being merged in the UI?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We currently request tests on PRs they'd never stop being tested if we didn't unique them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do they lock each other and produce a deadlock? Can that happen with two different PRs on the same repo?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not that - it's about over testing the patches. They're not free to do!
If you update a repo any PR attached to that repo is currently added to the PR patch checking queue. I misremembered this code - I just went to improve that but it's already fine...
However, imagine the Gitea case. Gitea has >100 PR. When you push to master all of those PRs need to be tested again - if the first one tested succeeds and you merge that before the others are complete they need to be tested again... If you are not uniqueing then you're going to be doing ~90 an additional unnecessary tests and that new PR of yours is not going to be tested for 190 tests...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
still a lot code :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow!!! Such hard work!! 😮 🚀
It looks much better than previous versions. Here some comments. This time I've finished reading all of it, so pretty please, if possible refrain from force pushing over this commit. 😅 🙏
// pullRequestQueue represents a queue to handle update pull request tests | ||
var pullRequestQueue = sync.NewUniqueQueue(setting.Repository.PullRequestQueueLength) | ||
// prQueue represents a queue to handle update pull request tests | ||
var prQueue queue.UniqueQueue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do they lock each other and produce a deadlock? Can that happen with two different PRs on the same repo?
queue.WorkerPool = NewWorkerPool(func(data ...Data) { | ||
for _, datum := range data { | ||
queue.lock.Lock() | ||
delete(queue.table, datum) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The datum is removed from the table before it's handled. checkAndUpdateStatus()
call to .Has()
becomes pointless.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you taking about in check.go?
Yes, this replicates current behaviour. Once you start handling a queue datum you have to assume that any further additions may be because of updates to that datum - hence it should be removed from the unique queue table at that point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, but now you're not preventing two checks from running at the same time! 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm... need to think about this one. The logic in the check PR function is pretty bad in any case and needs a refactor. Part of me wonders if we should fix it in another PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, as I've written before the logic here is broken. We really need your temporary lock thing to do the status changes but I think (!) if we change the following then it should improve things:
Lines 30 to 42 in 5ab601b
// AddToTaskQueue adds itself to pull request test task queue. | |
func AddToTaskQueue(pr *models.PullRequest) { | |
go func() { | |
err := prQueue.PushFunc(strconv.FormatInt(pr.ID, 10), func() error { | |
pr.Status = models.PullRequestStatusChecking | |
err := pr.UpdateCols("status") | |
if err != nil { | |
log.Error("AddToTaskQueue.UpdateCols[%d].(add to queue): %v", pr.ID, err) | |
} else { | |
log.Trace("Adding PR ID: %d to the test pull requests queue", pr.ID) | |
} | |
return err | |
}) |
This needs to only update to StatusChecking if status is not StatusMerged.
Then...
Lines 199 to 220 in 5ab601b
prID := datum.(string) | |
id := com.StrTo(prID).MustInt64() | |
log.Trace("Testing PR ID %d from the pull requests patch checking queue", id) | |
pr, err := models.GetPullRequestByID(id) | |
if err != nil { | |
log.Error("GetPullRequestByID[%s]: %v", prID, err) | |
continue | |
} else if pr.Status != models.PullRequestStatusChecking { | |
continue | |
} else if manuallyMerged(pr) { | |
continue | |
} else if err = TestPatch(pr); err != nil { | |
log.Error("testPatch[%d]: %v", pr.ID, err) | |
pr.Status = models.PullRequestStatusError | |
if err := pr.UpdateCols("status"); err != nil { | |
log.Error("update pr [%d] status to PullRequestStatusError failed: %v", pr.ID, err) | |
} | |
continue | |
} | |
checkAndUpdateStatus(pr) |
- We should only get the PR if the status is not merged.
- Then we can drop the check in 208
- and in setMerged we need to only update if not already set merged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. If possible, add some comments clarifying the "uniqueness" of the unique queues to avoid pitfalls in the future.
Kudos for the monstrous work!! 🎉
Make lg-tm work |
Add UniqueQueue as a type of Queue
Move TestPullRequests to be a UniqueQueue