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); logDebug("Beginning integration of {}", change);
try { try {
ChangeSet cs = mergeSuperSet.completeChangeSet(db, change, caller); ChangeSet cs = mergeSuperSet.setMergeOpRepoManager(orm)
.completeChangeSet(db, change, caller);
checkState(cs.ids().contains(change.getId()), checkState(cs.ids().contains(change.getId()),
"change %s missing from %s", change.getId(), cs); "change %s missing from %s", change.getId(), cs);
if (cs.furtherHiddenChanges()) { if (cs.furtherHiddenChanges()) {

View File

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