Replace most ChangeAccess queries with searches

Use the secondary index rather than depending on SQL indexes, which
will not be available in a notedb world. For the most part this change
is pretty straightforward, as the interface for InternalChangeQuery is
almost the same as for ChangeAccess. It also provides a bit more
functionality, such as limiting the results.

By default, all of these queries should bypass visibility checks,
since things like the submit queue necessarily need to see everything.
Even if it is properly running as InternalUser and that is hard-coded
to have visibility, it is simpler to just bypass the checks entirely.

The biggest complication comes from converting callers that previously
opened a new ReviewDb in a try/finally block and called a ChangeAccess
method directly from that. In the InternalChangeQuery model,
Provider<ReviewDb> needs to be injected, so we need to set the
ThreadLocalRequestContext with the manually-opened DB. To simplify
this code, add an AutoCloseable RequestContext implementation.

To save future schema churn, this change does not include a schema
change to drop the indexes.

Change-Id: I99de8a2cf2aba01971059b89df33b1676cd8546e
This commit is contained in:
Dave Borowitz
2014-12-23 12:25:37 -08:00
parent 3cbb9d94b0
commit dfc07f63bb
22 changed files with 400 additions and 234 deletions

View File

@@ -40,6 +40,7 @@ import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.index.ChangeIndexer;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.project.PutConfig;
import com.google.gson.reflect.TypeToken;
@@ -73,6 +74,8 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
@Inject
private ApprovalsUtil approvalsUtil;
@Inject
private ChangeIndexer indexer;
@Before
public void setUp() throws Exception {
@@ -166,6 +169,7 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
PatchSetApproval.LabelId.SUBMIT),
(short) 1,
new Timestamp(System.currentTimeMillis()))));
indexer.index(db, c);
}
private void submit(String changeId, int expectedStatus) throws IOException {

View File

@@ -14,9 +14,7 @@
package com.google.gerrit.reviewdb.server;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gwtorm.server.Access;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.PrimaryKey;
@@ -35,26 +33,6 @@ public interface ChangeAccess extends Access<Change, Change.Id> {
ResultSet<Change> byKeyRange(Change.Key reva, Change.Key revb)
throws OrmException;
@Query("WHERE dest = ? AND changeKey = ?")
ResultSet<Change> byBranchKey(Branch.NameKey p, Change.Key key)
throws OrmException;
@Query("WHERE dest.projectName = ?")
ResultSet<Change> byProject(Project.NameKey p) throws OrmException;
@Query("WHERE dest = ? AND status = '" + Change.STATUS_SUBMITTED
+ "' ORDER BY lastUpdatedOn")
ResultSet<Change> submitted(Branch.NameKey dest) throws OrmException;
@Query("WHERE status = '" + Change.STATUS_SUBMITTED + "'")
ResultSet<Change> allSubmitted() throws OrmException;
@Query("WHERE open = true AND dest.projectName = ?")
ResultSet<Change> byProjectOpenAll(Project.NameKey p) throws OrmException;
@Query("WHERE open = true AND dest = ?")
ResultSet<Change> byBranchOpenAll(Branch.NameKey p) throws OrmException;
@Query
ResultSet<Change> all() throws OrmException;
}

View File

@@ -69,18 +69,6 @@ ON account_project_watches (project_name);
-- *********************************************************************
-- ChangeAccess
-- covers: submitted, allSubmitted
CREATE INDEX changes_submitted
ON changes (status, dest_project_name, dest_branch_name, last_updated_on);
-- covers: byProjectOpenAll
CREATE INDEX changes_byProjectOpen
ON changes (open, dest_project_name, last_updated_on);
-- covers: byProject
CREATE INDEX changes_byProject
ON changes (dest_project_name);
CREATE INDEX changes_key
ON changes (change_key);

View File

@@ -76,21 +76,6 @@ ON account_project_watches (project_name)
-- *********************************************************************
-- ChangeAccess
-- covers: submitted, allSubmitted
CREATE INDEX changes_submitted
ON changes (status, dest_project_name, dest_branch_name, last_updated_on)
#
-- covers: byProjectOpenPrev, byProjectOpenNext
CREATE INDEX changes_byProjectOpen
ON changes (open, dest_project_name, last_updated_on);
#
-- covers: byProject
CREATE INDEX changes_byProject
ON changes (dest_project_name)
#
CREATE INDEX changes_key
ON changes (change_key)
#

View File

@@ -117,20 +117,6 @@ ON account_project_watches (project_name);
-- *********************************************************************
-- ChangeAccess
-- covers: submitted, allSubmitted
CREATE INDEX changes_submitted
ON changes (dest_project_name, dest_branch_name, last_updated_on)
WHERE status = 's';
-- covers: byProjectOpenAll
CREATE INDEX changes_byProjectOpen
ON changes (dest_project_name, last_updated_on)
WHERE open = 'Y';
-- covers: byProject
CREATE INDEX changes_byProject
ON changes (dest_project_name);
CREATE INDEX changes_key
ON changes (change_key);

View File

@@ -15,6 +15,7 @@
package com.google.gerrit.server;
import static com.google.gerrit.server.change.PatchSetInserter.ValidatePolicy.RECEIVE_COMMITS;
import static com.google.gerrit.server.query.change.ChangeData.asChanges;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
@@ -42,6 +43,7 @@ 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.RefControl;
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gerrit.server.ssh.SshInfo;
import com.google.gerrit.server.util.IdGenerator;
import com.google.gerrit.server.util.MagicBranch;
@@ -182,6 +184,7 @@ public class ChangeUtil {
private final Provider<CurrentUser> userProvider;
private final CommitValidators.Factory commitValidatorsFactory;
private final Provider<ReviewDb> db;
private final Provider<InternalChangeQuery> queryProvider;
private final RevertedSender.Factory revertedSenderFactory;
private final ChangeInserter.Factory changeInserterFactory;
private final PatchSetInserter.Factory patchSetInserterFactory;
@@ -193,6 +196,7 @@ public class ChangeUtil {
ChangeUtil(Provider<CurrentUser> userProvider,
CommitValidators.Factory commitValidatorsFactory,
Provider<ReviewDb> db,
Provider<InternalChangeQuery> queryProvider,
RevertedSender.Factory revertedSenderFactory,
ChangeInserter.Factory changeInserterFactory,
PatchSetInserter.Factory patchSetInserterFactory,
@@ -202,6 +206,7 @@ public class ChangeUtil {
this.userProvider = userProvider;
this.commitValidatorsFactory = commitValidatorsFactory;
this.db = db;
this.queryProvider = queryProvider;
this.revertedSenderFactory = revertedSenderFactory;
this.changeInserterFactory = changeInserterFactory;
this.patchSetInserterFactory = patchSetInserterFactory;
@@ -532,9 +537,9 @@ public class ChangeUtil {
// Try change triplet
Optional<ChangeTriplet> triplet = ChangeTriplet.parse(id);
if (triplet.isPresent()) {
return db.get().changes().byBranchKey(
return asChanges(queryProvider.get().byBranchKey(
triplet.get().branch(),
triplet.get().id()).toList();
triplet.get().id()));
}
throw new ResourceNotFoundException(id);

View File

@@ -17,9 +17,11 @@ package com.google.gerrit.server.args4j;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
import org.kohsuke.args4j.CmdLineException;
@@ -30,17 +32,16 @@ import org.kohsuke.args4j.spi.Parameters;
import org.kohsuke.args4j.spi.Setter;
public class ChangeIdHandler extends OptionHandler<Change.Id> {
@Inject
private ReviewDb db;
private final Provider<InternalChangeQuery> queryProvider;
@Inject
public ChangeIdHandler(
final ReviewDb db,
// TODO(dborowitz): Not sure whether this is injectable here.
Provider<InternalChangeQuery> queryProvider,
@Assisted final CmdLineParser parser, @Assisted final OptionDef option,
@Assisted final Setter<Change.Id> setter) {
super(parser, option, setter);
this.db = db;
this.queryProvider = queryProvider;
}
@Override
@@ -58,8 +59,8 @@ public class ChangeIdHandler extends OptionHandler<Change.Id> {
final Project.NameKey project = new Project.NameKey(tokens[0]);
final Branch.NameKey branch =
new Branch.NameKey(project, "refs/heads/" + tokens[1]);
for (final Change change : db.changes().byBranchKey(branch, key)) {
setter.addValue(change.getId());
for (final ChangeData cd : queryProvider.get().byBranchKey(branch, key)) {
setter.addValue(cd.getId());
return 1;
}
} catch (IllegalArgumentException e) {

View File

@@ -39,6 +39,8 @@ import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.RefControl;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gerrit.server.ssh.NoSshInfo;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
@@ -67,6 +69,7 @@ import java.util.TimeZone;
public class CherryPickChange {
private final Provider<ReviewDb> db;
private final Provider<InternalChangeQuery> queryProvider;
private final GitRepositoryManager gitManager;
private final TimeZone serverTimeZone;
private final Provider<CurrentUser> currentUser;
@@ -76,15 +79,17 @@ public class CherryPickChange {
final MergeUtil.Factory mergeUtilFactory;
@Inject
CherryPickChange(final Provider<ReviewDb> db,
@GerritPersonIdent final PersonIdent myIdent,
final GitRepositoryManager gitManager,
final Provider<CurrentUser> currentUser,
final CommitValidators.Factory commitValidatorsFactory,
final ChangeInserter.Factory changeInserterFactory,
final PatchSetInserter.Factory patchSetInserterFactory,
final MergeUtil.Factory mergeUtilFactory) {
CherryPickChange(Provider<ReviewDb> db,
Provider<InternalChangeQuery> queryProvider,
@GerritPersonIdent PersonIdent myIdent,
GitRepositoryManager gitManager,
Provider<CurrentUser> currentUser,
CommitValidators.Factory commitValidatorsFactory,
ChangeInserter.Factory changeInserterFactory,
PatchSetInserter.Factory patchSetInserterFactory,
MergeUtil.Factory mergeUtilFactory) {
this.db = db;
this.queryProvider = queryProvider;
this.gitManager = gitManager;
this.serverTimeZone = myIdent.getTimeZone();
this.currentUser = currentUser;
@@ -163,11 +168,11 @@ public class CherryPickChange {
changeKey = new Change.Key("I" + computedChangeId.name());
}
List<Change> destChanges =
db.get().changes()
.byBranchKey(new Branch.NameKey(project, destRef.getName()),
changeKey).toList();
Branch.NameKey newDest =
new Branch.NameKey(change.getProject(), destRef.getName());
List<ChangeData> destChanges = queryProvider.get()
.setLimit(2)
.byBranchKey(newDest, changeKey);
if (destChanges.size() > 1) {
throw new InvalidChangeOperationException("Several changes with key "
+ changeKey + " reside on the same branch. "
@@ -175,7 +180,7 @@ public class CherryPickChange {
} else if (destChanges.size() == 1) {
// The change key exists on the destination branch. The cherry pick
// will be added as a new patch set.
return insertPatchSet(git, revWalk, destChanges.get(0),
return insertPatchSet(git, revWalk, destChanges.get(0).change(),
cherryPickCommit, refControl, identifiedUser);
} else {
// Change key not found on destination branch. We can create a new

View File

@@ -32,6 +32,8 @@ import com.google.gerrit.server.CommonConverters;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.ResultSet;
import com.google.inject.Inject;
@@ -64,11 +66,15 @@ public class GetRelated implements RestReadView<RevisionResource> {
private final GitRepositoryManager gitMgr;
private final Provider<ReviewDb> dbProvider;
private final Provider<InternalChangeQuery> queryProvider;
@Inject
GetRelated(GitRepositoryManager gitMgr, Provider<ReviewDb> db) {
GetRelated(GitRepositoryManager gitMgr,
Provider<ReviewDb> db,
Provider<InternalChangeQuery> queryProvider) {
this.gitMgr = gitMgr;
this.dbProvider = db;
this.queryProvider = queryProvider;
}
@Override
@@ -92,8 +98,8 @@ public class GetRelated implements RestReadView<RevisionResource> {
private List<ChangeAndCommit> walk(RevisionResource rsrc, RevWalk rw, Ref ref)
throws OrmException, IOException {
Map<Change.Id, Change> changes = allOpenChanges(rsrc);
Map<PatchSet.Id, PatchSet> patchSets = allPatchSets(rsrc, changes.keySet());
Map<Change.Id, ChangeData> changes = allOpenChanges(rsrc);
Map<PatchSet.Id, PatchSet> patchSets = allPatchSets(rsrc, changes.values());
Map<String, PatchSet> commits = Maps.newHashMap();
for (PatchSet p : patchSets.values()) {
@@ -119,7 +125,7 @@ public class GetRelated implements RestReadView<RevisionResource> {
PatchSet p = commits.get(c.name());
Change g = null;
if (p != null) {
g = changes.get(p.getId().getParentKey());
g = changes.get(p.getId().getParentKey()).change();
added.add(p.getId().getParentKey());
} else {
// check if there is a merged or abandoned change for this commit
@@ -148,25 +154,18 @@ public class GetRelated implements RestReadView<RevisionResource> {
return list;
}
private Map<Change.Id, Change> allOpenChanges(RevisionResource rsrc)
private Map<Change.Id, ChangeData> allOpenChanges(RevisionResource rsrc)
throws OrmException {
ReviewDb db = dbProvider.get();
return db.changes().toMap(
db.changes().byBranchOpenAll(rsrc.getChange().getDest()));
return ChangeData.asMap(
queryProvider.get().byBranchOpen(rsrc.getChange().getDest()));
}
private Map<PatchSet.Id, PatchSet> allPatchSets(RevisionResource rsrc,
Collection<Change.Id> ids) throws OrmException {
int n = ids.size();
ReviewDb db = dbProvider.get();
List<ResultSet<PatchSet>> t = Lists.newArrayListWithCapacity(n);
for (Change.Id id : ids) {
t.add(db.patchSets().byChange(id));
}
Map<PatchSet.Id, PatchSet> r = Maps.newHashMapWithExpectedSize(n * 2);
for (ResultSet<PatchSet> rs : t) {
for (PatchSet p : rs) {
Collection<ChangeData> cds) throws OrmException {
Map<PatchSet.Id, PatchSet> r =
Maps.newHashMapWithExpectedSize(cds.size() * 2);
for (ChangeData cd : cds) {
for (PatchSet p : cd.patches()) {
r.put(p.getId(), p);
}
}
@@ -178,7 +177,7 @@ public class GetRelated implements RestReadView<RevisionResource> {
}
private List<ChangeAndCommit> children(RevisionResource rsrc, RevWalk rw,
Map<Change.Id, Change> changes, Map<PatchSet.Id, PatchSet> patchSets,
Map<Change.Id, ChangeData> changes, Map<PatchSet.Id, PatchSet> patchSets,
Set<Change.Id> added)
throws OrmException, IOException {
// children is a map of parent commit name to PatchSet built on it.
@@ -205,9 +204,9 @@ public class GetRelated implements RestReadView<RevisionResource> {
}
for (Map.Entry<Change.Id, PatchSet.Id> e : matches.entrySet()) {
Change change = changes.get(e.getKey());
ChangeData cd = changes.get(e.getKey());
PatchSet ps = patchSets.get(e.getValue());
if (change == null || ps == null || !seenChange.add(e.getKey())) {
if (cd == null || ps == null || !seenChange.add(e.getKey())) {
continue;
}
@@ -218,7 +217,7 @@ public class GetRelated implements RestReadView<RevisionResource> {
q.addFirst(ps.getRevision().get());
if (added.add(ps.getId().getParentKey())) {
rw.parseBody(c);
graph.add(new ChangeAndCommit(change, ps, c));
graph.add(new ChangeAndCommit(cd.change(), ps, c));
}
}
}
@@ -228,13 +227,15 @@ public class GetRelated implements RestReadView<RevisionResource> {
}
private boolean isVisible(ProjectControl projectCtl,
Map<Change.Id, Change> changes,
Map<Change.Id, ChangeData> changes,
Map<PatchSet.Id, PatchSet> patchSets,
PatchSet.Id psId) throws OrmException {
Change c = changes.get(psId.getParentKey());
ChangeData cd = changes.get(psId.getParentKey());
PatchSet ps = patchSets.get(psId);
if (c != null && ps != null) {
ChangeControl ctl = projectCtl.controlFor(c);
if (cd != null && ps != null) {
// Related changes are in the same project, so reuse the existing
// ProjectControl.
ChangeControl ctl = projectCtl.controlFor(cd.change());
return ctl.isVisible(dbProvider.get())
&& ctl.isPatchVisible(ps, dbProvider.get());
}

View File

@@ -20,11 +20,13 @@ import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gwtorm.server.SchemaFactory;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gerrit.server.util.OneOffRequestContext;
import com.google.inject.Inject;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Named;
@@ -79,20 +81,21 @@ public class ChangeCache implements GitReferenceUpdatedListener {
}
static class Loader extends CacheLoader<Project.NameKey, List<Change>> {
private final SchemaFactory<ReviewDb> schema;
private final OneOffRequestContext requestContext;
private final Provider<InternalChangeQuery> queryProvider;
@Inject
Loader(SchemaFactory<ReviewDb> schema) {
this.schema = schema;
Loader(OneOffRequestContext requestContext,
Provider<InternalChangeQuery> queryProvider) {
this.requestContext = requestContext;
this.queryProvider = queryProvider;
}
@Override
public List<Change> load(Project.NameKey key) throws Exception {
final ReviewDb db = schema.open();
try {
return Collections.unmodifiableList(db.changes().byProject(key).toList());
} finally {
db.close();
try (AutoCloseable ctx = requestContext.open()) {
return Collections.unmodifiableList(
ChangeData.asChanges(queryProvider.get().byProject(key)));
}
}
}

View File

@@ -65,11 +65,13 @@ import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.SubmitRuleEvaluator;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gerrit.server.util.RequestScopePropagator;
import com.google.gwtorm.server.AtomicUpdate;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
@@ -148,6 +150,7 @@ public class MergeOp {
private final MergeValidators.Factory mergeValidatorsFactory;
private final PatchSetInfoFactory patchSetInfoFactory;
private final ProjectCache projectCache;
private final Provider<InternalChangeQuery> queryProvider;
private final RequestScopePropagator requestScopePropagator;
private final SchemaFactory<ReviewDb> schemaFactory;
private final SubmitStrategyFactory submitStrategyFactory;
@@ -191,6 +194,7 @@ public class MergeOp {
MergeValidators.Factory mergeValidatorsFactory,
PatchSetInfoFactory patchSetInfoFactory,
ProjectCache projectCache,
Provider<InternalChangeQuery> queryProvider,
RequestScopePropagator requestScopePropagator,
SchemaFactory<ReviewDb> schemaFactory,
SubmitStrategyFactory submitStrategyFactory,
@@ -216,6 +220,7 @@ public class MergeOp {
this.mergeValidatorsFactory = mergeValidatorsFactory;
this.patchSetInfoFactory = patchSetInfoFactory;
this.projectCache = projectCache;
this.queryProvider = queryProvider;
this.requestScopePropagator = requestScopePropagator;
this.schemaFactory = schemaFactory;
this.submitStrategyFactory = submitStrategyFactory;
@@ -256,7 +261,7 @@ public class MergeOp {
boolean reopen = false;
ListMultimap<SubmitType, Change> toSubmit =
validateChangeList(db.changes().submitted(destBranch).toList());
validateChangeList(queryProvider.get().submitted(destBranch));
ListMultimap<SubmitType, CodeReviewCommit> toMergeNextTurn =
ArrayListMultimap.create();
List<CodeReviewCommit> potentiallyStillSubmittableOnNextRun =
@@ -444,9 +449,14 @@ public class MergeOp {
branchTip = null;
branchUpdate.setExpectedOldObjectId(ObjectId.zeroId());
} else {
for (Change c : db.changes().submitted(destBranch).toList()) {
for (ChangeData cd : queryProvider.get().submitted(destBranch)) {
try {
Change c = cd.change();
setNew(c, message(c, "Your change could not be merged, "
+ "because the destination branch does not exist anymore."));
} catch (OrmException e) {
log.error("Error setting change new", e);
}
}
}
return branchUpdate;
@@ -481,7 +491,7 @@ public class MergeOp {
}
private ListMultimap<SubmitType, Change> validateChangeList(
List<Change> submitted) throws MergeException {
List<ChangeData> submitted) throws MergeException {
ListMultimap<SubmitType, Change> toSubmit = ArrayListMultimap.create();
Map<String, Ref> allRefs;
@@ -496,15 +506,16 @@ public class MergeOp {
tips.add(r.getObjectId());
}
for (Change chg : submitted) {
for (ChangeData cd : submitted) {
ChangeControl ctl;
Change chg;
try {
ctl = changeControlFactory.controlFor(chg,
identifiedUserFactory.create(chg.getOwner()));
} catch (NoSuchChangeException e) {
ctl = cd.changeControl();
chg = cd.change();
} catch (OrmException e) {
throw new MergeException("Failed to validate changes", e);
}
Change.Id changeId = chg.getId();
Change.Id changeId = cd.getId();
if (chg.currentPatchSetId() == null) {
logError("Missing current patch set on change " + changeId);
commits.put(changeId, CodeReviewCommit.noPatchSet(ctl));
@@ -514,7 +525,7 @@ public class MergeOp {
PatchSet ps;
try {
ps = db.patchSets().get(chg.currentPatchSetId());
ps = cd.currentPatchSet();
} catch (OrmException e) {
throw new MergeException("Cannot query the database", e);
}
@@ -565,6 +576,7 @@ public class MergeOp {
continue;
}
// TODO(dborowitz): Consider putting ChangeData in CodeReviewCommit.
commit.setControl(ctl);
commit.setPatchsetId(ps.getId());
commits.put(changeId, commit);
@@ -733,7 +745,7 @@ public class MergeOp {
private void updateChangeStatus(List<Change> submitted)
throws NoSuchChangeException {
logDebug("Updating change status for {} changes", submitted);
logDebug("Updating change status for {} changes", submitted.size());
for (Change c : submitted) {
CodeReviewCommit commit = commits.get(c.getId());
CommitMergeStatus s = commit != null ? commit.getStatusCode() : null;
@@ -747,7 +759,8 @@ public class MergeOp {
}
String txt = s.getMessage();
logDebug("Status of change {} on {}: {}", c.getId(), c.getDest(), s);
logDebug("Status of change {} ({}) on {}: {}", c.getId(), commit.name(),
c.getDest(), s);
try {
switch (s) {
@@ -1207,9 +1220,9 @@ public class MergeOp {
Exception err = null;
try {
openSchema();
for (Change c
: db.changes().byProjectOpenAll(destBranch.getParentKey())) {
abandonOneChange(c);
for (ChangeData cd
: queryProvider.get().byProjectOpen(destBranch.getParentKey())) {
abandonOneChange(cd.change());
}
db.close();
db = null;

View File

@@ -272,6 +272,7 @@ public class ReceiveCommits {
private final IdentifiedUser currentUser;
private final ReviewDb db;
private final Provider<InternalChangeQuery> queryProvider;
private final ChangeData.Factory changeDataFactory;
private final ChangeUpdate.Factory updateFactory;
private final SchemaFactory<ReviewDb> schemaFactory;
@@ -377,6 +378,7 @@ public class ReceiveCommits {
final NotesMigration notesMigration) throws IOException {
this.currentUser = (IdentifiedUser) projectControl.getCurrentUser();
this.db = db;
this.queryProvider = queryProvider;
this.changeDataFactory = changeDataFactory;
this.updateFactory = updateFactory;
this.schemaFactory = schemaFactory;
@@ -1523,7 +1525,7 @@ public class ReceiveCommits {
return;
}
List<Change> changes = p.destChanges.toList();
List<ChangeData> changes = p.destChanges;
if (changes.size() > 1) {
// WTF, multiple changes in this project have the same key?
// Since the commit is new, the user should recreate it with
@@ -1538,7 +1540,8 @@ public class ReceiveCommits {
if (changes.size() == 1) {
// Schedule as a replacement to this one matching change.
//
if (requestReplace(magicBranch.cmd, false, changes.get(0), p.commit)) {
if (requestReplace(
magicBranch.cmd, false, changes.get(0).change(), p.commit)) {
continue;
} else {
newChanges = Collections.emptyList();
@@ -1602,12 +1605,12 @@ public class ReceiveCommits {
private class ChangeLookup {
final RevCommit commit;
final Change.Key changeKey;
final ResultSet<Change> destChanges;
final List<ChangeData> destChanges;
ChangeLookup(RevCommit c, Change.Key key) throws OrmException {
commit = c;
changeKey = key;
destChanges = db.changes().byBranchKey(magicBranch.dest, key);
destChanges = queryProvider.get().byBranchKey(magicBranch.dest, key);
}
}
@@ -2353,7 +2356,7 @@ public class ReceiveCommits {
closeProgress.update(1);
if (closedChange != null) {
if (byKey == null) {
byKey = openChangesByKey(branch);
byKey = openChangesByBranch(branch);
}
byKey.remove(closedChange);
}
@@ -2361,7 +2364,7 @@ public class ReceiveCommits {
for (final String changeId : c.getFooterLines(CHANGE_ID)) {
if (byKey == null) {
byKey = openChangesByKey(branch);
byKey = openChangesByBranch(branch);
}
final Change onto = byKey.get(new Change.Key(changeId.trim()));
@@ -2436,11 +2439,11 @@ public class ReceiveCommits {
return change.getKey();
}
private Map<Change.Key, Change> openChangesByKey(Branch.NameKey branch)
private Map<Change.Key, Change> openChangesByBranch(Branch.NameKey branch)
throws OrmException {
final Map<Change.Key, Change> r = new HashMap<>();
for (Change c : db.changes().byBranchOpenAll(branch)) {
r.put(c.getKey(), c);
for (ChangeData cd : queryProvider.get().byBranchOpen(branch)) {
r.put(cd.change().getKey(), cd.change());
}
return r;
}

View File

@@ -101,8 +101,10 @@ public class ReceiveCommitsAdvertiseRefsHook implements AdvertiseRefsHook {
final int limit = 32;
try {
Set<PatchSet.Id> toGet = Sets.newHashSetWithExpectedSize(limit);
for (ChangeData cd :
queryProvider.get().setLimit(limit).byProjectOpen(projectName)) {
for (ChangeData cd : queryProvider.get()
.enforceVisibility(true)
.setLimit(limit)
.byProjectOpen(projectName)) {
PatchSet.Id id = cd.change().currentPatchSetId();
if (id != null) {
toGet.add(id);

View File

@@ -15,11 +15,12 @@
package com.google.gerrit.server.git;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gerrit.server.util.OneOffRequestContext;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.slf4j.Logger;
@@ -32,36 +33,40 @@ public class ReloadSubmitQueueOp extends DefaultQueueOp {
private static final Logger log =
LoggerFactory.getLogger(ReloadSubmitQueueOp.class);
private final SchemaFactory<ReviewDb> schema;
private final OneOffRequestContext requestContext;
private final Provider<InternalChangeQuery> queryProvider;
private final MergeQueue mergeQueue;
@Inject
ReloadSubmitQueueOp(final WorkQueue wq, final SchemaFactory<ReviewDb> sf,
final MergeQueue mq) {
ReloadSubmitQueueOp(
OneOffRequestContext rc,
WorkQueue wq,
Provider<InternalChangeQuery> qp,
MergeQueue mq) {
super(wq);
schema = sf;
requestContext = rc;
queryProvider = qp;
mergeQueue = mq;
}
@Override
public void run() {
final HashSet<Branch.NameKey> pending = new HashSet<>();
try (AutoCloseable ctx = requestContext.open()) {
HashSet<Branch.NameKey> pending = new HashSet<>();
for (ChangeData cd : queryProvider.get().allSubmitted()) {
try {
final ReviewDb c = schema.open();
try {
for (final Change change : c.changes().allSubmitted()) {
pending.add(change.getDest());
}
} finally {
c.close();
}
pending.add(cd.change().getDest());
} catch (OrmException e) {
log.error("Cannot reload MergeQueue", e);
log.error("Error reading submitted change", e);
}
}
for (final Branch.NameKey branch : pending) {
for (Branch.NameKey branch : pending) {
mergeQueue.schedule(branch);
}
} catch (Exception e) {
log.error("Cannot reload MergeQueue", e);
}
}
@Override

View File

@@ -14,28 +14,27 @@
package com.google.gerrit.server.index;
import static com.google.gerrit.server.query.change.ChangeData.asChanges;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.git.QueueProvider.QueueType;
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gerrit.server.util.ManualRequestContext;
import com.google.gerrit.server.util.OneOffRequestContext;
import com.google.gerrit.server.util.RequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.util.Providers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -48,24 +47,21 @@ public class ReindexAfterUpdate implements GitReferenceUpdatedListener {
private static final Logger log = LoggerFactory
.getLogger(ReindexAfterUpdate.class);
private final ThreadLocalRequestContext tl;
private final SchemaFactory<ReviewDb> schemaFactory;
private final IdentifiedUser.GenericFactory userFactory;
private final OneOffRequestContext requestContext;
private final Provider<InternalChangeQuery> queryProvider;
private final ChangeIndexer.Factory indexerFactory;
private final IndexCollection indexes;
private final ListeningExecutorService executor;
@Inject
ReindexAfterUpdate(
ThreadLocalRequestContext tl,
SchemaFactory<ReviewDb> schemaFactory,
IdentifiedUser.GenericFactory userFactory,
OneOffRequestContext requestContext,
Provider<InternalChangeQuery> queryProvider,
ChangeIndexer.Factory indexerFactory,
IndexCollection indexes,
@IndexExecutor(QueueType.BATCH) ListeningExecutorService executor) {
this.tl = tl;
this.schemaFactory = schemaFactory;
this.userFactory = userFactory;
this.requestContext = requestContext;
this.queryProvider = queryProvider;
this.indexerFactory = indexerFactory;
this.indexes = indexes;
this.executor = executor;
@@ -81,8 +77,7 @@ public class ReindexAfterUpdate implements GitReferenceUpdatedListener {
List<ListenableFuture<Void>> result =
Lists.newArrayListWithCapacity(changes.size());
for (Change c : changes) {
result.add(executor.submit(
new Index(event, c.getOwner(), c.getId())));
result.add(executor.submit(new Index(event, c.getId())));
}
return Futures.allAsList(result);
}
@@ -90,7 +85,6 @@ public class ReindexAfterUpdate implements GitReferenceUpdatedListener {
}
private abstract class Task<V> implements Callable<V> {
protected ReviewDb db;
protected Event event;
protected Task(Event event) {
@@ -99,20 +93,15 @@ public class ReindexAfterUpdate implements GitReferenceUpdatedListener {
@Override
public final V call() throws Exception {
try {
db = schemaFactory.open();
return impl();
try (ManualRequestContext ctx = requestContext.open()) {
return impl(ctx);
} catch (Exception e) {
log.error("Failed to reindex changes after " + event, e);
throw e;
} finally {
if (db != null) {
db.close();
}
}
}
protected abstract V impl() throws Exception;
protected abstract V impl(RequestContext ctx) throws Exception;
}
private class GetChanges extends Task<List<Change>> {
@@ -121,49 +110,33 @@ public class ReindexAfterUpdate implements GitReferenceUpdatedListener {
}
@Override
protected List<Change> impl() throws OrmException {
protected List<Change> impl(RequestContext ctx) throws OrmException {
String ref = event.getRefName();
Project.NameKey project = new Project.NameKey(event.getProjectName());
if (ref.equals(RefNames.REFS_CONFIG)) {
return db.changes().byProjectOpenAll(project).toList();
return asChanges(queryProvider.get().byProjectOpen(project));
} else {
return db.changes().byBranchOpenAll(new Branch.NameKey(project, ref))
.toList();
return asChanges(queryProvider.get().byBranchOpen(
new Branch.NameKey(project, ref)));
}
}
}
private class Index extends Task<Void> {
private final Account.Id user;
private final Change.Id id;
Index(Event event, Account.Id user, Change.Id id) {
Index(Event event, Change.Id id) {
super(event);
this.user = user;
this.id = id;
}
@Override
protected Void impl() throws OrmException, IOException {
RequestContext context = new RequestContext() {
@Override
public CurrentUser getCurrentUser() {
return userFactory.create(user);
}
@Override
public Provider<ReviewDb> getReviewDbProvider() {
return Providers.of(db);
}
};
RequestContext old = tl.setContext(context);
try {
protected Void impl(RequestContext ctx) throws OrmException, IOException {
// Reload change, as some time may have passed since GetChanges.
ReviewDb db = ctx.getReviewDbProvider().get();
Change c = db.changes().get(id);
indexerFactory.create(executor, indexes).index(db, c);
return null;
} finally {
tl.setContext(old);
}
}
}
}

View File

@@ -21,6 +21,7 @@ import static org.eclipse.jgit.lib.RefDatabase.ALL;
import com.google.common.base.Stopwatch;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
@@ -31,6 +32,7 @@ import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -39,6 +41,7 @@ import com.google.gerrit.server.git.MultiProgressMonitor;
import com.google.gerrit.server.git.MultiProgressMonitor.Task;
import com.google.gerrit.server.patch.PatchListLoader;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
@@ -65,9 +68,11 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -237,7 +242,7 @@ public class SiteIndexer {
repo = repoManager.openRepository(project);
Map<String, Ref> refs = repo.getRefDatabase().getRefs(ALL);
db = schemaFactory.open();
for (Change c : db.changes().byProject(project)) {
for (Change c : scanChanges(db, repo)) {
Ref r = refs.get(c.currentPatchSetId().toRefName());
if (r != null) {
byId.put(r.getObjectId(), changeDataFactory.create(db, c));
@@ -268,6 +273,26 @@ public class SiteIndexer {
};
}
private static List<Change> scanChanges(ReviewDb db, Repository repo)
throws OrmException, IOException {
Map<String, Ref> refs =
repo.getRefDatabase().getRefs(RefNames.REFS_CHANGES);
Set<Change.Id> ids = new LinkedHashSet<>();
for (Ref r : refs.values()) {
Change.Id id = Change.Id.fromRef(r.getName());
if (id != null) {
ids.add(id);
}
}
List<Change> changes = new ArrayList<>(ids.size());
// A batch size of N may overload get(Iterable), so use something smaller,
// but still >1.
for (List<Change.Id> batch : Iterables.partition(ids, 30)) {
Iterables.addAll(changes, db.changes().get(batch));
}
return changes;
}
private static class ProjectIndexer implements Callable<Void> {
private final ChangeIndexer indexer;
private final ThreeWayMergeStrategy mergeStrategy;

View File

@@ -25,6 +25,7 @@ import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.project.DeleteBranch.Input;
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.ResultSet;
import com.google.inject.Inject;
@@ -48,16 +49,19 @@ public class DeleteBranch implements RestModifyView<BranchResource, Input>{
private final Provider<IdentifiedUser> identifiedUser;
private final GitRepositoryManager repoManager;
private final Provider<ReviewDb> dbProvider;
private final Provider<InternalChangeQuery> queryProvider;
private final GitReferenceUpdated referenceUpdated;
private final ChangeHooks hooks;
@Inject
DeleteBranch(Provider<IdentifiedUser> identifiedUser,
GitRepositoryManager repoManager, Provider<ReviewDb> dbProvider,
Provider<InternalChangeQuery> queryProvider,
GitReferenceUpdated referenceUpdated, ChangeHooks hooks) {
this.identifiedUser = identifiedUser;
this.repoManager = repoManager;
this.dbProvider = dbProvider;
this.queryProvider = queryProvider;
this.referenceUpdated = referenceUpdated;
this.hooks = hooks;
}
@@ -68,8 +72,8 @@ public class DeleteBranch implements RestModifyView<BranchResource, Input>{
if (!rsrc.getControl().controlForRef(rsrc.getBranchKey()).canDelete()) {
throw new AuthException("Cannot delete branch");
}
if (dbProvider.get().changes().byBranchOpenAll(rsrc.getBranchKey())
.iterator().hasNext()) {
if (!queryProvider.get().setLimit(1)
.byBranchOpen(rsrc.getBranchKey()).isEmpty()) {
throw new ResourceConflictException("branch " + rsrc.getBranchKey()
+ " has open changes");
}

View File

@@ -75,6 +75,24 @@ import java.util.List;
import java.util.Map;
public class ChangeData {
public static List<Change> asChanges(List<ChangeData> changeDatas)
throws OrmException {
List<Change> result = new ArrayList<>(changeDatas.size());
for (ChangeData cd : changeDatas) {
result.add(cd.change());
}
return result;
}
public static Map<Change.Id, ChangeData> asMap(List<ChangeData> changes) {
Map<Change.Id, ChangeData> result =
Maps.newHashMapWithExpectedSize(changes.size());
for (ChangeData cd : changes) {
result.put(cd.getId(), cd);
}
return result;
}
public static void ensureChangeLoaded(Iterable<ChangeData> changes)
throws OrmException {
Map<Change.Id, ChangeData> missing = Maps.newHashMap();

View File

@@ -17,6 +17,8 @@ package com.google.gerrit.server.query.change;
import static com.google.gerrit.server.query.Predicate.and;
import static com.google.gerrit.server.query.change.ChangeStatusPredicate.open;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.query.Predicate;
import com.google.gerrit.server.query.QueryParseException;
@@ -25,17 +27,36 @@ import com.google.inject.Inject;
import java.util.List;
/** Execute a single query over changes, for use by Gerrit internals. */
/**
* Execute a single query over changes, for use by Gerrit internals.
* <p>
* By default, visibility of returned changes is not enforced (unlike in {@link
* QueryProcessor}). The methods in this class are not typically used by
* user-facing paths, but rather by internal callers that need to process all
* matching results.
*/
public class InternalChangeQuery {
private static Predicate<ChangeData> project(Project.NameKey projectName) {
return new ProjectPredicate(projectName.get());
private static Predicate<ChangeData> ref(Branch.NameKey branch) {
return new RefPredicate(branch.get());
}
private static Predicate<ChangeData> change(Change.Key key) {
return new ChangeIdPredicate(key.get());
}
private static Predicate<ChangeData> project(Project.NameKey project) {
return new ProjectPredicate(project.get());
}
private static Predicate<ChangeData> status(Change.Status status) {
return new ChangeStatusPredicate(status);
}
private final QueryProcessor qp;
@Inject
InternalChangeQuery(QueryProcessor queryProcessor) {
qp = queryProcessor;
qp = queryProcessor.enforceVisibility(false);
}
public InternalChangeQuery setLimit(int n) {
@@ -43,9 +64,47 @@ public class InternalChangeQuery {
return this;
}
public List<ChangeData> byProjectOpen(Project.NameKey projectName)
public InternalChangeQuery enforceVisibility(boolean enforce) {
qp.enforceVisibility(enforce);
return this;
}
public List<ChangeData> byBranchKey(Branch.NameKey branch, Change.Key key)
throws OrmException {
return query(and(project(projectName), open()));
return query(and(
ref(branch),
project(branch.getParentKey()),
change(key)));
}
public List<ChangeData> byProject(Project.NameKey project)
throws OrmException {
return query(project(project));
}
public List<ChangeData> submitted(Branch.NameKey branch) throws OrmException {
return query(and(
ref(branch),
project(branch.getParentKey()),
status(Change.Status.SUBMITTED)));
}
public List<ChangeData> allSubmitted() throws OrmException {
return query(status(Change.Status.SUBMITTED));
}
public List<ChangeData> byBranchOpen(Branch.NameKey branch)
throws OrmException {
return query(and(
ref(branch),
project(branch.getParentKey()),
open()));
}
public List<ChangeData> byProjectOpen(Project.NameKey project)
throws OrmException {
return query(and(project(project), open()));
}
private List<ChangeData> query(Predicate<ChangeData> p) throws OrmException {

View File

@@ -0,0 +1,57 @@
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.util;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Provider;
import com.google.inject.util.Providers;
/**
* Closeable version of a {@link RequestContext} with manually-specified
* providers.
*/
public class ManualRequestContext implements RequestContext, AutoCloseable {
private final CurrentUser user;
private final Provider<ReviewDb> db;
private final ThreadLocalRequestContext requestContext;
private final RequestContext old;
ManualRequestContext(CurrentUser user, SchemaFactory<ReviewDb> schemaFactory,
ThreadLocalRequestContext requestContext) throws OrmException {
this.user = user;
this.db = Providers.of(schemaFactory.open());
this.requestContext = requestContext;
old = requestContext.setContext(this);
}
@Override
public CurrentUser getCurrentUser() {
return user;
}
@Override
public Provider<ReviewDb> getReviewDbProvider() {
return db;
}
@Override
public void close() {
requestContext.setContext(old);
db.get().close();
}
}

View File

@@ -0,0 +1,51 @@
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.util;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.InternalUser;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Singleton;
/**
* Helper to create one-off request contexts.
* <p>
* Each call to {@link #open()} opens a new {@link ReviewDb}, so this class
* should only be used in a bounded try/finally block.
* <p>
* The user in the request context is {@link InternalUser}.
*/
@Singleton
public class OneOffRequestContext {
private final InternalUser.Factory userFactory;
private final SchemaFactory<ReviewDb> schemaFactory;
private final ThreadLocalRequestContext requestContext;
@Inject
OneOffRequestContext(InternalUser.Factory userFactory,
SchemaFactory<ReviewDb> schemaFactory,
ThreadLocalRequestContext requestContext) {
this.userFactory = userFactory;
this.schemaFactory = schemaFactory;
this.requestContext = requestContext;
}
public ManualRequestContext open() throws OrmException {
return new ManualRequestContext(userFactory.create(),
schemaFactory, requestContext);
}
}

View File

@@ -21,6 +21,7 @@ import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.ChangesCollection;
import com.google.gerrit.server.change.DeleteReviewer;
@@ -29,7 +30,6 @@ import com.google.gerrit.server.change.ReviewerResource;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
import com.google.gwtorm.server.OrmException;