Ensure InternalQuery/QueryProcessor are one-time-use

These classes are non-threadsafe and have mutable fields for a
builder-like pattern; they should not be reused. Nonetheless, this was
happening in a few places, which seemed to not be causing problems, but
they may have just been hard to observe.

Ensure they are consistently used only once with a checkState in
QueryProcessor#query, and document this limitation.

Change-Id: I1c7d392b1094dcb562894108b652ee7750d98da4
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index 6c1fb39..48aa0bb 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -152,7 +152,7 @@
 
   @Inject private Sequences seq;
 
-  @Inject private InternalAccountQuery accountQuery;
+  @Inject private Provider<InternalAccountQuery> accountQueryProvider;
 
   @Inject protected Emails emails;
 
@@ -1033,7 +1033,7 @@
     }
 
     assertThat(accountCache.getOrNull(admin.id)).isNull();
-    assertThat(accountQuery.byDefault(admin.id.toString())).isEmpty();
+    assertThat(accountQueryProvider.get().byDefault(admin.id.toString())).isEmpty();
   }
 
   @Test
@@ -1257,7 +1257,7 @@
   @Test
   public void internalQueryFindActiveAndInactiveAccounts() throws Exception {
     String name = name("foo");
-    assertThat(accountQuery.byDefault(name)).isEmpty();
+    assertThat(accountQueryProvider.get().byDefault(name)).isEmpty();
 
     TestAccount foo1 = accountCreator.create(name + "-1");
     assertThat(gApi.accounts().id(foo1.username).getActive()).isTrue();
@@ -1266,7 +1266,7 @@
     gApi.accounts().id(foo2.username).setActive(false);
     assertThat(gApi.accounts().id(foo2.username).getActive()).isFalse();
 
-    assertThat(accountQuery.byDefault(name)).hasSize(2);
+    assertThat(accountQueryProvider.get().byDefault(name)).hasSize(2);
   }
 
   private void assertSequenceNumbers(List<SshKeyInfo> sshKeys) {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
index 365789c..4f7a5ba 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
@@ -38,6 +38,7 @@
 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 java.io.FileNotFoundException;
 import java.io.IOException;
@@ -63,7 +64,7 @@
   private final AccountCache accountCache;
   private final AccountManager accountManager;
   private final SiteHeaderFooter headers;
-  private final InternalAccountQuery accountQuery;
+  private final Provider<InternalAccountQuery> queryProvider;
 
   @Inject
   BecomeAnyAccountLoginServlet(
@@ -73,14 +74,14 @@
       AccountCache ac,
       AccountManager am,
       SiteHeaderFooter shf,
-      InternalAccountQuery aq) {
+      Provider<InternalAccountQuery> qp) {
     webSession = ws;
     schema = sf;
     accounts = a;
     accountCache = ac;
     accountManager = am;
     headers = shf;
-    accountQuery = aq;
+    queryProvider = qp;
   }
 
   @Override
@@ -198,7 +199,8 @@
 
   private AuthResult byUserName(String userName) {
     try {
-      List<AccountState> accountStates = accountQuery.byExternalId(SCHEME_USERNAME, userName);
+      List<AccountState> accountStates =
+          queryProvider.get().byExternalId(SCHEME_USERNAME, userName);
       if (accountStates.isEmpty()) {
         getServletContext().log("No accounts with username " + userName + " found");
         return null;
@@ -217,7 +219,8 @@
   private AuthResult byPreferredEmail(String email) {
     try (ReviewDb db = schema.open()) {
       Optional<Account> match =
-          accountQuery
+          queryProvider
+              .get()
               .byPreferredEmail(email)
               .stream()
               // the index query also matches prefixes, filter those out
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ReviewerRecommender.java b/gerrit-server/src/main/java/com/google/gerrit/server/ReviewerRecommender.java
index b9bd658..7374833 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ReviewerRecommender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ReviewerRecommender.java
@@ -80,7 +80,7 @@
   private final ChangeQueryBuilder changeQueryBuilder;
   private final Config config;
   private final DynamicMap<ReviewerSuggestion> reviewerSuggestionPluginMap;
-  private final InternalChangeQuery internalChangeQuery;
+  private final Provider<InternalChangeQuery> queryProvider;
   private final WorkQueue workQueue;
   private final Provider<ReviewDb> dbProvider;
   private final ApprovalsUtil approvalsUtil;
@@ -89,7 +89,7 @@
   ReviewerRecommender(
       ChangeQueryBuilder changeQueryBuilder,
       DynamicMap<ReviewerSuggestion> reviewerSuggestionPluginMap,
-      InternalChangeQuery internalChangeQuery,
+      Provider<InternalChangeQuery> queryProvider,
       WorkQueue workQueue,
       Provider<ReviewDb> dbProvider,
       ApprovalsUtil approvalsUtil,
@@ -98,7 +98,7 @@
     fillOptions.addAll(AccountLoader.DETAILED_OPTIONS);
     this.changeQueryBuilder = changeQueryBuilder;
     this.config = config;
-    this.internalChangeQuery = internalChangeQuery;
+    this.queryProvider = queryProvider;
     this.reviewerSuggestionPluginMap = reviewerSuggestionPluginMap;
     this.workQueue = workQueue;
     this.dbProvider = dbProvider;
@@ -202,7 +202,8 @@
     // Get the user's last 25 changes, check approvals
     try {
       List<ChangeData> result =
-          internalChangeQuery
+          queryProvider
+              .get()
               .setLimit(25)
               .setRequestedFields(ImmutableSet.of(ChangeField.REVIEWER.getName()))
               .query(changeQueryBuilder.owner("self"));
@@ -268,7 +269,7 @@
     }
 
     List<List<ChangeData>> result =
-        internalChangeQuery.setLimit(25).setRequestedFields(ImmutableSet.of()).query(predicates);
+        queryProvider.get().setLimit(25).setRequestedFields(ImmutableSet.of()).query(predicates);
 
     Iterator<List<ChangeData>> queryResultIterator = result.iterator();
     Iterator<Account.Id> reviewersIterator = reviewers.keySet().iterator();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ReviewersUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/ReviewersUtil.java
index b328e37..7ba75c5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ReviewersUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ReviewersUtil.java
@@ -109,7 +109,7 @@
 
   private final AccountLoader accountLoader;
   private final AccountQueryBuilder accountQueryBuilder;
-  private final AccountQueryProcessor accountQueryProcessor;
+  private final Provider<AccountQueryProcessor> queryProvider;
   private final GroupBackend groupBackend;
   private final GroupMembers.Factory groupMembersFactory;
   private final Provider<CurrentUser> currentUser;
@@ -120,7 +120,7 @@
   ReviewersUtil(
       AccountLoader.Factory accountLoaderFactory,
       AccountQueryBuilder accountQueryBuilder,
-      AccountQueryProcessor accountQueryProcessor,
+      Provider<AccountQueryProcessor> queryProvider,
       GroupBackend groupBackend,
       GroupMembers.Factory groupMembersFactory,
       Provider<CurrentUser> currentUser,
@@ -130,7 +130,7 @@
     fillOptions.addAll(AccountLoader.DETAILED_OPTIONS);
     this.accountLoader = accountLoaderFactory.create(fillOptions);
     this.accountQueryBuilder = accountQueryBuilder;
-    this.accountQueryProcessor = accountQueryProcessor;
+    this.queryProvider = queryProvider;
     this.currentUser = currentUser;
     this.groupBackend = groupBackend;
     this.groupMembersFactory = groupMembersFactory;
@@ -199,7 +199,8 @@
     try (Timer0.Context ctx = metrics.queryAccountsLatency.start()) {
       try {
         QueryResult<AccountState> result =
-            accountQueryProcessor
+            queryProvider
+                .get()
                 .setLimit(suggestReviewers.getLimit() * CANDIDATE_LIST_MULTIPLIER)
                 .query(
                     AccountPredicates.andActive(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/Emails.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/Emails.java
index 15f4509..db707a8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/Emails.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/Emails.java
@@ -24,6 +24,7 @@
 import com.google.gerrit.server.query.account.InternalAccountQuery;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
+import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import java.io.IOException;
 import java.util.List;
@@ -32,12 +33,12 @@
 @Singleton
 public class Emails {
   private final ExternalIds externalIds;
-  private final InternalAccountQuery accountQuery;
+  private final Provider<InternalAccountQuery> queryProvider;
 
   @Inject
-  public Emails(ExternalIds externalIds, InternalAccountQuery accountQuery) {
+  public Emails(ExternalIds externalIds, Provider<InternalAccountQuery> queryProvider) {
     this.externalIds = externalIds;
-    this.accountQuery = accountQuery;
+    this.queryProvider = queryProvider;
   }
 
   /**
@@ -60,7 +61,7 @@
    * @see #getAccountsFor(String...)
    */
   public ImmutableSet<Account.Id> getAccountFor(String email) throws IOException, OrmException {
-    List<AccountState> byPreferredEmail = accountQuery.byPreferredEmail(email);
+    List<AccountState> byPreferredEmail = queryProvider.get().byPreferredEmail(email);
     return Streams.concat(
             externalIds.byEmail(email).stream().map(e -> e.accountId()),
             byPreferredEmail
@@ -85,7 +86,8 @@
         .entries()
         .stream()
         .forEach(e -> builder.put(e.getKey(), e.getValue().accountId()));
-    accountQuery
+    queryProvider
+        .get()
         .byPreferredEmail(emails)
         .entries()
         .stream()
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/AbandonUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/AbandonUtil.java
index 80f25af..58a908d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/AbandonUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/AbandonUtil.java
@@ -27,6 +27,7 @@
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
+import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -40,7 +41,7 @@
   private static final Logger log = LoggerFactory.getLogger(AbandonUtil.class);
 
   private final ChangeCleanupConfig cfg;
-  private final ChangeQueryProcessor queryProcessor;
+  private final Provider<ChangeQueryProcessor> queryProvider;
   private final ChangeQueryBuilder queryBuilder;
   private final Abandon abandon;
   private final InternalUser internalUser;
@@ -49,11 +50,11 @@
   AbandonUtil(
       ChangeCleanupConfig cfg,
       InternalUser.Factory internalUserFactory,
-      ChangeQueryProcessor queryProcessor,
+      Provider<ChangeQueryProcessor> queryProvider,
       ChangeQueryBuilder queryBuilder,
       Abandon abandon) {
     this.cfg = cfg;
-    this.queryProcessor = queryProcessor;
+    this.queryProvider = queryProvider;
     this.queryBuilder = queryBuilder;
     this.abandon = abandon;
     internalUser = internalUserFactory.create();
@@ -72,7 +73,7 @@
       }
 
       List<ChangeData> changesToAbandon =
-          queryProcessor.enforceVisibility(false).query(queryBuilder.parse(query)).entities();
+          queryProvider.get().enforceVisibility(false).query(queryBuilder.parse(query)).entities();
       ImmutableListMultimap.Builder<Project.NameKey, ChangeControl> builder =
           ImmutableListMultimap.builder();
       for (ChangeData cd : changesToAbandon) {
@@ -110,7 +111,11 @@
     for (ChangeControl cc : changeControls) {
       String newQuery = query + " change:" + cc.getId();
       List<ChangeData> changesToAbandon =
-          queryProcessor.enforceVisibility(false).query(queryBuilder.parse(newQuery)).entities();
+          queryProvider
+              .get()
+              .enforceVisibility(false)
+              .query(queryBuilder.parse(newQuery))
+              .entities();
       if (!changesToAbandon.isEmpty()) {
         validChanges.add(cc);
       } else {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
index d3ddbd9..8823e6d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
@@ -228,7 +228,7 @@
   private final InternalUser.Factory internalUserFactory;
   private final MergeSuperSet mergeSuperSet;
   private final MergeValidators.Factory mergeValidatorsFactory;
-  private final InternalChangeQuery internalChangeQuery;
+  private final Provider<InternalChangeQuery> queryProvider;
   private final SubmitStrategyFactory submitStrategyFactory;
   private final SubmoduleOp.Factory subOpFactory;
   private final Provider<MergeOpRepoManager> ormProvider;
@@ -255,7 +255,7 @@
       InternalUser.Factory internalUserFactory,
       MergeSuperSet mergeSuperSet,
       MergeValidators.Factory mergeValidatorsFactory,
-      InternalChangeQuery internalChangeQuery,
+      Provider<InternalChangeQuery> queryProvider,
       SubmitStrategyFactory submitStrategyFactory,
       SubmoduleOp.Factory subOpFactory,
       Provider<MergeOpRepoManager> ormProvider,
@@ -267,7 +267,7 @@
     this.internalUserFactory = internalUserFactory;
     this.mergeSuperSet = mergeSuperSet;
     this.mergeValidatorsFactory = mergeValidatorsFactory;
-    this.internalChangeQuery = internalChangeQuery;
+    this.queryProvider = queryProvider;
     this.submitStrategyFactory = submitStrategyFactory;
     this.subOpFactory = subOpFactory;
     this.ormProvider = ormProvider;
@@ -860,7 +860,7 @@
 
   private void abandonAllOpenChangeForDeletedProject(Project.NameKey destProject) {
     try {
-      for (ChangeData cd : internalChangeQuery.byProjectOpen(destProject)) {
+      for (ChangeData cd : queryProvider.get().byProjectOpen(destProject)) {
         try (BatchUpdate bu =
             batchUpdateFactory.create(db, destProject, internalUserFactory.create(), ts)) {
           bu.setRequestId(submissionId);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/RebaseSorter.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/RebaseSorter.java
index d596987..6fc5eaa 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/RebaseSorter.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/RebaseSorter.java
@@ -21,6 +21,7 @@
 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.Provider;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -41,7 +42,7 @@
   private final RevFlag canMergeFlag;
   private final RevCommit initialTip;
   private final Set<RevCommit> alreadyAccepted;
-  private final InternalChangeQuery internalChangeQuery;
+  private final Provider<InternalChangeQuery> queryProvider;
   private final Set<CodeReviewCommit> incoming;
 
   public RebaseSorter(
@@ -49,13 +50,13 @@
       RevCommit initialTip,
       Set<RevCommit> alreadyAccepted,
       RevFlag canMergeFlag,
-      InternalChangeQuery internalChangeQuery,
+      Provider<InternalChangeQuery> queryProvider,
       Set<CodeReviewCommit> incoming) {
     this.rw = rw;
     this.canMergeFlag = canMergeFlag;
     this.initialTip = initialTip;
     this.alreadyAccepted = alreadyAccepted;
-    this.internalChangeQuery = internalChangeQuery;
+    this.queryProvider = queryProvider;
     this.incoming = incoming;
   }
 
@@ -116,7 +117,7 @@
       }
 
       // check if the commit associated change is merged in the same branch
-      List<ChangeData> changes = internalChangeQuery.byCommit(commit);
+      List<ChangeData> changes = queryProvider.get().byCommit(commit);
       for (ChangeData change : changes) {
         if (change.change().getStatus() == Status.MERGED
             && change.change().getDest().equals(dest)) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategy.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategy.java
index 9892b6e..2a22c1c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategy.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitStrategy.java
@@ -58,6 +58,7 @@
 import com.google.gerrit.server.util.RequestId;
 import com.google.inject.Inject;
 import com.google.inject.Module;
+import com.google.inject.Provider;
 import com.google.inject.assistedinject.Assisted;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -119,7 +120,7 @@
     final RebaseChangeOp.Factory rebaseFactory;
     final OnSubmitValidators.Factory onSubmitValidatorsFactory;
     final TagCache tagCache;
-    final InternalChangeQuery internalChangeQuery;
+    final Provider<InternalChangeQuery> queryProvider;
 
     final Branch.NameKey destBranch;
     final CodeReviewRevWalk rw;
@@ -159,7 +160,7 @@
         RebaseChangeOp.Factory rebaseFactory,
         OnSubmitValidators.Factory onSubmitValidatorsFactory,
         TagCache tagCache,
-        InternalChangeQuery internalChangeQuery,
+        Provider<InternalChangeQuery> queryProvider,
         @Assisted Branch.NameKey destBranch,
         @Assisted CommitStatus commitStatus,
         @Assisted CodeReviewRevWalk rw,
@@ -188,7 +189,7 @@
       this.projectCache = projectCache;
       this.rebaseFactory = rebaseFactory;
       this.tagCache = tagCache;
-      this.internalChangeQuery = internalChangeQuery;
+      this.queryProvider = queryProvider;
 
       this.serverIdent = serverIdent;
       this.destBranch = destBranch;
@@ -214,12 +215,7 @@
       this.mergeSorter = new MergeSorter(rw, alreadyAccepted, canMergeFlag, incoming);
       this.rebaseSorter =
           new RebaseSorter(
-              rw,
-              mergeTip.getInitialTip(),
-              alreadyAccepted,
-              canMergeFlag,
-              internalChangeQuery,
-              incoming);
+              rw, mergeTip.getInitialTip(), alreadyAccepted, canMergeFlag, queryProvider, incoming);
       this.mergeUtil = mergeUtilFactory.create(project);
       this.onSubmitValidatorsFactory = onSubmitValidatorsFactory;
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/InternalQuery.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/InternalQuery.java
index 64029ab..30f7328 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/InternalQuery.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/InternalQuery.java
@@ -33,6 +33,9 @@
  * <p>By default, visibility of returned entities 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.
+ *
+ * <p>Instances are one-time-use. Other singleton classes should inject a Provider rather than
+ * holding on to a single instance.
  */
 public class InternalQuery<T> {
   private final QueryProcessor<T> queryProcessor;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/QueryProcessor.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/QueryProcessor.java
index 65f9721..af814a6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/QueryProcessor.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/QueryProcessor.java
@@ -51,8 +51,15 @@
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.stream.IntStream;
 
+/**
+ * Lower-level implementation for executing a single query over a secondary index.
+ *
+ * <p>Instances are one-time-use. Other singleton classes should inject a Provider rather than
+ * holding on to a single instance.
+ */
 public abstract class QueryProcessor<T> {
   @Singleton
   protected static class Metrics {
@@ -81,6 +88,10 @@
   private final IndexRewriter<T> rewriter;
   private final String limitField;
 
+  // This class is not generally thread-safe, but programmer error may result in it being shared
+  // across threads. At least ensure the bit for checking if it's been used is threadsafe.
+  private final AtomicBoolean used;
+
   protected int start;
 
   private boolean enforceVisibility = true;
@@ -104,6 +115,7 @@
     this.indexes = indexes;
     this.rewriter = rewriter;
     this.limitField = limitField;
+    this.used = new AtomicBoolean(false);
   }
 
   public QueryProcessor<T> setStart(int n) {
@@ -180,6 +192,7 @@
       @Nullable List<String> queryStrings, List<Predicate<T>> queries)
       throws OrmException, QueryParseException {
     long startNanos = System.nanoTime();
+    checkState(!used.getAndSet(true), "%s has already been used", getClass().getSimpleName());
     int cnt = queries.size();
     if (queryStrings != null) {
       int qs = queryStrings.size();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountQueryProcessor.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountQueryProcessor.java
index dd092c7..67f62a4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountQueryProcessor.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/account/AccountQueryProcessor.java
@@ -32,6 +32,12 @@
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
+/**
+ * Query processor for the account index.
+ *
+ * <p>Instances are one-time-use. Other singleton classes should inject a Provider rather than
+ * holding on to a single instance.
+ */
 public class AccountQueryProcessor extends QueryProcessor<AccountState> {
   private final AccountControl.Factory accountControlFactory;
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/account/InternalAccountQuery.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
index 46e7d2a..153f115 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
@@ -34,6 +34,12 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/**
+ * Query wrapper for the account index.
+ *
+ * <p>Instances are one-time-use. Other singleton classes should inject a Provider rather than
+ * holding on to a single instance.
+ */
 public class InternalAccountQuery extends InternalQuery<AccountState> {
   private static final Logger log = LoggerFactory.getLogger(InternalAccountQuery.class);
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryProcessor.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryProcessor.java
index d3ec68f..3759a67 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryProcessor.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryProcessor.java
@@ -40,6 +40,12 @@
 import java.util.List;
 import java.util.Set;
 
+/**
+ * Query processor for the change index.
+ *
+ * <p>Instances are one-time-use. Other singleton classes should inject a Provider rather than
+ * holding on to a single instance.
+ */
 public class ChangeQueryProcessor extends QueryProcessor<ChangeData>
     implements PluginDefinedAttributesFactory {
   /**
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/InternalChangeQuery.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
index 3c3b223..1ddfd5a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
@@ -46,6 +46,12 @@
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
 
+/**
+ * Query wrapper for the change index.
+ *
+ * <p>Instances are one-time-use. Other singleton classes should inject a Provider rather than
+ * holding on to a single instance.
+ */
 public class InternalChangeQuery extends InternalQuery<ChangeData> {
   private static Predicate<ChangeData> ref(Branch.NameKey branch) {
     return new RefPredicate(branch.get());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
index 1522fcf..91d7dbc 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
@@ -59,7 +59,12 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-/** Change query implementation that outputs to a stream in the style of an SSH command. */
+/**
+ * Change query implementation that outputs to a stream in the style of an SSH command.
+ *
+ * <p>Instances are one-time-use. Other singleton classes should inject a Provider rather than
+ * holding on to a single instance.
+ */
 public class OutputStreamQuery {
   private static final Logger log = LoggerFactory.getLogger(OutputStreamQuery.class);
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/group/GroupQueryProcessor.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/group/GroupQueryProcessor.java
index d5534e2..4abb7aa 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/group/GroupQueryProcessor.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/group/GroupQueryProcessor.java
@@ -32,6 +32,12 @@
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
+/**
+ * Query processor for the group index.
+ *
+ * <p>Instances are one-time-use. Other singleton classes should inject a Provider rather than
+ * holding on to a single instance.
+ */
 public class GroupQueryProcessor extends QueryProcessor<AccountGroup> {
   private final GroupControl.GenericFactory groupControlFactory;
 
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java
index 8323051..c5eea0f 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java
@@ -104,7 +104,7 @@
 
   @Inject protected OneOffRequestContext oneOffRequestContext;
 
-  @Inject protected InternalAccountQuery internalAccountQuery;
+  @Inject protected Provider<InternalAccountQuery> queryProvider;
 
   @Inject protected AllProjectsName allProjects;
 
@@ -294,19 +294,19 @@
     AccountInfo user2 = newAccountWithFullName("jroe", "Jane Roe");
     AccountInfo user3 = newAccountWithFullName("user3", "Mr Selfish");
 
-    assertThat(internalAccountQuery.byWatchedProject(p)).isEmpty();
+    assertThat(queryProvider.get().byWatchedProject(p)).isEmpty();
 
     watch(user1, p, null);
-    assertAccounts(internalAccountQuery.byWatchedProject(p), user1);
+    assertAccounts(queryProvider.get().byWatchedProject(p), user1);
 
     watch(user2, p, "keyword");
-    assertAccounts(internalAccountQuery.byWatchedProject(p), user1, user2);
+    assertAccounts(queryProvider.get().byWatchedProject(p), user1, user2);
 
     watch(user3, p2, "keyword");
     watch(user3, allProjects, "keyword");
-    assertAccounts(internalAccountQuery.byWatchedProject(p), user1, user2);
-    assertAccounts(internalAccountQuery.byWatchedProject(p2), user3);
-    assertAccounts(internalAccountQuery.byWatchedProject(allProjects), user3);
+    assertAccounts(queryProvider.get().byWatchedProject(p), user1, user2);
+    assertAccounts(queryProvider.get().byWatchedProject(p2), user3);
+    assertAccounts(queryProvider.get().byWatchedProject(allProjects), user3);
   }
 
   @Test
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index 3a1e1233..9ab4db0 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -167,13 +167,13 @@
   @Inject protected ChangeIndexer indexer;
   @Inject protected IndexConfig indexConfig;
   @Inject protected InMemoryRepositoryManager repoManager;
-  @Inject protected InternalChangeQuery internalChangeQuery;
+  @Inject protected Provider<InternalChangeQuery> queryProvider;
   @Inject protected ChangeNotes.Factory notesFactory;
   @Inject protected OneOffRequestContext oneOffRequestContext;
   @Inject protected PatchSetInserter.Factory patchSetFactory;
   @Inject protected PatchSetUtil psUtil;
   @Inject protected ChangeControl.GenericFactory changeControlFactory;
-  @Inject protected ChangeQueryProcessor queryProcessor;
+  @Inject protected Provider<ChangeQueryProcessor> queryProcessorProvider;
   @Inject protected SchemaCreator schemaCreator;
   @Inject protected SchemaFactory<ReviewDb> schemaFactory;
   @Inject protected Sequences seq;
@@ -2011,7 +2011,7 @@
 
     for (int i = 1; i <= 11; i++) {
       Iterable<ChangeData> cds =
-          internalChangeQuery.byCommitsOnBranchNotMerged(repo.getRepository(), db, dest, shas, i);
+          queryProvider.get().byCommitsOnBranchNotMerged(repo.getRepository(), db, dest, shas, i);
       Iterable<Integer> ids = FluentIterable.from(cds).transform(in -> in.getId().get());
       String name = "limit " + i;
       assertThat(ids).named(name).hasSize(n);
@@ -2029,7 +2029,10 @@
     requestContext.setContext(newRequestContext(userId));
     // Use QueryProcessor directly instead of API so we get ChangeDatas back.
     List<ChangeData> cds =
-        queryProcessor.query(queryBuilder.parse(change.getId().toString())).entities();
+        queryProcessorProvider
+            .get()
+            .query(queryBuilder.parse(change.getId().toString()))
+            .entities();
     assertThat(cds).hasSize(1);
 
     ChangeData cd = cds.get(0);
@@ -2059,7 +2062,8 @@
     requestContext.setContext(newRequestContext(userId));
     // Use QueryProcessor directly instead of API so we get ChangeDatas back.
     List<ChangeData> cds =
-        queryProcessor
+        queryProcessorProvider
+            .get()
             .setRequestedFields(
                 ImmutableSet.of(ChangeField.PATCH_SET.getName(), ChangeField.CHANGE.getName()))
             .query(queryBuilder.parse(change.getId().toString()))
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java
index 96f573f..db89eda 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java
@@ -40,7 +40,6 @@
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.group.GroupsUpdate;
 import com.google.gerrit.server.group.ServerInitiated;
-import com.google.gerrit.server.query.account.InternalAccountQuery;
 import com.google.gerrit.server.schema.SchemaCreator;
 import com.google.gerrit.server.util.ManualRequestContext;
 import com.google.gerrit.server.util.OneOffRequestContext;
@@ -94,8 +93,6 @@
 
   @Inject protected OneOffRequestContext oneOffRequestContext;
 
-  @Inject protected InternalAccountQuery internalAccountQuery;
-
   @Inject protected AllProjectsName allProjects;
 
   @Inject protected GroupCache groupCache;
diff --git a/plugins/singleusergroup b/plugins/singleusergroup
index 1f28eff..89e92bc 160000
--- a/plugins/singleusergroup
+++ b/plugins/singleusergroup
@@ -1 +1 @@
-Subproject commit 1f28effbe7878e51776f63af4e2541183a445286
+Subproject commit 89e92bc36f04184a709cf16fc9773eebba8d9c0f