Dequeue failed change at head early.

When a change at the head of the queue fails, immediately remove
it from the queue and re-launch jobs behind it -- there's no
need to wait for it to finish.

Change-Id: I5d8650c1aa6d56090175a7b90a7baf327f6b8158
Reviewed-on: https://review.openstack.org/11825
Reviewed-by: Clark Boylan <clark.boylan@gmail.com>
Approved: James E. Blair <corvus@inaugust.com>
Tested-by: Jenkins
This commit is contained in:
James E. Blair 2012-08-22 15:19:31 -07:00 committed by Jenkins
parent caec0c5bf6
commit ec59012061
2 changed files with 90 additions and 21 deletions

View File

@ -123,9 +123,6 @@ def job_has_changes(*args):
ref = job.parameters['ZUUL_REF']
repo_messages = [c.message.strip() for c in repo.iter_commits(ref)]
commit_messages = ['%s-1' % commit.subject for commit in commits]
print 'checking that job %s has changes:' % ref
print ' commit messages:', commit_messages
print ' repo messages :', repo_messages
for msg in commit_messages:
if msg not in repo_messages:
return False
@ -859,7 +856,7 @@ class testScheduler(unittest.TestCase):
jobs[0].release()
self.waitUntilSettled()
assert len(jobs) == 1 # project-test2
assert len(jobs) == 2 # project-test2, project-merge for B
assert self.countJobResults(finished_jobs, 'ABORTED') == 4
self.fake_jenkins.hold_jobs_in_build = False
@ -921,8 +918,8 @@ class testScheduler(unittest.TestCase):
jobs[0].release()
self.waitUntilSettled()
assert len(jobs) == 1 # project-test2
assert len(queue) == 1
assert len(jobs) == 2 # project-test2, project-merge for B
assert len(queue) == 2
assert self.countJobResults(finished_jobs, 'ABORTED') == 0
self.fake_jenkins.hold_jobs_in_queue = False
@ -1031,7 +1028,6 @@ class testScheduler(unittest.TestCase):
repo = git.Repo(path)
repo_messages = [c.message.strip() for c in repo.iter_commits(ref)]
repo_messages.reverse()
print ' repo messages :', repo_messages
correct_messages = ['initial commit', 'A-1', 'B-1', 'C-1']
assert repo_messages == correct_messages
@ -1119,7 +1115,6 @@ class testScheduler(unittest.TestCase):
repo = git.Repo(path)
repo_messages = [c.message.strip() for c in repo.iter_commits(ref)]
repo_messages.reverse()
print ' repo messages :', repo_messages
correct_messages = ['initial commit', 'mp commit', 'A-1', 'B-1', 'C-1']
assert repo_messages == correct_messages
@ -1167,14 +1162,12 @@ class testScheduler(unittest.TestCase):
repo_messages = [c.message.strip()
for c in repo.iter_commits(ref_master)]
repo_messages.reverse()
print ' repo messages :', repo_messages
correct_messages = ['initial commit', 'A-1', 'C-1']
assert repo_messages == correct_messages
repo_messages = [c.message.strip()
for c in repo.iter_commits(ref_mp)]
repo_messages.reverse()
print ' repo messages :', repo_messages
correct_messages = ['initial commit', 'mp commit', 'B-1']
assert repo_messages == correct_messages
@ -1192,8 +1185,6 @@ class testScheduler(unittest.TestCase):
jobs = self.fake_jenkins.all_jobs
finished_jobs = self.fake_jenkins.job_history
print jobs
print finished_jobs
assert A.data['status'] == 'MERGED'
assert A.reported == 2
@ -1229,11 +1220,77 @@ class testScheduler(unittest.TestCase):
jobs = self.fake_jenkins.all_jobs
finished_jobs = self.fake_jenkins.job_history
pprint.pprint(jobs)
pprint.pprint(finished_jobs)
assert A.data['status'] == 'NEW'
assert A.reported == 2
assert B.data['status'] == 'NEW'
assert B.reported == 2
assert C.data['status'] == 'NEW'
assert C.reported == 2
assert len(finished_jobs) == 1
def test_head_is_dequeued_once(self):
"Test that if a change at the head fails it is dequeud only once"
# If it's dequeued more than once, we should see extra
# aborted jobs.
self.fake_jenkins.hold_jobs_in_build = True
A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B')
C = self.fake_gerrit.addFakeChange('org/project1', 'master', 'C')
A.addApproval('CRVW', 2)
B.addApproval('CRVW', 2)
C.addApproval('CRVW', 2)
self.fake_jenkins.fakeAddFailTest('project1-test1', A)
self.fake_jenkins.fakeAddFailTest('project1-test2', A)
self.fake_jenkins.fakeAddFailTest('project1-project2-integration', A)
self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
self.waitUntilSettled()
jobs = self.fake_jenkins.all_jobs
finished_jobs = self.fake_jenkins.job_history
assert len(jobs) == 1
assert jobs[0].name == 'project1-merge'
assert job_has_changes(jobs[0], A)
self.fake_jenkins.fakeRelease('.*-merge')
self.waitUntilSettled()
self.fake_jenkins.fakeRelease('.*-merge')
self.waitUntilSettled()
self.fake_jenkins.fakeRelease('.*-merge')
self.waitUntilSettled()
assert len(jobs) == 9
assert jobs[0].name == 'project1-test1'
assert jobs[1].name == 'project1-test2'
assert jobs[2].name == 'project1-project2-integration'
assert jobs[3].name == 'project1-test1'
assert jobs[4].name == 'project1-test2'
assert jobs[5].name == 'project1-project2-integration'
assert jobs[6].name == 'project1-test1'
assert jobs[7].name == 'project1-test2'
assert jobs[8].name == 'project1-project2-integration'
jobs[0].release()
self.waitUntilSettled()
assert len(jobs) == 3 # test2, integration, merge for B
assert self.countJobResults(finished_jobs, 'ABORTED') == 6
self.fake_jenkins.hold_jobs_in_build = False
self.fake_jenkins.fakeRelease()
self.waitUntilSettled()
assert len(jobs) == 0
assert len(finished_jobs) == 20
assert A.data['status'] == 'NEW'
assert B.data['status'] == 'NEW'
assert C.data['status'] == 'NEW'
assert len(finished_jobs) == 1
assert B.data['status'] == 'MERGED'
assert C.data['status'] == 'MERGED'
assert A.reported == 2
assert B.reported == 2
assert C.reported == 2

View File

@ -827,9 +827,21 @@ class DependentPipelineManager(BasePipelineManager):
# This or some other build failed. All changes behind this change
# will need to be retested. To free up resources cancel the builds
# behind this one as they will be rerun anyways.
self.cancelJobs(change.change_behind, prime=False)
self.log.debug("Canceling builds behind change: %s due to "
"failure." % change)
if not change.change_ahead:
change_behind = change.change_behind
self.log.debug("Removing failed change %s from queue" % change)
change.delete()
self.log.info("Canceling/relaunching jobs for change %s "
"behind failed change %s" %
(change_behind, change))
self.cancelJobs(change_behind)
change_behind = self._dequeueDependentChanges(change_behind)
if change_behind:
self.launchJobs(change_behind)
else:
self.cancelJobs(change.change_behind, prime=False)
self.log.debug("Canceling builds behind change: %s due to "
"failure." % change)
return True
def possiblyReportChange(self, change):