MergeSuperSet: Cache open repositories

completeChangeSetWithoutTopic was trying to be nice by grouping
changes by project to cut down on opening repositories multiple times.
However, it would still open repos multiple times across multiple
iterations of the main completeChangeSet loop.

Reuse the existing MergeOpRepoManager, since this code is already
called in the MergeOp path where we have one available. As it happens,
the options (particularly the sort) that are already set in
MergeOpRepoManager are more or less what we need here.

Change-Id: Icdc78daab3290f5a39dba0e5a162d51ec93ca00a
This commit is contained in:
Dave Borowitz
2016-09-12 12:58:34 -04:00
parent 3a4e10f3da
commit 9b152d9901
2 changed files with 109 additions and 101 deletions

View File

@@ -412,7 +412,8 @@ public class MergeOp implements AutoCloseable {
logDebug("Beginning integration of {}", change);
try {
ChangeSet cs = mergeSuperSet.completeChangeSet(db, change, caller);
ChangeSet cs = mergeSuperSet.setMergeOpRepoManager(orm)
.completeChangeSet(db, change, caller);
checkState(cs.ids().contains(change.getId()),
"change %s missing from %s", change.getId(), cs);
if (cs.furtherHiddenChanges()) {

View File

@@ -14,13 +14,12 @@
package com.google.gerrit.server.git;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
import com.google.gerrit.common.data.SubmitTypeRecord;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.reviewdb.client.Branch;
@@ -31,8 +30,10 @@ import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.change.Submit;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.MergeOpRepoManager.OpenRepo;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.SubmitRuleEvaluator;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
@@ -40,15 +41,11 @@ import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevSort;
import org.eclipse.jgit.revwalk.RevWalk;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -82,23 +79,33 @@ public class MergeSuperSet {
private final ChangeData.Factory changeDataFactory;
private final Provider<InternalChangeQuery> queryProvider;
private final GitRepositoryManager repoManager;
private final Provider<MergeOpRepoManager> repoManagerProvider;
private final Config cfg;
private MergeOpRepoManager orm;
private boolean closeOrm;
@Inject
MergeSuperSet(@GerritServerConfig Config cfg,
ChangeData.Factory changeDataFactory,
Provider<InternalChangeQuery> queryProvider,
GitRepositoryManager repoManager) {
Provider<MergeOpRepoManager> repoManagerProvider) {
this.cfg = cfg;
this.changeDataFactory = changeDataFactory;
this.queryProvider = queryProvider;
this.repoManager = repoManager;
this.repoManagerProvider = repoManagerProvider;
}
public ChangeSet completeChangeSet(ReviewDb db, Change change, CurrentUser user)
throws MissingObjectException, IncorrectObjectTypeException, IOException,
OrmException {
public MergeSuperSet setMergeOpRepoManager(MergeOpRepoManager orm) {
checkState(this.orm == null);
this.orm = checkNotNull(orm);
closeOrm = false;
return this;
}
public ChangeSet completeChangeSet(ReviewDb db, Change change,
CurrentUser user) throws IOException, OrmException {
try {
ChangeData cd =
changeDataFactory.create(db, change.getProject(), change.getId());
cd.changeControl(user);
@@ -107,16 +114,12 @@ public class MergeSuperSet {
return completeChangeSetIncludingTopics(db, cs, user);
}
return completeChangeSetWithoutTopic(db, cs, user);
} finally {
if (closeOrm && orm != null) {
orm.close();
orm = null;
}
private static ImmutableListMultimap<Project.NameKey, ChangeData>
byProject(Iterable<ChangeData> changes) throws OrmException {
ImmutableListMultimap.Builder<Project.NameKey, ChangeData> builder =
new ImmutableListMultimap.Builder<>();
for (ChangeData cd : changes) {
builder.put(cd.change().getProject(), cd);
}
return builder.build();
}
private SubmitType submitType(ChangeData cd, PatchSet ps, boolean visible)
@@ -146,22 +149,17 @@ public class MergeSuperSet {
return str.type;
}
private ChangeSet completeChangeSetWithoutTopic(ReviewDb db, ChangeSet changes,
CurrentUser user) throws MissingObjectException,
IncorrectObjectTypeException, IOException, OrmException {
private ChangeSet completeChangeSetWithoutTopic(ReviewDb db,
ChangeSet changes, CurrentUser user) throws IOException, OrmException {
List<ChangeData> visibleChanges = new ArrayList<>();
List<ChangeData> nonVisibleChanges = new ArrayList<>();
Multimap<Project.NameKey, ChangeData> pc =
byProject(
Iterables.concat(changes.changes(), changes.nonVisibleChanges()));
for (Project.NameKey project : pc.keySet()) {
try (Repository repo = repoManager.openRepository(project);
RevWalk rw = CodeReviewCommit.newRevWalk(repo)) {
for (ChangeData cd : pc.get(project)) {
for (ChangeData cd :
Iterables.concat(changes.changes(), changes.nonVisibleChanges())) {
checkState(cd.hasChangeControl(),
"completeChangeSet forgot to set changeControl for current user"
+ " at ChangeData creation time");
OpenRepo or = getRepo(cd.change().getProject());
boolean visible = changes.ids().contains(cd.getId());
if (visible && !cd.changeControl().isVisible(db, cd)) {
// We thought the change was visible, but it isn't.
@@ -192,19 +190,17 @@ public class MergeSuperSet {
// Get the underlying git commit object
String objIdStr = ps.getRevision().get();
RevCommit commit = rw.parseCommit(ObjectId.fromString(objIdStr));
RevCommit commit = or.rw.parseCommit(ObjectId.fromString(objIdStr));
// Collect unmerged ancestors
Branch.NameKey destBranch = cd.change().getDest();
repo.getRefDatabase().refresh();
Ref ref = repo.getRefDatabase().getRef(destBranch.get());
Ref ref = or.repo.getRefDatabase().getRef(destBranch.get());
rw.reset();
rw.sort(RevSort.TOPO);
rw.markStart(commit);
or.rw.reset();
or.rw.markStart(commit);
if (ref != null) {
RevCommit head = rw.parseCommit(ref.getObjectId());
rw.markUninteresting(head);
RevCommit head = or.rw.parseCommit(ref.getObjectId());
or.rw.markUninteresting(head);
}
List<String> hashes = new ArrayList<>();
@@ -212,7 +208,7 @@ public class MergeSuperSet {
// SubmitStrategyOp to correct the situation later, assuming it gets
// returned by byCommitsOnBranchNotMerged below.
hashes.add(objIdStr);
for (RevCommit c : rw) {
for (RevCommit c : or.rw) {
if (!c.equals(commit)) {
hashes.add(c.name());
}
@@ -221,19 +217,31 @@ public class MergeSuperSet {
if (!hashes.isEmpty()) {
Iterable<ChangeData> destChanges = query()
.byCommitsOnBranchNotMerged(
repo, db, cd.change().getDest(), hashes);
or.repo, db, cd.change().getDest(), hashes);
for (ChangeData chd : destChanges) {
chd.changeControl(user);
dest.add(chd);
}
}
}
}
}
return new ChangeSet(visibleChanges, nonVisibleChanges);
}
private OpenRepo getRepo(Project.NameKey project) throws IOException {
if (orm == null) {
orm = repoManagerProvider.get();
closeOrm = true;
}
try {
OpenRepo or = orm.openRepo(project);
checkState(or.rw.hasRevSort(RevSort.TOPO));
return or;
} catch (NoSuchProjectException e) {
throw new IOException(e);
}
}
/**
* Completes {@code cs} with any additional changes from its topics
* <p>
@@ -296,8 +304,7 @@ public class MergeSuperSet {
private ChangeSet completeChangeSetIncludingTopics(
ReviewDb db, ChangeSet changes, CurrentUser user)
throws MissingObjectException, IncorrectObjectTypeException, IOException,
OrmException {
throws IOException, OrmException {
Set<String> topicsSeen = new HashSet<>();
Set<String> visibleTopicsSeen = new HashSet<>();
int oldSeen;