ChangeRebuilderImpl: Handle non-monotonic patch set timestamps

Some data created by old versions of Gerrit has timestamps out of order
in ReviewDb. Naively converting these to NoteDb results in out-of-order
commits in the meta graph. Since at least I099fd703 (possibly earlier),
ChangeNotesParser depends on ordering in the meta graph to set the
currentPatchSetId properly, so these changes were showing up with the
current patch set being the latest patch set by creation time.

The easiest way to fix this is to use the dependency mechanism in
ChangeRebuilderImpl to ensure that each patch set has a dependency on
the previous patch set. However, this breaks the createdOn timestamp,
replacing the bogus old timestamp with a newer one. Handle this case in
ChangeBundle by ignoring createdOn if ReviewDb is out of order.

Change-Id: I1a245ff528cd96b6549c237bb9073b1820af93de
This commit is contained in:
Dave Borowitz
2017-02-06 12:45:21 -05:00
committed by Edwin Kempin
parent 72f61b20fc
commit c6db35e1dc
4 changed files with 203 additions and 6 deletions

View File

@@ -1290,6 +1290,111 @@ public class ChangeBundleTest extends GerritBaseTests {
b1, b2, "description differs for PatchSet.Id " + ps1.getId() + ":" + " { abc } != {cba}");
}
@Test
public void diffPatchSetsIgnoresCreatedOnWhenReviewDbIsNonMonotonic() throws Exception {
Change c = TestChanges.newChange(project, accountId);
Timestamp beforePs1 = TimeUtil.nowTs();
PatchSet goodPs1 = new PatchSet(new PatchSet.Id(c.getId(), 1));
goodPs1.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
goodPs1.setUploader(accountId);
goodPs1.setCreatedOn(TimeUtil.nowTs());
PatchSet goodPs2 = new PatchSet(new PatchSet.Id(c.getId(), 2));
goodPs2.setRevision(new RevId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
goodPs2.setUploader(accountId);
goodPs2.setCreatedOn(TimeUtil.nowTs());
assertThat(goodPs2.getCreatedOn()).isGreaterThan(goodPs1.getCreatedOn());
PatchSet badPs2 = clone(goodPs2);
badPs2.setCreatedOn(beforePs1);
assertThat(badPs2.getCreatedOn()).isLessThan(goodPs1.getCreatedOn());
// Both ReviewDb, exact match required.
ChangeBundle b1 =
new ChangeBundle(
c,
messages(),
patchSets(goodPs1, goodPs2),
approvals(),
comments(),
reviewers(),
REVIEW_DB);
ChangeBundle b2 =
new ChangeBundle(
c,
messages(),
patchSets(goodPs1, badPs2),
approvals(),
comments(),
reviewers(),
REVIEW_DB);
assertDiffs(
b1,
b2,
"createdOn differs for PatchSet.Id "
+ badPs2.getId()
+ ":"
+ " {2009-09-30 17:00:18.0} != {2009-09-30 17:00:06.0}");
// Non-monotonic in ReviewDb but monotonic in NoteDb, timestamps are
// ignored, including for ps1.
PatchSet badPs1 = clone(goodPs1);
badPs1.setCreatedOn(TimeUtil.nowTs());
b1 =
new ChangeBundle(
c,
messages(),
patchSets(badPs1, badPs2),
approvals(),
comments(),
reviewers(),
REVIEW_DB);
b2 =
new ChangeBundle(
c,
messages(),
patchSets(goodPs1, goodPs2),
approvals(),
comments(),
reviewers(),
NOTE_DB);
assertNoDiffs(b1, b2);
assertNoDiffs(b2, b1);
// Non-monotonic in NoteDb but monotonic in ReviewDb, timestamps are not
// ignored.
b1 =
new ChangeBundle(
c,
messages(),
patchSets(goodPs1, goodPs2),
approvals(),
comments(),
reviewers(),
REVIEW_DB);
b2 =
new ChangeBundle(
c,
messages(),
patchSets(badPs1, badPs2),
approvals(),
comments(),
reviewers(),
NOTE_DB);
assertDiffs(
b1,
b2,
"createdOn differs for PatchSet.Id "
+ badPs1.getId()
+ " in NoteDb vs. ReviewDb:"
+ " {2009-09-30 17:00:24.0} != {2009-09-30 17:00:12.0}",
"createdOn differs for PatchSet.Id "
+ badPs2.getId()
+ " in NoteDb vs. ReviewDb:"
+ " {2009-09-30 17:00:06.0} != {2009-09-30 17:00:18.0}");
}
@Test
public void diffPatchSetApprovalKeySets() throws Exception {
Change c = TestChanges.newChange(project, accountId);