From b49726e145fb55bfdd87ec98c7f2135b9ebbbe57 Mon Sep 17 00:00:00 2001 From: Dave Borowitz Date: Wed, 26 Mar 2014 16:01:55 -0700 Subject: [PATCH 1/2] Refactor MergeabilityChecker to use a builder pattern This class was growing with various permutations of change, project, branch, sync/async, which are suited to constructing with a Builder. We also want to add a priority flag to certain checks to support more fine-grained scheduling. In general, due to the strategy of kicking off async work that can fan out and ignoring errors, try to log errors as they happen instead of depending on callers to call get() on the resulting Futures. Change-Id: Ia575793e5ceb5aa3148b45a9a49e4e10e50a5108 --- .../rpc/project/ReviewProjectAccess.java | 2 +- .../gerrit/server/change/ChangeInserter.java | 6 +- .../server/change/MergeabilityChecker.java | 311 +++++++++--------- .../server/change/PatchSetInserter.java | 6 +- .../gerrit/server/git/ReceiveCommits.java | 6 +- .../server/index/ChangeBatchIndexer.java | 2 +- 6 files changed, 162 insertions(+), 171 deletions(-) diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java index b2c4608b70..6537a6b529 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java @@ -155,7 +155,7 @@ public class ReviewProjectAccess extends ProjectAccessHandler { } finally { db.rollback(); } - mergeabilityChecker.updateAndIndexAsync(change).checkedGet(); + mergeabilityChecker.newCheck().addChange(change).reindex().run(); hooks.doPatchsetCreatedHook(change, ps, db); try { CreateChangeSender cm = diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java index c208bb6d6d..ff94f35ef7 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java @@ -178,8 +178,10 @@ public class ChangeInserter { } update.commit(); - CheckedFuture f = - mergeabilityChecker.updateAndIndexAsync(change); + CheckedFuture f = mergeabilityChecker.newCheck() + .addChange(change) + .reindex() + .runAsync(); if (!messageIsForChange()) { insertMessage(db); } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecker.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecker.java index bb4027a488..e6d8b69d2e 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecker.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecker.java @@ -16,6 +16,8 @@ package com.google.gerrit.server.change; import com.google.common.base.Function; import com.google.common.base.Throwables; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; import com.google.common.util.concurrent.AsyncFunction; import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.Futures; @@ -54,7 +56,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; -import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; @@ -63,6 +64,137 @@ public class MergeabilityChecker implements GitReferenceUpdatedListener { private static final Logger log = LoggerFactory .getLogger(MergeabilityChecker.class); + private static final Function MAPPER = + new Function() { + @Override + public IOException apply(Exception in) { + if (in instanceof IOException) { + return (IOException) in; + } else if (in instanceof ExecutionException + && in.getCause() instanceof IOException) { + return (IOException) in.getCause(); + } else { + return new IOException(in); + } + } + }; + + public class Check { + private List changes; + private List branches; + private List projects; + private boolean force; + private boolean reindex; + + private Check() { + changes = Lists.newArrayListWithExpectedSize(1); + branches = Lists.newArrayListWithExpectedSize(1); + projects = Lists.newArrayListWithExpectedSize(1); + } + + public Check addChange(Change change) { + changes.add(change); + return this; + } + + public Check addBranch(Branch.NameKey branch) { + branches.add(branch); + return this; + } + + public Check addProject(Project.NameKey project) { + projects.add(project); + return this; + } + + /** Force reindexing regardless of whether mergeable flag was modified. */ + public Check reindex() { + reindex = true; + return this; + } + + /** Force mergeability check even if change is not stale. */ + private Check force() { + force = true; + return this; + } + + public CheckedFuture runAsync() { + ListenableFuture> getChanges; + if (branches.isEmpty() && projects.isEmpty()) { + getChanges = Futures.immediateFuture(changes); + } else { + getChanges = executor.submit( + new Callable>() { + @Override + public List call() throws OrmException { + return getChanges(); + } + }); + } + + return Futures.makeChecked(Futures.transform(getChanges, + new AsyncFunction, List>() { + @Override + public ListenableFuture> apply(List changes) { + List> result = + Lists.newArrayListWithCapacity(changes.size()); + for (final Change c : changes) { + ListenableFuture b = + executor.submit(new Task(c, force)); + if (reindex) { + result.add(Futures.transform( + b, new AsyncFunction() { + @SuppressWarnings("unchecked") + @Override + public ListenableFuture apply( + Boolean indexUpdated) throws Exception { + if (!indexUpdated) { + return (ListenableFuture) + indexer.indexAsync(c.getId()); + } + return Futures.immediateFuture(null); + } + })); + } else { + result.add(b); + } + } + return Futures.allAsList(result); + } + }), MAPPER); + } + + public void run() throws IOException { + try { + runAsync().checkedGet(); + } catch (Exception e) { + Throwables.propagateIfPossible(e, IOException.class); + throw MAPPER.apply(e); + } + } + + private List getChanges() throws OrmException { + ReviewDb db = schemaFactory.open(); + try { + List results = Lists.newArrayList(); + results.addAll(changes); + for (Project.NameKey p : projects) { + Iterables.addAll(results, db.changes().byProjectOpenAll(p)); + } + for (Branch.NameKey b : branches) { + Iterables.addAll(results, db.changes().byBranchOpenAll(b)); + } + return results; + } catch (OrmException e) { + log.error("Failed to fetch changes for mergeability check", e); + throw e; + } finally { + db.close(); + } + } + } + private final ThreadLocalRequestContext tl; private final SchemaFactory schemaFactory; private final IdentifiedUser.GenericFactory identifiedUserFactory; @@ -93,27 +225,17 @@ public class MergeabilityChecker implements GitReferenceUpdatedListener { this.metaDataUpdateFactory = metaDataUpdateFactory; } - private static final Function MAPPER = - new Function() { - @Override - public IOException apply(Exception in) { - if (in instanceof IOException) { - return (IOException) in; - } else if (in instanceof ExecutionException - && in.getCause() instanceof IOException) { - return (IOException) in.getCause(); - } else { - return new IOException(in); - } - } - }; + public Check newCheck() { + return new Check(); + } @Override public void onGitReferenceUpdated(GitReferenceUpdatedListener.Event event) { String ref = event.getRefName(); if (ref.startsWith(Constants.R_HEADS) || ref.equals(RefNames.REFS_CONFIG)) { - executor.submit(new BranchUpdateTask(schemaFactory, - new Project.NameKey(event.getProjectName()), ref)); + Branch.NameKey branch = new Branch.NameKey( + new Project.NameKey(event.getProjectName()), ref); + newCheck().addBranch(branch).runAsync(); } if (ref.equals(RefNames.REFS_CONFIG)) { Project.NameKey p = new Project.NameKey(event.getProjectName()); @@ -121,15 +243,7 @@ public class MergeabilityChecker implements GitReferenceUpdatedListener { ProjectConfig oldCfg = parseConfig(p, event.getOldObjectId()); ProjectConfig newCfg = parseConfig(p, event.getNewObjectId()); if (recheckMerges(oldCfg, newCfg)) { - try { - new ProjectUpdateTask(schemaFactory, p, true).call(); - } catch (Exception e) { - String msg = "Failed to update mergeability flags for project " + p.get() - + " on update of " + RefNames.REFS_CONFIG; - log.error(msg, e); - Throwables.propagateIfPossible(e); - throw new RuntimeException(msg, e); - } + newCheck().addProject(p).force().runAsync(); } } catch (ConfigInvalidException | IOException e) { String msg = "Failed to update mergeability flags for project " + p.get() @@ -160,87 +274,13 @@ public class MergeabilityChecker implements GitReferenceUpdatedListener { return ProjectConfig.read(metaDataUpdateFactory.create(p), id); } - /** - * Updates the mergeability flag of the change asynchronously. If the - * mergeability flag is updated the change is reindexed. - * - * @param change the change for which the mergeability flag should be updated - * @return CheckedFuture that updates the mergeability flag of the change and - * returns {@code true} if the mergeability flag was updated and - * the change was reindexed, and {@code false} if the - * mergeability flag was not updated and the change was not reindexed - */ - public CheckedFuture updateAsync(Change change) { - return updateAsync(change, false); - } - - private CheckedFuture updateAsync(Change change, boolean force) { - return Futures.makeChecked( - executor.submit(new ChangeUpdateTask(schemaFactory, change, force)), - MAPPER); - } - - /** - * Updates the mergeability flag of the change asynchronously and reindexes - * the change in any case. - * - * @param change the change for which the mergeability flag should be updated - * @return CheckedFuture that updates the mergeability flag of the change and - * reindexes the change (whether the mergeability flag was updated or - * not) - */ - public CheckedFuture updateAndIndexAsync(Change change) { - final Change.Id id = change.getId(); - return Futures.makeChecked( - Futures.transform(updateAsync(change), - new AsyncFunction() { - @SuppressWarnings("unchecked") - @Override - public ListenableFuture apply(Boolean indexUpdated) - throws Exception { - if (!indexUpdated) { - return (ListenableFuture) indexer.indexAsync(id); - } - return Futures.immediateFuture(null); - } - }), MAPPER); - } - - public boolean update(Change change) throws IOException { - try { - return new ChangeUpdateTask(schemaFactory, change).call(); - } catch (Exception e) { - Throwables.propagateIfPossible(e); - throw MAPPER.apply(e); - } - } - - public void update(Project.NameKey project) throws IOException { - try { - for (CheckedFuture f : new ProjectUpdateTask( - schemaFactory, project, false).call()) { - f.checkedGet(); - } - } catch (Exception e) { - Throwables.propagateIfPossible(e); - throw MAPPER.apply(e); - } - } - - private class ChangeUpdateTask implements Callable { - private final SchemaFactory schemaFactory; + private class Task implements Callable { private final Change change; private final boolean force; private ReviewDb reviewDb; - ChangeUpdateTask(SchemaFactory schemaFactory, Change change) { - this(schemaFactory, change, false); - } - - ChangeUpdateTask(SchemaFactory schemaFactory, Change change, - boolean force) { - this.schemaFactory = schemaFactory; + Task(Change change, boolean force) { this.change = change; this.force = force; } @@ -287,6 +327,12 @@ public class MergeabilityChecker implements GitReferenceUpdatedListener { } catch (ResourceConflictException e) { // change is closed return false; + } catch (Exception e) { + String msg = "Failed to update mergeability flags for project " + + change.getDest().getParentKey() + " on update of " + + change.getDest().get(); + log.error(msg, e); + throw e; } finally { tl.setContext(old); if (reviewDb != null) { @@ -296,65 +342,4 @@ public class MergeabilityChecker implements GitReferenceUpdatedListener { } } } - - private abstract class UpdateTask implements - Callable>> { - private final SchemaFactory schemaFactory; - private final boolean force; - - UpdateTask(SchemaFactory schemaFactory, boolean force) { - this.schemaFactory = schemaFactory; - this.force = force; - } - - @Override - public List> call() throws Exception { - List openChanges; - ReviewDb db = schemaFactory.open(); - try { - openChanges = loadChanges(db); - } finally { - db.close(); - } - - List> futures = - new ArrayList<>(openChanges.size()); - for (Change change : mergeabilityCheckQueue.addAll(openChanges, force)) { - futures.add(updateAsync(change, force)); - } - return futures; - } - - protected abstract List loadChanges(ReviewDb db) throws OrmException; - } - - private class BranchUpdateTask extends UpdateTask { - private final Branch.NameKey branch; - - BranchUpdateTask(SchemaFactory schemaFactory, - Project.NameKey project, String ref) { - super(schemaFactory, false); - this.branch = new Branch.NameKey(project, ref); - } - - @Override - protected List loadChanges(ReviewDb db) throws OrmException { - return db.changes().byBranchOpenAll(branch).toList(); - } - } - - private class ProjectUpdateTask extends UpdateTask { - private final Project.NameKey project; - - ProjectUpdateTask(SchemaFactory schemaFactory, - Project.NameKey project, boolean force) { - super(schemaFactory, force); - this.project = project; - } - - @Override - protected List loadChanges(ReviewDb db) throws OrmException { - return db.changes().byProjectOpenAll(project).toList(); - } - } } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java index b581c67eb3..c059bf4ac0 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PatchSetInserter.java @@ -306,8 +306,10 @@ public class PatchSetInserter { } finally { db.rollback(); } - CheckedFuture f = - mergeabilityChecker.updateAndIndexAsync(updatedChange); + CheckedFuture f = mergeabilityChecker.newCheck() + .addChange(updatedChange) + .reindex() + .runAsync(); if (runHooks) { hooks.doPatchsetCreatedHook(updatedChange, patchSet, db); } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java index fdc87f3dfc..b43967ac4d 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java @@ -2004,8 +2004,10 @@ public class ReceiveCommits { if (cmd.getResult() == NOT_ATTEMPTED) { cmd.execute(rp); } - CheckedFuture f = - mergeabilityChecker.updateAndIndexAsync(change); + CheckedFuture f = mergeabilityChecker.newCheck() + .addChange(change) + .reindex() + .runAsync(); gitRefUpdated.fire(project.getNameKey(), newPatchSet.getRefName(), ObjectId.zeroId(), newCommit); hooks.doPatchsetCreatedHook(change, newPatchSet, db); diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeBatchIndexer.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeBatchIndexer.java index a1075fa154..5ee240b8a6 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeBatchIndexer.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeBatchIndexer.java @@ -211,7 +211,7 @@ public class ChangeBatchIndexer { private boolean updateMergeable(Project.NameKey project) { if (mergeabilityChecker != null) { try { - mergeabilityChecker.update(project); + mergeabilityChecker.newCheck().addProject(project).run(); } catch (IOException e) { log.error("Error in mergeability checker", e); return false; From 6ea964a0a17b95051db8a56c05a99fcabd14b0f0 Mon Sep 17 00:00:00 2001 From: Dave Borowitz Date: Wed, 26 Mar 2014 16:49:45 -0700 Subject: [PATCH 2/2] Split mergeability checks by priority When a project config is updated or a change is merged, all open changes on an entire project or branch need to be rechecked for mergeability. For projects or branches with lots of open changes, this can flood the queue with potentially slow checks. This will block simpler cases, such as checking mergeability of a single change during ReceiveCommits. Define a new Priority enum on the MergeabilityChecksExecutor so we can split the queue based on priority, allowing smaller, interactive changes such as pushes to not wait for the entire queued backlog. Change-Id: I57f9eb33dbbcd8a560adfe4458ce48d7b0817957 --- Documentation/config-gerrit.txt | 9 ++++++++ .../java/com/google/gerrit/pgm/Reindex.java | 12 +++++++++- .../server/change/MergeabilityChecker.java | 23 ++++++++++++++++--- .../change/MergeabilityChecksExecutor.java | 5 ++++ .../MergeabilityChecksExecutorModule.java | 20 ++++++++++++++-- 5 files changed, 63 insertions(+), 6 deletions(-) diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt index 6acb2d6a00..36b0e31370 100644 --- a/Documentation/config-gerrit.txt +++ b/Documentation/config-gerrit.txt @@ -823,6 +823,15 @@ changes is updated. + Default is 1. +[[changeMerge.interactiveThreadPoolSize]]changeMerge.interactiveThreadPoolSize:: ++ +Maximum size of the thread pool in which the mergeability flag of open +changes is updated, when processing interactive user requests (e.g. +pushes to refs/for/*). Set to 0 or negative to share the pool for +background mergeability checks. ++ +Default is 1. + [[commentlink]] === Section commentlink diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Reindex.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Reindex.java index 2b0850e2cf..b8649629e4 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Reindex.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Reindex.java @@ -44,6 +44,7 @@ import com.google.gerrit.server.cache.h2.DefaultCacheFactory; import com.google.gerrit.server.change.ChangeKindCache; import com.google.gerrit.server.change.MergeabilityChecker; import com.google.gerrit.server.change.MergeabilityChecksExecutor; +import com.google.gerrit.server.change.MergeabilityChecksExecutor.Priority; import com.google.gerrit.server.change.PatchSetInserter; import com.google.gerrit.server.config.CanonicalWebUrl; import com.google.gerrit.server.config.CanonicalWebUrlProvider; @@ -290,11 +291,20 @@ public class Reindex extends SiteProgram { @Provides @Singleton - @MergeabilityChecksExecutor + @MergeabilityChecksExecutor(Priority.BACKGROUND) public WorkQueue.Executor createMergeabilityChecksExecutor( WorkQueue queues) { return queues.createQueue(1, "MergeabilityChecks"); } + + @Provides + @Singleton + @MergeabilityChecksExecutor(Priority.INTERACTIVE) + public WorkQueue.Executor createInteractiveMergeabilityChecksExecutor( + @MergeabilityChecksExecutor(Priority.BACKGROUND) + WorkQueue.Executor bg) { + return bg; + } } private int indexAll() throws Exception { diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecker.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecker.java index e6d8b69d2e..0734d12496 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecker.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecker.java @@ -34,6 +34,7 @@ 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.change.MergeabilityChecksExecutor.Priority; import com.google.gerrit.server.change.Mergeable.MergeableInfo; import com.google.gerrit.server.git.MetaDataUpdate; import com.google.gerrit.server.git.ProjectConfig; @@ -85,11 +86,13 @@ public class MergeabilityChecker implements GitReferenceUpdatedListener { private List projects; private boolean force; private boolean reindex; + private boolean interactive; private Check() { changes = Lists.newArrayListWithExpectedSize(1); branches = Lists.newArrayListWithExpectedSize(1); projects = Lists.newArrayListWithExpectedSize(1); + interactive = true; } public Check addChange(Change change) { @@ -99,11 +102,13 @@ public class MergeabilityChecker implements GitReferenceUpdatedListener { public Check addBranch(Branch.NameKey branch) { branches.add(branch); + interactive = false; return this; } public Check addProject(Project.NameKey project) { projects.add(project); + interactive = false; return this; } @@ -119,7 +124,12 @@ public class MergeabilityChecker implements GitReferenceUpdatedListener { return this; } + private ListeningExecutorService getExecutor() { + return interactive ? interactiveExecutor : backgroundExecutor; + } + public CheckedFuture runAsync() { + final ListeningExecutorService executor = getExecutor(); ListenableFuture> getChanges; if (branches.isEmpty() && projects.isEmpty()) { getChanges = Futures.immediateFuture(changes); @@ -201,7 +211,8 @@ public class MergeabilityChecker implements GitReferenceUpdatedListener { private final ChangeControl.GenericFactory changeControlFactory; private final Provider mergeable; private final ChangeIndexer indexer; - private final ListeningExecutorService executor; + private final ListeningExecutorService backgroundExecutor; + private final ListeningExecutorService interactiveExecutor; private final MergeabilityCheckQueue mergeabilityCheckQueue; private final MetaDataUpdate.Server metaDataUpdateFactory; @@ -211,7 +222,10 @@ public class MergeabilityChecker implements GitReferenceUpdatedListener { IdentifiedUser.GenericFactory identifiedUserFactory, ChangeControl.GenericFactory changeControlFactory, Provider mergeable, ChangeIndexer indexer, - @MergeabilityChecksExecutor Executor executor, + @MergeabilityChecksExecutor(Priority.BACKGROUND) + Executor backgroundExecutor, + @MergeabilityChecksExecutor(Priority.INTERACTIVE) + Executor interactiveExecutor, MergeabilityCheckQueue mergeabilityCheckQueue, MetaDataUpdate.Server metaDataUpdateFactory) { this.tl = tl; @@ -220,7 +234,10 @@ public class MergeabilityChecker implements GitReferenceUpdatedListener { this.changeControlFactory = changeControlFactory; this.mergeable = mergeable; this.indexer = indexer; - this.executor = MoreExecutors.listeningDecorator(executor); + this.backgroundExecutor = + MoreExecutors.listeningDecorator(backgroundExecutor); + this.interactiveExecutor = + MoreExecutors.listeningDecorator(interactiveExecutor); this.mergeabilityCheckQueue = mergeabilityCheckQueue; this.metaDataUpdateFactory = metaDataUpdateFactory; } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecksExecutor.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecksExecutor.java index 5a26086768..632e6ac053 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecksExecutor.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecksExecutor.java @@ -28,4 +28,9 @@ import java.lang.annotation.Retention; @Retention(RUNTIME) @BindingAnnotation public @interface MergeabilityChecksExecutor { + public enum Priority { + BACKGROUND, INTERACTIVE; + } + + Priority value(); } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecksExecutorModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecksExecutorModule.java index 102cfafe83..96c83d36ab 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecksExecutorModule.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/MergeabilityChecksExecutorModule.java @@ -14,6 +14,7 @@ package com.google.gerrit.server.change; +import com.google.gerrit.server.change.MergeabilityChecksExecutor.Priority; import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.git.WorkQueue; import com.google.inject.AbstractModule; @@ -22,7 +23,6 @@ import com.google.inject.Singleton; import org.eclipse.jgit.lib.Config; - /** Module providing the {@link MergeabilityChecksExecutor}. */ public class MergeabilityChecksExecutorModule extends AbstractModule { @Override @@ -31,11 +31,27 @@ public class MergeabilityChecksExecutorModule extends AbstractModule { @Provides @Singleton - @MergeabilityChecksExecutor + @MergeabilityChecksExecutor(Priority.BACKGROUND) public WorkQueue.Executor createMergeabilityChecksExecutor( @GerritServerConfig Config config, WorkQueue queues) { int poolSize = config.getInt("changeMerge", null, "threadPoolSize", 1); return queues.createQueue(poolSize, "MergeabilityChecks"); } + + @Provides + @Singleton + @MergeabilityChecksExecutor(Priority.INTERACTIVE) + public WorkQueue.Executor createMergeabilityChecksExecutor( + @GerritServerConfig Config config, + WorkQueue queues, + @MergeabilityChecksExecutor(Priority.BACKGROUND) + WorkQueue.Executor backgroundExecutor) { + int poolSize = + config.getInt("changeMerge", null, "interactiveThreadPoolSize", 1); + if (poolSize <= 0) { + return backgroundExecutor; + } + return queues.createQueue(poolSize, "InteractiveMergeabilityChecks"); + } }