ChangeUtil: Don't scan all refs to get next patch set ID
The nextPatchSetId variant that takes a Map<String, Ref> was intended for use in ReceiveCommits, which happens to have such a map readily available. When it's not already available, scanning all refs is wasteful, since we only care about refs under a given change. Split the method that takes a Map into two different, slightly more descriptively named methods. This should significantly improve performance on large repositories of REST API handlers like Rebase that create new patch sets. The type Map<String, ObjectId> was chosen for its similarity to the RefCache interface, which reports ObjectIds instead of Refs. A similar interface will be used in the restricted repository view that we expose to BatchUpdate.Contexts. Change-Id: I6ffcc3099658afea1cf8ac5bcec44b92e263a915
This commit is contained in:
@@ -16,6 +16,7 @@ package com.google.gerrit.server;
|
||||
|
||||
import static java.util.Comparator.comparingInt;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||
@@ -24,8 +25,8 @@ import java.io.IOException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.RefDatabase;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
|
||||
@Singleton
|
||||
@@ -47,7 +48,16 @@ public class ChangeUtil {
|
||||
return UUID_ENCODING.encode(buf, 0, 4) + '_' + UUID_ENCODING.encode(buf, 4, 4);
|
||||
}
|
||||
|
||||
public static PatchSet.Id nextPatchSetId(Map<String, Ref> allRefs, PatchSet.Id id) {
|
||||
/**
|
||||
* Get the next patch set ID from a previously-read map of all refs.
|
||||
*
|
||||
* @param allRefs map of full ref name to ref, in the same format returned by {@link
|
||||
* org.eclipse.jgit.lib.RefDatabase#getRefs(String)} when passing {@code ""}.
|
||||
* @param id previous patch set ID.
|
||||
* @return next unused patch set ID for the same change, skipping any IDs whose corresponding ref
|
||||
* names appear in the {@code allRefs} map.
|
||||
*/
|
||||
public static PatchSet.Id nextPatchSetIdFromAllRefsMap(Map<String, Ref> allRefs, PatchSet.Id id) {
|
||||
PatchSet.Id next = nextPatchSetId(id);
|
||||
while (allRefs.containsKey(next.toRefName())) {
|
||||
next = nextPatchSetId(next);
|
||||
@@ -55,12 +65,55 @@ public class ChangeUtil {
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next patch set ID from a previously-read map of refs below the change prefix.
|
||||
*
|
||||
* @param changeRefs map of ref suffix to SHA-1, where the keys are ref names with the {@code
|
||||
* refs/changes/CD/ABCD/} prefix stripped. All refs should be under {@code id}'s change ref
|
||||
* prefix. The keys match the format returned by {@link
|
||||
* org.eclipse.jgit.lib.RefDatabase#getRefs(String)} when passing the appropriate {@code
|
||||
* refs/changes/CD/ABCD}.
|
||||
* @param id previous patch set ID.
|
||||
* @return next unused patch set ID for the same change, skipping any IDs whose corresponding ref
|
||||
* names appear in the {@code changeRefs} map.
|
||||
*/
|
||||
public static PatchSet.Id nextPatchSetIdFromChangeRefsMap(
|
||||
Map<String, ObjectId> changeRefs, PatchSet.Id id) {
|
||||
int prefixLen = id.getParentKey().toRefPrefix().length();
|
||||
PatchSet.Id next = nextPatchSetId(id);
|
||||
while (changeRefs.containsKey(next.toRefName().substring(prefixLen))) {
|
||||
next = nextPatchSetId(next);
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next patch set ID just looking at a single previous patch set ID.
|
||||
*
|
||||
* <p>This patch set ID may or may not be available in the database; callers that want a
|
||||
* previously-unused ID should use {@link #nextPatchSetIdFromAllRefsMap} or {@link
|
||||
* #nextPatchSetIdFromChangeRefsMap}.
|
||||
*
|
||||
* @param id previous patch set ID.
|
||||
* @return next patch set ID for the same change, incrementing by 1.
|
||||
*/
|
||||
public static PatchSet.Id nextPatchSetId(PatchSet.Id id) {
|
||||
return new PatchSet.Id(id.getParentKey(), id.get() + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next patch set ID from scanning refs in the repo.
|
||||
*
|
||||
* @param git repository to scan for patch set refs.
|
||||
* @param id previous patch set ID.
|
||||
* @return next unused patch set ID for the same change, skipping any IDs whose corresponding ref
|
||||
* names appear in the repository.
|
||||
*/
|
||||
public static PatchSet.Id nextPatchSetId(Repository git, PatchSet.Id id) throws IOException {
|
||||
return nextPatchSetId(git.getRefDatabase().getRefs(RefDatabase.ALL), id);
|
||||
return nextPatchSetIdFromChangeRefsMap(
|
||||
Maps.transformValues(
|
||||
git.getRefDatabase().getRefs(id.getParentKey().toRefPrefix()), Ref::getObjectId),
|
||||
id);
|
||||
}
|
||||
|
||||
public static String cropSubject(String subject) {
|
||||
|
||||
@@ -2507,7 +2507,8 @@ public class ReceiveCommits {
|
||||
|
||||
private void newPatchSet() throws IOException {
|
||||
RevCommit newCommit = rp.getRevWalk().parseCommit(newCommitId);
|
||||
psId = ChangeUtil.nextPatchSetId(allRefs, notes.getChange().currentPatchSetId());
|
||||
psId =
|
||||
ChangeUtil.nextPatchSetIdFromAllRefsMap(allRefs, notes.getChange().currentPatchSetId());
|
||||
info = patchSetInfoFactory.get(rp.getRevWalk(), newCommit, psId);
|
||||
cmd = new ReceiveCommand(ObjectId.zeroId(), newCommitId, psId.toRefName());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user