Skip to content
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

Add Unique Queue infrastructure and move TestPullRequests to this #9856

Merged
merged 23 commits into from
Feb 2, 2020

Conversation

zeripath
Copy link
Contributor

Add UniqueQueue as a type of Queue
Move TestPullRequests to be a UniqueQueue

@zeripath zeripath added the type/feature Completely new functionality. Can only be merged if feature freeze is not active. label Jan 18, 2020
@zeripath zeripath added this to the 1.12.0 milestone Jan 18, 2020
@GiteaBot GiteaBot added the lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. label Jan 18, 2020
@codecov-io
Copy link

codecov-io commented Jan 18, 2020

Codecov Report

Merging #9856 into master will increase coverage by <.01%.
The diff coverage is 43.44%.

Impacted file tree graph

@@            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
Impacted Files Coverage Δ
modules/setting/setting.go 44.07% <0%> (ø) ⬆️
cmd/embedded_stub.go 0% <0%> (ø) ⬆️
modules/repofiles/action.go 63.45% <100%> (ø) ⬆️
modules/repofiles/update.go 39.02% <42.2%> (ø) ⬆️
routers/private/hook.go 36.43% <50%> (ø) ⬆️
models/gpg_key.go 54.81% <0%> (-0.56%) ⬇️
... and 1 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 25c1427...111a87e. Read the comment docs.

@zeripath
Copy link
Contributor Author

There is a slight change of semantics here. The modules/sync.UniqueQueue will block whilst PushFunc(data, fn) is running.

I'm not sure if we want that?

@zeripath
Copy link
Contributor Author

zeripath commented Jan 18, 2020

oh sigh golangci-lint has disagreed... Done

@zeripath
Copy link
Contributor Author

@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.

@zeripath
Copy link
Contributor Author

Have forcibly rebased this on #10001

integrations/testlogger.go Outdated Show resolved Hide resolved
@zeripath zeripath force-pushed the unique-queues branch 3 times, most recently from a2f44af to 6ca8430 Compare January 28, 2020 21:51
@6543
Copy link
Member

6543 commented Jan 29, 2020

@zeripath Pleace rebase :)

@guillep2k
Copy link
Member

Is there any particular reason why you didn't implement this as a wrapper that adds uniqueness semantics? Something like (pardon my French):

queue, err := NewLevelQueue(handle, levelCfg, exemplar)
if err != nil && requires_uniqueness {
	queue, err = NewUniqueQueue(queue)
}

I feel like there's a lot of repeated code, and maintenance will be a problem.

@GiteaBot GiteaBot added lgtm/need 1 This PR needs approval from one additional maintainer to be merged. and removed lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. labels Jan 29, 2020
@zeripath
Copy link
Contributor Author

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(?).

modules/setting/queue.go Outdated Show resolved Hide resolved
modules/queue/setting.go Show resolved Hide resolved
// 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
Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Member

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?

Copy link
Contributor Author

@zeripath zeripath Feb 1, 2020

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...

Copy link
Member

@6543 6543 left a 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 :)

Copy link
Member

@guillep2k guillep2k left a 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. 😅 🙏

modules/queue/queue_bytefifo.go Show resolved Hide resolved
modules/queue/queue_bytefifo.go Outdated Show resolved Hide resolved
modules/queue/queue_disk_channel.go Outdated Show resolved Hide resolved
modules/queue/unique_queue_disk_channel.go Show resolved Hide resolved
modules/queue/unique_queue_disk_channel.go Show resolved Hide resolved
modules/queue/unique_queue_wrapped.go Outdated Show resolved Hide resolved
modules/queue/unique_queue_wrapped.go Show resolved Hide resolved
modules/setting/queue.go Outdated Show resolved Hide resolved
// 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
Copy link
Member

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)
Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

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! 🤔

Copy link
Contributor Author

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.

Copy link
Contributor Author

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:

// 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...

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)

  1. We should only get the PR if the status is not merged.
  2. Then we can drop the check in 208
  3. and in setMerged we need to only update if not already set merged.

Copy link
Member

@guillep2k guillep2k left a 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!! 🎉

@GiteaBot GiteaBot added lgtm/done This PR has enough approvals to get merged. There are no important open reservations anymore. and removed lgtm/need 1 This PR needs approval from one additional maintainer to be merged. labels Feb 2, 2020
@zeripath
Copy link
Contributor Author

zeripath commented Feb 2, 2020

Make lg-tm work

@zeripath zeripath merged commit 2c90338 into go-gitea:master Feb 2, 2020
@zeripath zeripath deleted the unique-queues branch February 28, 2020 17:19
@go-gitea go-gitea locked and limited conversation to collaborators Nov 24, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
lgtm/done This PR has enough approvals to get merged. There are no important open reservations anymore. type/feature Completely new functionality. Can only be merged if feature freeze is not active.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants