Run /abandon and /restore in a single database operation
On the backend behind gerrit-review each database call is expensive, unless we do something crazy like wrap a block of database operations into a single mock transaction with the beginTransaction/commit idiom. Simplify both of these tasks by getting rid of the ugly helper function from ChangeUtil and explicitly perform all database operations before sending email to interested parties. Change-Id: I63cbbc7ac47d20f202b042740abb204e040e7c65
This commit is contained in:
@@ -48,7 +48,7 @@ public class ApprovalsUtil {
|
||||
private final ApprovalTypes approvalTypes;
|
||||
|
||||
@Inject
|
||||
ApprovalsUtil(ReviewDb db, ApprovalTypes approvalTypes) {
|
||||
public ApprovalsUtil(ReviewDb db, ApprovalTypes approvalTypes) {
|
||||
this.db = db;
|
||||
this.approvalTypes = approvalTypes;
|
||||
}
|
||||
|
@@ -30,7 +30,6 @@ import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.git.MergeOp;
|
||||
import com.google.gerrit.server.mail.EmailException;
|
||||
import com.google.gerrit.server.mail.ReplyToChangeSender;
|
||||
import com.google.gerrit.server.mail.RevertedSender;
|
||||
import com.google.gerrit.server.patch.PatchSetInfoFactory;
|
||||
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
|
||||
@@ -56,8 +55,6 @@ import org.eclipse.jgit.revwalk.RevWalk;
|
||||
import org.eclipse.jgit.util.Base64;
|
||||
import org.eclipse.jgit.util.ChangeIdUtil;
|
||||
import org.eclipse.jgit.util.NB;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.Timestamp;
|
||||
@@ -70,8 +67,6 @@ import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
public class ChangeUtil {
|
||||
private static final Logger log = LoggerFactory.getLogger(ChangeUtil.class);
|
||||
|
||||
private static int uuidPrefix;
|
||||
private static int uuidSeq;
|
||||
|
||||
@@ -513,25 +508,6 @@ public class ChangeUtil {
|
||||
db.patchSets().delete(Collections.singleton(patch));
|
||||
}
|
||||
|
||||
public static <T extends ReplyToChangeSender> void updatedChange(
|
||||
final ReviewDb db, final IdentifiedUser user, final Change change,
|
||||
final ChangeMessage cmsg, ReplyToChangeSender.Factory<T> senderFactory)
|
||||
throws OrmException {
|
||||
db.changeMessages().insert(Collections.singleton(cmsg));
|
||||
|
||||
new ApprovalsUtil(db, null).syncChangeStatus(change);
|
||||
|
||||
// Email the reviewers
|
||||
try {
|
||||
final ReplyToChangeSender cm = senderFactory.create(change);
|
||||
cm.setFrom(user.getAccountId());
|
||||
cm.setChangeMessage(cmsg);
|
||||
cm.send();
|
||||
} catch (Exception e) {
|
||||
log.error("Cannot email update for change " + change.getChangeId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public static String sortKey(long lastUpdated, int id){
|
||||
// The encoding uses minutes since Wed Oct 1 00:00:00 2008 UTC.
|
||||
// We overrun approximately 4,085 years later, so ~6093.
|
||||
|
@@ -23,18 +23,27 @@ import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||
import com.google.gerrit.extensions.restapi.RestModifyView;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.reviewdb.client.ChangeMessage;
|
||||
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.ApprovalsUtil;
|
||||
import com.google.gerrit.server.ChangeUtil;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.change.Abandon.Input;
|
||||
import com.google.gerrit.server.mail.AbandonedSender;
|
||||
import com.google.gerrit.server.mail.ReplyToChangeSender;
|
||||
import com.google.gerrit.server.project.ChangeControl;
|
||||
import com.google.gwtorm.server.AtomicUpdate;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
public class Abandon implements RestModifyView<ChangeResource, Input> {
|
||||
private static final Logger log = LoggerFactory.getLogger(Abandon.class);
|
||||
|
||||
private final ChangeHooks hooks;
|
||||
private final AbandonedSender.Factory abandonedSenderFactory;
|
||||
private final Provider<ReviewDb> dbProvider;
|
||||
@@ -66,6 +75,7 @@ public class Abandon implements RestModifyView<ChangeResource, Input> {
|
||||
throws BadRequestException, AuthException,
|
||||
ResourceConflictException, Exception {
|
||||
ChangeControl control = req.getControl();
|
||||
IdentifiedUser caller = (IdentifiedUser) control.getCurrentUser();
|
||||
Change change = req.getChange();
|
||||
if (!control.canAbandon()) {
|
||||
throw new AuthException("abandon not permitted");
|
||||
@@ -73,24 +83,11 @@ public class Abandon implements RestModifyView<ChangeResource, Input> {
|
||||
throw new ResourceConflictException("change is " + status(change));
|
||||
}
|
||||
|
||||
// Create a message to accompany the abandoned change
|
||||
ChangeMessage message;
|
||||
ReviewDb db = dbProvider.get();
|
||||
PatchSet.Id patchSetId = change.currentPatchSetId();
|
||||
IdentifiedUser currentUser = (IdentifiedUser) control.getCurrentUser();
|
||||
String message = Strings.emptyToNull(input.message);
|
||||
ChangeMessage cmsg = new ChangeMessage(
|
||||
new ChangeMessage.Key(change.getId(), ChangeUtil.messageUUID(db)),
|
||||
currentUser.getAccountId(), patchSetId);
|
||||
StringBuilder msg = new StringBuilder();
|
||||
msg.append(String.format("Patch Set %d: Abandoned", patchSetId.get()));
|
||||
if (message != null) {
|
||||
msg.append("\n\n");
|
||||
msg.append(message);
|
||||
}
|
||||
cmsg.setMessage(msg.toString());
|
||||
|
||||
// Abandon the change
|
||||
Change updatedChange = db.changes().atomicUpdate(
|
||||
db.changes().beginTransaction(change.getId());
|
||||
try {
|
||||
change = db.changes().atomicUpdate(
|
||||
change.getId(),
|
||||
new AtomicUpdate<Change>() {
|
||||
@Override
|
||||
@@ -103,16 +100,51 @@ public class Abandon implements RestModifyView<ChangeResource, Input> {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
if (updatedChange == null) {
|
||||
if (change == null) {
|
||||
throw new ResourceConflictException("change is "
|
||||
+ status(db.changes().get(change.getId())));
|
||||
+ status(db.changes().get(req.getChange().getId())));
|
||||
}
|
||||
message = newMessage(input, caller, change);
|
||||
db.changeMessages().insert(Collections.singleton(message));
|
||||
new ApprovalsUtil(db, null).syncChangeStatus(change);
|
||||
db.commit();
|
||||
} finally {
|
||||
db.rollback();
|
||||
}
|
||||
|
||||
ChangeUtil.updatedChange(db, currentUser, updatedChange, cmsg,
|
||||
abandonedSenderFactory);
|
||||
hooks.doChangeAbandonedHook(updatedChange, currentUser.getAccount(),
|
||||
message, db);
|
||||
return json.format(change.getId());
|
||||
try {
|
||||
ReplyToChangeSender cm = abandonedSenderFactory.create(change);
|
||||
cm.setFrom(caller.getAccountId());
|
||||
cm.setChangeMessage(message);
|
||||
cm.send();
|
||||
} catch (Exception e) {
|
||||
log.error("Cannot email update for change " + change.getChangeId(), e);
|
||||
}
|
||||
hooks.doChangeAbandonedHook(change,
|
||||
caller.getAccount(),
|
||||
Strings.emptyToNull(input.message),
|
||||
db);
|
||||
return json.format(change);
|
||||
}
|
||||
|
||||
private ChangeMessage newMessage(Input input, IdentifiedUser caller,
|
||||
Change change) throws OrmException {
|
||||
StringBuilder msg = new StringBuilder();
|
||||
msg.append("Abandoned");
|
||||
if (!Strings.nullToEmpty(input.message).trim().isEmpty()) {
|
||||
msg.append("\n\n");
|
||||
msg.append(input.message.trim());
|
||||
}
|
||||
|
||||
ChangeMessage message = new ChangeMessage(
|
||||
new ChangeMessage.Key(
|
||||
change.getId(),
|
||||
ChangeUtil.messageUUID(dbProvider.get())),
|
||||
caller.getAccountId(),
|
||||
change.getLastUpdatedOn(),
|
||||
change.currentPatchSetId());
|
||||
message.setMessage(msg.toString());
|
||||
return message;
|
||||
}
|
||||
|
||||
private static String status(Change change) {
|
||||
|
@@ -23,18 +23,27 @@ import com.google.gerrit.extensions.restapi.RestModifyView;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.reviewdb.client.Change.Status;
|
||||
import com.google.gerrit.reviewdb.client.ChangeMessage;
|
||||
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.ApprovalsUtil;
|
||||
import com.google.gerrit.server.ChangeUtil;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.change.Restore.Input;
|
||||
import com.google.gerrit.server.mail.ReplyToChangeSender;
|
||||
import com.google.gerrit.server.mail.RestoredSender;
|
||||
import com.google.gerrit.server.project.ChangeControl;
|
||||
import com.google.gwtorm.server.AtomicUpdate;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
public class Restore implements RestModifyView<ChangeResource, Input> {
|
||||
private static final Logger log = LoggerFactory.getLogger(Restore.class);
|
||||
|
||||
private final ChangeHooks hooks;
|
||||
private final RestoredSender.Factory restoredSenderFactory;
|
||||
private final Provider<ReviewDb> dbProvider;
|
||||
@@ -65,6 +74,7 @@ public class Restore implements RestModifyView<ChangeResource, Input> {
|
||||
public Object apply(ChangeResource req, Input input)
|
||||
throws Exception {
|
||||
ChangeControl control = req.getControl();
|
||||
IdentifiedUser caller = (IdentifiedUser) control.getCurrentUser();
|
||||
Change change = req.getChange();
|
||||
if (!control.canRestore()) {
|
||||
throw new AuthException("restore not permitted");
|
||||
@@ -72,24 +82,11 @@ public class Restore implements RestModifyView<ChangeResource, Input> {
|
||||
throw new ResourceConflictException("change is " + status(change));
|
||||
}
|
||||
|
||||
// Create a message to accompany the restore change
|
||||
ChangeMessage message;
|
||||
ReviewDb db = dbProvider.get();
|
||||
PatchSet.Id patchSetId = change.currentPatchSetId();
|
||||
IdentifiedUser currentUser = (IdentifiedUser) control.getCurrentUser();
|
||||
String message = Strings.emptyToNull(input.message);
|
||||
final ChangeMessage cmsg = new ChangeMessage(
|
||||
new ChangeMessage.Key(change.getId(), ChangeUtil.messageUUID(db)),
|
||||
currentUser.getAccountId(), patchSetId);
|
||||
StringBuilder msg = new StringBuilder();
|
||||
msg.append(String.format("Patch Set %d: Restored", patchSetId.get()));
|
||||
if (message != null) {
|
||||
msg.append("\n\n");
|
||||
msg.append(message);
|
||||
}
|
||||
cmsg.setMessage(msg.toString());
|
||||
|
||||
// Restore the change
|
||||
final Change updatedChange = db.changes().atomicUpdate(
|
||||
db.changes().beginTransaction(change.getId());
|
||||
try {
|
||||
change = db.changes().atomicUpdate(
|
||||
change.getId(),
|
||||
new AtomicUpdate<Change>() {
|
||||
@Override
|
||||
@@ -102,16 +99,51 @@ public class Restore implements RestModifyView<ChangeResource, Input> {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
if (updatedChange == null) {
|
||||
if (change == null) {
|
||||
throw new ResourceConflictException("change is "
|
||||
+ status(db.changes().get(change.getId())));
|
||||
+ status(db.changes().get(req.getChange().getId())));
|
||||
}
|
||||
message = newMessage(input, caller, change);
|
||||
db.changeMessages().insert(Collections.singleton(message));
|
||||
new ApprovalsUtil(db, null).syncChangeStatus(change);
|
||||
db.commit();
|
||||
} finally {
|
||||
db.rollback();
|
||||
}
|
||||
|
||||
ChangeUtil.updatedChange(db, currentUser, updatedChange, cmsg,
|
||||
restoredSenderFactory);
|
||||
hooks.doChangeRestoredHook(updatedChange, currentUser.getAccount(),
|
||||
message, db);
|
||||
return json.format(change.getId());
|
||||
try {
|
||||
ReplyToChangeSender cm = restoredSenderFactory.create(change);
|
||||
cm.setFrom(caller.getAccountId());
|
||||
cm.setChangeMessage(message);
|
||||
cm.send();
|
||||
} catch (Exception e) {
|
||||
log.error("Cannot email update for change " + change.getChangeId(), e);
|
||||
}
|
||||
hooks.doChangeRestoredHook(change,
|
||||
caller.getAccount(),
|
||||
Strings.emptyToNull(input.message),
|
||||
dbProvider.get());
|
||||
return json.format(change);
|
||||
}
|
||||
|
||||
private ChangeMessage newMessage(Input input, IdentifiedUser caller,
|
||||
Change change) throws OrmException {
|
||||
StringBuilder msg = new StringBuilder();
|
||||
msg.append("Restored");
|
||||
if (!Strings.nullToEmpty(input.message).trim().isEmpty()) {
|
||||
msg.append("\n\n");
|
||||
msg.append(input.message.trim());
|
||||
}
|
||||
|
||||
ChangeMessage message = new ChangeMessage(
|
||||
new ChangeMessage.Key(
|
||||
change.getId(),
|
||||
ChangeUtil.messageUUID(dbProvider.get())),
|
||||
caller.getAccountId(),
|
||||
change.getLastUpdatedOn(),
|
||||
change.currentPatchSetId());
|
||||
message.setMessage(msg.toString());
|
||||
return message;
|
||||
}
|
||||
|
||||
private static String status(Change change) {
|
||||
|
Reference in New Issue
Block a user