Use BatchRefUpdate to execute reference changes

Some storage backends for JGit are able to update multiple references
in a single pass efficiently. Take advantage of this by pushing
any normal reference updates (such as direct push or branch create)
into a single BatchRefUpdate object.

Change-Id: Iadd7c5a3271f5ab8eaf31881392c85dd5b0a709d
This commit is contained in:
Shawn O. Pearce
2012-07-26 11:36:58 -07:00
parent c756538f63
commit 1732f75709
2 changed files with 46 additions and 13 deletions

View File

@@ -17,6 +17,7 @@ package com.google.gerrit.server.git;
import static java.util.concurrent.TimeUnit.NANOSECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS;
import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -58,7 +59,7 @@ public class MultiProgressMonitor {
private static final char NO_SPINNER = ' '; private static final char NO_SPINNER = ' ';
/** Handle for a sub-task. */ /** Handle for a sub-task. */
public class Task { public class Task implements ProgressMonitor {
private final String name; private final String name;
private final int total; private final int total;
private volatile int count; private volatile int count;
@@ -76,6 +77,7 @@ public class MultiProgressMonitor {
* *
* @param completed number of work units completed. * @param completed number of work units completed.
*/ */
@Override
public void update(final int completed) { public void update(final int completed) {
count += completed; count += completed;
if (total != UNKNOWN) { if (total != UNKNOWN) {
@@ -97,6 +99,23 @@ public class MultiProgressMonitor {
wakeUp(); wakeUp();
} }
} }
@Override
public void start(int totalTasks) {
}
@Override
public void beginTask(String title, int totalWork) {
}
@Override
public void endTask() {
}
@Override
public boolean isCancelled() {
return false;
}
} }
private final OutputStream out; private final OutputStream out;

View File

@@ -68,6 +68,7 @@ import com.google.inject.assistedinject.Assisted;
import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.AbbreviatedObjectId; import org.eclipse.jgit.lib.AbbreviatedObjectId;
import org.eclipse.jgit.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.ObjectReader;
@@ -253,6 +254,7 @@ public class ReceiveCommits {
private Task closeProgress; private Task closeProgress;
private Task commandProgress; private Task commandProgress;
private MessageSender messageSender; private MessageSender messageSender;
private BatchRefUpdate batch;
@Inject @Inject
ReceiveCommits(final ReviewDb db, ReceiveCommits(final ReviewDb db,
@@ -460,6 +462,10 @@ public class ReceiveCommits {
closeProgress = progress.beginSubTask("closed", UNKNOWN); closeProgress = progress.beginSubTask("closed", UNKNOWN);
commandProgress = progress.beginSubTask("refs", UNKNOWN); commandProgress = progress.beginSubTask("refs", UNKNOWN);
batch = repo.getRefDatabase().newBatchUpdate();
batch.setRefLogIdent(rp.getRefLogIdent());
batch.setRefLogMessage("push", true);
parseCommands(commands); parseCommands(commands);
if (newChange != null && newChange.getResult() == NOT_ATTEMPTED) { if (newChange != null && newChange.getResult() == NOT_ATTEMPTED) {
createNewChanges(); createNewChanges();
@@ -469,6 +475,22 @@ public class ReceiveCommits {
doReplaces(); doReplaces();
replaceProgress.end(); replaceProgress.end();
if (!batch.getCommands().isEmpty()) {
try {
batch.execute(rp.getRevWalk(), commandProgress);
} catch (IOException err) {
int cnt = 0;
for (ReceiveCommand cmd : batch.getCommands()) {
if (cmd.getResult() == NOT_ATTEMPTED) {
cmd.setResult(REJECTED_OTHER_REASON, "internal server error");
cnt++;
}
}
log.error(String.format(
"Failed to store %d refs in %s", cnt, project.getName()), err);
}
}
if (!errors.isEmpty()) { if (!errors.isEmpty()) {
for (Error error : errors.keySet()) { for (Error error : errors.keySet()) {
rp.sendMessage(buildError(error, errors.get(error))); rp.sendMessage(buildError(error, errors.get(error)));
@@ -691,9 +713,7 @@ public class ReceiveCommits {
RefControl ctl = projectControl.controlForRef(cmd.getRefName()); RefControl ctl = projectControl.controlForRef(cmd.getRefName());
if (ctl.canCreate(rp.getRevWalk(), obj)) { if (ctl.canCreate(rp.getRevWalk(), obj)) {
validateNewCommits(ctl, cmd); validateNewCommits(ctl, cmd);
if (cmd.getResult() == NOT_ATTEMPTED) { batch.addCommand(cmd);
cmd.execute(rp);
}
} else { } else {
errors.put(Error.CREATE, ctl.getRefName()); errors.put(Error.CREATE, ctl.getRefName());
reject(cmd, "can not create new references"); reject(cmd, "can not create new references");
@@ -708,9 +728,7 @@ public class ReceiveCommits {
} }
validateNewCommits(ctl, cmd); validateNewCommits(ctl, cmd);
if (cmd.getResult() == NOT_ATTEMPTED) { batch.addCommand(cmd);
cmd.execute(rp);
}
} else { } else {
if (GitRepositoryManager.REF_CONFIG.equals(ctl.getRefName())) { if (GitRepositoryManager.REF_CONFIG.equals(ctl.getRefName())) {
errors.put(Error.CONFIG_UPDATE, GitRepositoryManager.REF_CONFIG); errors.put(Error.CONFIG_UPDATE, GitRepositoryManager.REF_CONFIG);
@@ -743,9 +761,7 @@ public class ReceiveCommits {
private void parseDelete(final ReceiveCommand cmd) { private void parseDelete(final ReceiveCommand cmd) {
RefControl ctl = projectControl.controlForRef(cmd.getRefName()); RefControl ctl = projectControl.controlForRef(cmd.getRefName());
if (ctl.canDelete()) { if (ctl.canDelete()) {
if (cmd.getResult() == NOT_ATTEMPTED) { batch.addCommand(cmd);
cmd.execute(rp);
}
} else { } else {
if (GitRepositoryManager.REF_CONFIG.equals(ctl.getRefName())) { if (GitRepositoryManager.REF_CONFIG.equals(ctl.getRefName())) {
reject(cmd, "cannot delete project configuration"); reject(cmd, "cannot delete project configuration");
@@ -778,9 +794,7 @@ public class ReceiveCommits {
} }
if (ctl.canForceUpdate()) { if (ctl.canForceUpdate()) {
if (cmd.getResult() == NOT_ATTEMPTED) { batch.setAllowNonFastForwards(true).addCommand(cmd);
cmd.execute(rp);
}
} else { } else {
cmd.setResult(REJECTED_NONFASTFORWARD, " need '" cmd.setResult(REJECTED_NONFASTFORWARD, " need '"
+ PermissionRule.FORCE_PUSH + "' privilege."); + PermissionRule.FORCE_PUSH + "' privilege.");