Improve performance of ReceiveCommits by reducing RevWalk load
JGit RevWalk does not perform well when a large number of objects are
added to the start set by markStart or markUninteresting. Avoid
putting existing refs/changes/ or refs/tags/ into the RevWalk and
instead use only the refs/heads namespace and the name of the branch
used in the refs/for/ push line.
Catch existing changes by looking for their exact commit SHA-1, rather
than complete ancestory. This should have roughly the same outcome for
anyone pushing a new commit on top of an existing open change, but
with lower computional cost at the server.
Change-Id: Ie2bb9176799528f6422292f3f889e3d28cbf5135
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
index 8316f7b..ef3530e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.git;
+import static org.eclipse.jgit.lib.Constants.R_HEADS;
import static com.google.gerrit.server.git.MultiProgressMonitor.UNKNOWN;
import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK;
@@ -28,6 +29,7 @@
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
@@ -1116,12 +1118,22 @@
walk.sort(RevSort.TOPO);
walk.sort(RevSort.REVERSE, true);
try {
+ Set<ObjectId> existing = Sets.newHashSet();
walk.markStart(walk.parseCommit(newChange.getNewId()));
- for (ObjectId id : existingObjects()) {
- try {
- walk.markUninteresting(walk.parseCommit(id));
- } catch (IOException e) {
+ for (Ref ref : repo.getAllRefs().values()) {
+ if (ref.getObjectId() == null) {
continue;
+ } else if (ref.getName().startsWith("refs/changes/")) {
+ existing.add(ref.getObjectId());
+ } else if (ref.getName().startsWith(R_HEADS)
+ || ref.getName().equals(destBranchCtl.getRefName())) {
+ try {
+ walk.markUninteresting(walk.parseCommit(ref.getObjectId()));
+ } catch (IOException e) {
+ log.warn(String.format("Invalid ref %s in %s",
+ ref.getName(), project.getName()), e);
+ continue;
+ }
}
}
@@ -1131,7 +1143,7 @@
if (c == null) {
break;
}
- if (replaceByCommit.containsKey(c)) {
+ if (existing.contains(c) || replaceByCommit.containsKey(c)) {
// This commit was already scheduled to replace an existing PatchSet.
//
continue;