Performance Fix: Minimize number of advertisedHaves
While we set up hooks in ReceiveCommits to minimize the number of
advertised refs (for instance filtering out refs/changes and
refs/cache-automerge refs), their objectIds will still be added to
BaseReceivePack.advertisedHaves (JGit), leading to an unnecessarily
complex commit graph for JGit to traverse when checking for commit
connectivity in BaseReceivePack.checkConnectivity().
By filtering the refs before the objectIds are added to advertisedHaves,
lots of time can be saved when pushing to complex Gits.
Push times for one of our Gits (warm caches):
Before fix:
real 0m42.926s
After fix:
real 0m6.325s
This Git has around 90k change/ps refs.
Change-Id: I2f0a0830bd733f908f0aeaf1eaf0412dae428a16
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 bcc30144..897b53a 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
@@ -18,7 +18,6 @@
import static com.google.gerrit.server.git.MultiProgressMonitor.UNKNOWN;
import static com.google.gerrit.server.mail.MailUtil.getRecipientsFromApprovals;
import static com.google.gerrit.server.mail.MailUtil.getRecipientsFromFooters;
-
import static org.eclipse.jgit.lib.Constants.R_HEADS;
import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK;
@@ -114,6 +113,7 @@
import org.eclipse.jgit.transport.AdvertiseRefsHookChain;
import org.eclipse.jgit.transport.BaseReceivePack;
import org.eclipse.jgit.transport.ReceiveCommand;
+import org.eclipse.jgit.transport.RefFilter;
import org.eclipse.jgit.transport.ReceiveCommand.Result;
import org.eclipse.jgit.transport.ReceivePack;
import org.eclipse.jgit.transport.UploadPack;
@@ -363,6 +363,20 @@
rp.setAllowDeletes(true);
rp.setAllowNonFastForwards(true);
rp.setCheckReceivedObjects(true);
+ rp.setRefFilter(new RefFilter() {
+ @Override
+ public Map<String, Ref> filter(Map<String, Ref> refs) {
+ Map<String, Ref> filteredRefs = Maps.newHashMapWithExpectedSize(refs.size());
+ for (Map.Entry<String, Ref> e : refs.entrySet()) {
+ String name = e.getKey();
+ if (!name.startsWith("refs/changes/")
+ && !name.startsWith(GitRepositoryManager.REFS_CACHE_AUTOMERGE)) {
+ filteredRefs.put(name, e.getValue());
+ }
+ }
+ return filteredRefs;
+ }
+ });
if (!projectControl.allRefsAreVisible()) {
rp.setCheckReferencedObjectsAreReachable(config.checkReferencedObjectsAreReachable);