Don't reset buildset when cycle dependency merged

In case a live change depends on a cycle and the cycle is merged while
the item is still active the scheduler will detect the cycle as changed
and re-enqueue the dependent change.

The reason for this behavior is that we don't consider dependencies of
merged changes when building the dependency graph.

Change-Id: Ibc952886b56655c0705882497511b120e5a731cd
This commit is contained in:
Simon Westphahl 2024-03-21 15:34:05 +01:00 committed by James E. Blair
parent 3bcd923a68
commit 349c6a029d
2 changed files with 48 additions and 3 deletions

View File

@ -381,6 +381,44 @@ class TestGerritCircularDependencies(ZuulTestCase):
self.assertEqual(B.data["status"], "MERGED")
self.assertEqual(C.data["status"], "MERGED")
def test_dependency_on_merged_cycle(self):
self.executor_server.hold_jobs_in_build = True
A = self.fake_gerrit.addFakeChange("org/project", "master", "A")
B = self.fake_gerrit.addFakeChange("org/project1", "master", "B")
C = self.fake_gerrit.addFakeChange("org/project2", "master", "C")
# A -> B -> C -> B (via commit-depends)
A.data["commitMessage"] = "{}\n\nDepends-On: {}\n".format(
A.subject, B.data["url"]
)
B.data["commitMessage"] = "{}\n\nDepends-On: {}\n".format(
B.subject, C.data["url"]
)
C.data["commitMessage"] = "{}\n\nDepends-On: {}\n".format(
C.subject, B.data["url"]
)
# Start jobs for A while B + C are still open so they get
# enqueued as a non-live item ahead of A.
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
# Fake merge of change B + C while those changes are
# still part of a non-live item as dependency for A.
B.setMerged()
C.setMerged()
self.fake_gerrit.addEvent(B.getChangeMergedEvent())
self.fake_gerrit.addEvent(C.getChangeMergedEvent())
self.waitUntilSettled()
self.executor_server.hold_jobs_in_build = False
self.executor_server.release()
self.waitUntilSettled()
self.assertHistory([
dict(name="project-job", result="SUCCESS", changes="3,1 2,1 1,1"),
], ordered=False)
def test_dependent_change_on_cycle(self):
self.executor_server.hold_jobs_in_build = True

View File

@ -1633,11 +1633,18 @@ class PipelineManager(metaclass=ABCMeta):
# Verify that the cycle dependency graph is correct
cycle = self.cycleForChange(
item.changes[0], dependency_graph, item.event, debug=False)
cycle = cycle or [item.changes[0]]
cycle = set(cycle or [item.changes[0]])
item_cycle = set(item.changes)
if set(cycle) != item_cycle:
# We don't consider merged dependencies when building the
# dependency graph, so we need to ignore differences resulting
# from changes that have been merged in the meantime. Put any
# missing merged changes back in the cycle for comparison
# purposes.
merged_changes = set(c for c in (item_cycle - cycle) if c.is_merged)
cycle |= merged_changes
if cycle != item_cycle:
log.info("Item cycle has changed: %s, now: %s, was: %s", item,
set(cycle), item_cycle)
cycle, item_cycle)
self.removeItem(item)
if item.live:
self.reEnqueueChanges(item, item.changes)