summaryrefslogtreecommitdiffstats
path: root/gerrit-server/src/main/java/com/google/gerrit/server/update/ReviewDbBatchUpdate.java
diff options
context:
space:
mode:
Diffstat (limited to 'gerrit-server/src/main/java/com/google/gerrit/server/update/ReviewDbBatchUpdate.java')
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/update/ReviewDbBatchUpdate.java183
1 files changed, 63 insertions, 120 deletions
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/update/ReviewDbBatchUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/update/ReviewDbBatchUpdate.java
index 63d83ac1f5..0cc994e477 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/update/ReviewDbBatchUpdate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/update/ReviewDbBatchUpdate.java
@@ -14,11 +14,11 @@
package com.google.gerrit.server.update;
-import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static java.util.Comparator.comparing;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import static java.util.stream.Collectors.toList;
import com.google.common.base.Stopwatch;
import com.google.common.base.Throwables;
@@ -29,7 +29,6 @@ import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.Description.Units;
@@ -57,18 +56,12 @@ import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
import com.google.gerrit.server.notedb.NoteDbUpdateManager;
import com.google.gerrit.server.notedb.NoteDbUpdateManager.MismatchedStateException;
import com.google.gerrit.server.notedb.NotesMigration;
-import com.google.gerrit.server.project.ChangeControl;
-import com.google.gerrit.server.project.InvalidChangeOperationException;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gerrit.server.project.NoSuchRefException;
import com.google.gerrit.server.util.RequestId;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.assistedinject.Assisted;
-import com.google.inject.assistedinject.AssistedInject;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.ArrayList;
@@ -84,7 +77,6 @@ import org.eclipse.jgit.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -117,19 +109,14 @@ class ReviewDbBatchUpdate extends BatchUpdate {
}
class ContextImpl implements Context {
- private Repository repoWrapper;
-
@Override
- public Repository getRepository() throws IOException {
- if (repoWrapper == null) {
- repoWrapper = new ReadOnlyRepository(ReviewDbBatchUpdate.this.getRepository());
- }
- return repoWrapper;
+ public RepoView getRepoView() throws IOException {
+ return ReviewDbBatchUpdate.this.getRepoView();
}
@Override
public RevWalk getRevWalk() throws IOException {
- return ReviewDbBatchUpdate.this.getRevWalk();
+ return getRepoView().getRevWalk();
}
@Override
@@ -165,24 +152,19 @@ class ReviewDbBatchUpdate extends BatchUpdate {
private class RepoContextImpl extends ContextImpl implements RepoContext {
@Override
- public Repository getRepository() throws IOException {
- return ReviewDbBatchUpdate.this.getRepository();
- }
-
- @Override
public ObjectInserter getInserter() throws IOException {
- return ReviewDbBatchUpdate.this.getObjectInserter();
+ return getRepoView().getInserterWrapper();
}
@Override
public void addRefUpdate(ReceiveCommand cmd) throws IOException {
initRepository();
- commands.add(cmd);
+ repoView.getCommands().add(cmd);
}
}
private class ChangeContextImpl extends ContextImpl implements ChangeContext {
- private final ChangeControl ctl;
+ private final ChangeNotes notes;
private final Map<PatchSet.Id, ChangeUpdate> updates;
private final ReviewDbWrapper dbWrapper;
private final Repository threadLocalRepo;
@@ -192,8 +174,8 @@ class ReviewDbBatchUpdate extends BatchUpdate {
private boolean bumpLastUpdatedOn = true;
protected ChangeContextImpl(
- ChangeControl ctl, ReviewDbWrapper dbWrapper, Repository repo, RevWalk rw) {
- this.ctl = ctl;
+ ChangeNotes notes, ReviewDbWrapper dbWrapper, Repository repo, RevWalk rw) {
+ this.notes = checkNotNull(notes);
this.dbWrapper = dbWrapper;
this.threadLocalRepo = repo;
this.threadLocalRevWalk = rw;
@@ -207,11 +189,6 @@ class ReviewDbBatchUpdate extends BatchUpdate {
}
@Override
- public Repository getRepository() {
- return threadLocalRepo;
- }
-
- @Override
public RevWalk getRevWalk() {
return threadLocalRevWalk;
}
@@ -220,8 +197,8 @@ class ReviewDbBatchUpdate extends BatchUpdate {
public ChangeUpdate getUpdate(PatchSet.Id psId) {
ChangeUpdate u = updates.get(psId);
if (u == null) {
- u = changeUpdateFactory.create(ctl, when);
- if (newChanges.containsKey(ctl.getId())) {
+ u = changeUpdateFactory.create(notes, user, when);
+ if (newChanges.containsKey(notes.getChangeId())) {
u.setAllowWriteToNewRef(true);
}
u.setPatchSetId(psId);
@@ -231,14 +208,13 @@ class ReviewDbBatchUpdate extends BatchUpdate {
}
@Override
- public ChangeControl getControl() {
- checkNotNull(ctl);
- return ctl;
+ public ChangeNotes getNotes() {
+ return notes;
}
@Override
- public void bumpLastUpdatedOn(boolean bump) {
- bumpLastUpdatedOn = bump;
+ public void dontBumpLastUpdatedOn() {
+ bumpLastUpdatedOn = false;
}
@Override
@@ -272,18 +248,9 @@ class ReviewDbBatchUpdate extends BatchUpdate {
if (updates.isEmpty()) {
return;
}
- if (requestId != null) {
- for (BatchUpdate u : updates) {
- checkArgument(
- u.requestId == null || u.requestId == requestId,
- "refusing to overwrite RequestId %s in update with %s",
- u.requestId,
- requestId);
- u.setRequestId(requestId);
- }
- }
+ setRequestIds(updates, requestId);
try {
- Order order = getOrder(updates);
+ Order order = getOrder(updates, listener);
boolean updateChangesInParallel = getUpdateChangesInParallel(updates);
switch (order) {
case REPO_BEFORE_DB:
@@ -304,66 +271,40 @@ class ReviewDbBatchUpdate extends BatchUpdate {
for (ReviewDbBatchUpdate u : updates) {
u.reindexChanges(u.executeChangeOps(updateChangesInParallel, dryrun));
}
- listener.afterUpdateChanges();
for (ReviewDbBatchUpdate u : updates) {
u.executeUpdateRepo();
}
- listener.afterUpdateRepos();
for (ReviewDbBatchUpdate u : updates) {
u.executeRefUpdates(dryrun);
}
- listener.afterUpdateRefs();
break;
default:
throw new IllegalStateException("invalid execution order: " + order);
}
- @SuppressWarnings("deprecation")
- List<com.google.common.util.concurrent.CheckedFuture<?, IOException>> indexFutures =
- new ArrayList<>();
- for (ReviewDbBatchUpdate u : updates) {
- indexFutures.addAll(u.indexFutures);
- }
- ChangeIndexer.allAsList(indexFutures).get();
-
- for (ReviewDbBatchUpdate u : updates) {
- if (u.batchRefUpdate != null) {
- // Fire ref update events only after all mutations are finished, since
- // callers may assume a patch set ref being created means the change
- // was created, or a branch advancing meaning some changes were
- // closed.
- u.gitRefUpdated.fire(
- u.project,
- u.batchRefUpdate,
- u.getUser().isIdentifiedUser() ? u.getUser().asIdentifiedUser().getAccount() : null);
- }
- }
+ ChangeIndexer.allAsList(
+ updates.stream().flatMap(u -> u.indexFutures.stream()).collect(toList()))
+ .get();
+
+ // Fire ref update events only after all mutations are finished, since callers may assume a
+ // patch set ref being created means the change was created, or a branch advancing meaning
+ // some changes were closed.
+ updates.stream()
+ .filter(u -> u.batchRefUpdate != null)
+ .forEach(
+ u -> u.gitRefUpdated.fire(u.project, u.batchRefUpdate, u.getAccount().orElse(null)));
+
if (!dryrun) {
for (ReviewDbBatchUpdate u : updates) {
u.executePostOps();
}
}
- } catch (UpdateException | RestApiException e) {
- // Propagate REST API exceptions thrown by operations; they commonly throw
- // exceptions like ResourceConflictException to indicate an atomic update
- // failure.
- throw e;
-
- // Convert other common non-REST exception types with user-visible
- // messages to corresponding REST exception types
- } catch (InvalidChangeOperationException e) {
- throw new ResourceConflictException(e.getMessage(), e);
- } catch (NoSuchChangeException | NoSuchRefException | NoSuchProjectException e) {
- throw new ResourceNotFoundException(e.getMessage(), e);
-
} catch (Exception e) {
- Throwables.throwIfUnchecked(e);
- throw new UpdateException(e);
+ wrapAndThrowException(e);
}
}
private final AllUsersName allUsers;
- private final ChangeControl.GenericFactory changeControlFactory;
private final ChangeIndexer indexer;
private final ChangeNotes.Factory changeNotesFactory;
private final ChangeUpdate.Factory changeUpdateFactory;
@@ -380,11 +321,10 @@ class ReviewDbBatchUpdate extends BatchUpdate {
private final List<com.google.common.util.concurrent.CheckedFuture<?, IOException>> indexFutures =
new ArrayList<>();
- @AssistedInject
+ @Inject
ReviewDbBatchUpdate(
@GerritServerConfig Config cfg,
AllUsersName allUsers,
- ChangeControl.GenericFactory changeControlFactory,
ChangeIndexer indexer,
ChangeNotes.Factory changeNotesFactory,
@ChangeUpdateExecutor ListeningExecutorService changeUpdateExector,
@@ -402,7 +342,6 @@ class ReviewDbBatchUpdate extends BatchUpdate {
@Assisted Timestamp when) {
super(repoManager, serverIdent, project, user, when);
this.allUsers = allUsers;
- this.changeControlFactory = changeControlFactory;
this.changeNotesFactory = changeNotesFactory;
this.changeUpdateExector = changeUpdateExector;
this.changeUpdateFactory = changeUpdateFactory;
@@ -417,11 +356,6 @@ class ReviewDbBatchUpdate extends BatchUpdate {
}
@Override
- public void execute() throws UpdateException, RestApiException {
- execute(BatchUpdateListener.NONE);
- }
-
- @Override
public void execute(BatchUpdateListener listener) throws UpdateException, RestApiException {
execute(ImmutableList.of(this), listener, requestId, false);
}
@@ -444,20 +378,18 @@ class ReviewDbBatchUpdate extends BatchUpdate {
op.updateRepo(ctx);
}
- if (onSubmitValidators != null && commands != null && !commands.isEmpty()) {
- try (ObjectReader reader = ctx.getInserter().newReader()) {
- // Validation of refs has to take place here and not at the beginning
- // executeRefUpdates. Otherwise failing validation in a second BatchUpdate object will
- // happen *after* first object's executeRefUpdates has finished, hence after first repo's
- // refs have been updated, which is too late.
- onSubmitValidators.validate(
- project, new ReadOnlyRepository(getRepository()), reader, commands.getCommands());
- }
+ if (onSubmitValidators != null && !getRefUpdates().isEmpty()) {
+ // Validation of refs has to take place here and not at the beginning of executeRefUpdates.
+ // Otherwise, failing validation in a second BatchUpdate object will happen *after* the
+ // first update's executeRefUpdates has finished, hence after first repo's refs have been
+ // updated, which is too late.
+ onSubmitValidators.validate(
+ project, ctx.getRevWalk().getObjectReader(), repoView.getCommands());
}
- if (inserter != null) {
+ if (repoView != null) {
logDebug("Flushing inserter");
- inserter.flush();
+ repoView.getInserter().flush();
} else {
logDebug("No objects to flush");
}
@@ -468,24 +400,31 @@ class ReviewDbBatchUpdate extends BatchUpdate {
}
private void executeRefUpdates(boolean dryrun) throws IOException, RestApiException {
- if (commands == null || commands.isEmpty()) {
+ if (getRefUpdates().isEmpty()) {
logDebug("No ref updates to execute");
return;
}
// May not be opened if the caller added ref updates but no new objects.
+ // TODO(dborowitz): Really?
initRepository();
- batchRefUpdate = repo.getRefDatabase().newBatchUpdate();
+ batchRefUpdate = repoView.getRepository().getRefDatabase().newBatchUpdate();
+ batchRefUpdate.setPushCertificate(pushCert);
batchRefUpdate.setRefLogMessage(refLogMessage, true);
+ batchRefUpdate.setAllowNonFastForwards(true);
+ repoView.getCommands().addTo(batchRefUpdate);
if (user.isIdentifiedUser()) {
batchRefUpdate.setRefLogIdent(user.asIdentifiedUser().newRefLogIdent(when, tz));
}
- commands.addTo(batchRefUpdate);
logDebug("Executing batch of {} ref updates", batchRefUpdate.getCommands().size());
if (dryrun) {
return;
}
- batchRefUpdate.execute(revWalk, NullProgressMonitor.INSTANCE);
+ // Force BatchRefUpdate to read newly referenced objects using a new RevWalk, rather than one
+ // that might have access to unflushed objects.
+ try (RevWalk updateRw = new RevWalk(repoView.getRepository())) {
+ batchRefUpdate.execute(updateRw, NullProgressMonitor.INSTANCE);
+ }
boolean ok = true;
for (ReceiveCommand cmd : batchRefUpdate.getCommands()) {
if (cmd.getResult() != ReceiveCommand.Result.OK) {
@@ -510,11 +449,11 @@ class ReviewDbBatchUpdate extends BatchUpdate {
tasks = new ArrayList<>(ops.keySet().size());
try {
- if (notesMigration.commitChangeWrites() && repo != null) {
+ if (notesMigration.commitChangeWrites() && repoView != null) {
// A NoteDb change may have been rebuilt since the repo was originally
// opened, so make sure we see that.
logDebug("Preemptively scanning for repo changes");
- repo.scanForRepoChanges();
+ repoView.getRepository().scanForRepoChanges();
}
if (!ops.isEmpty() && notesMigration.failChangeWrites()) {
// Fail fast before attempting any writes if changes are read-only, as
@@ -582,9 +521,10 @@ class ReviewDbBatchUpdate extends BatchUpdate {
// updates on the change repo first.
logDebug("Executing NoteDb updates for {} changes", tasks.size());
try {
- BatchRefUpdate changeRefUpdate = getRepository().getRefDatabase().newBatchUpdate();
+ initRepository();
+ BatchRefUpdate changeRefUpdate = repoView.getRepository().getRefDatabase().newBatchUpdate();
boolean hasAllUsersCommands = false;
- try (ObjectInserter ins = getRepository().newObjectInserter()) {
+ try (ObjectInserter ins = repoView.getRepository().newObjectInserter()) {
int objs = 0;
for (ChangeTask task : tasks) {
if (task.noteDbResult == null) {
@@ -691,7 +631,8 @@ class ReviewDbBatchUpdate extends BatchUpdate {
public Void call() throws Exception {
taskId = id.toString() + "-" + Thread.currentThread().getId();
if (Thread.currentThread() == mainThread) {
- Repository repo = getRepository();
+ initRepository();
+ Repository repo = repoView.getRepository();
try (RevWalk rw = new RevWalk(repo)) {
call(ReviewDbBatchUpdate.this.db, repo, rw);
}
@@ -839,8 +780,7 @@ class ReviewDbBatchUpdate extends BatchUpdate {
NoteDbChangeState.checkNotReadOnly(c, skewMs);
}
ChangeNotes notes = changeNotesFactory.createForBatchUpdate(c, !isNew);
- ChangeControl ctl = changeControlFactory.controlFor(notes, user);
- return new ChangeContextImpl(ctl, new BatchUpdateReviewDb(db), repo, rw);
+ return new ChangeContextImpl(notes, new BatchUpdateReviewDb(db), repo, rw);
}
private NoteDbUpdateManager stageNoteDbUpdate(ChangeContextImpl ctx, boolean deleted)
@@ -850,7 +790,10 @@ class ReviewDbBatchUpdate extends BatchUpdate {
updateManagerFactory
.create(ctx.getProject())
.setChangeRepo(
- ctx.getRepository(), ctx.getRevWalk(), null, new ChainedReceiveCommands(repo));
+ ctx.threadLocalRepo,
+ ctx.threadLocalRevWalk,
+ null,
+ new ChainedReceiveCommands(ctx.threadLocalRepo));
if (ctx.getUser().isIdentifiedUser()) {
updateManager.setRefLogIdent(
ctx.getUser().asIdentifiedUser().newRefLogIdent(ctx.getWhen(), tz));