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:
@@ -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;
|
||||||
|
|||||||
@@ -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.");
|
||||||
|
|||||||
Reference in New Issue
Block a user