Merge "Refactor AbstractDaemonTest class."
diff --git a/java/com/google/gerrit/common/UsedAt.java b/java/com/google/gerrit/common/UsedAt.java
index 5ea5177..83af551 100644
--- a/java/com/google/gerrit/common/UsedAt.java
+++ b/java/com/google/gerrit/common/UsedAt.java
@@ -41,6 +41,7 @@
     PLUGIN_CHECKS,
     PLUGIN_CODE_OWNERS,
     PLUGIN_DELETE_PROJECT,
+    PLUGIN_GITHUB,
     PLUGIN_HIGH_AVAILABILITY,
     PLUGIN_MULTI_SITE,
     PLUGIN_PULL_REPLICATION,
diff --git a/java/com/google/gerrit/metrics/Timer0.java b/java/com/google/gerrit/metrics/Timer0.java
index 72ebc67..d59595a 100644
--- a/java/com/google/gerrit/metrics/Timer0.java
+++ b/java/com/google/gerrit/metrics/Timer0.java
@@ -49,8 +49,6 @@
     }
   }
 
-  private boolean suppressLogging;
-
   protected final String name;
 
   public Timer0(String name) {
@@ -76,22 +74,14 @@
   public final void record(long value, TimeUnit unit) {
     long durationMs = unit.toMillis(value);
 
-    if (!suppressLogging) {
-      LoggingContext.getInstance()
-          .addPerformanceLogRecord(() -> PerformanceLogRecord.create(name, durationMs));
-      logger.atFinest().log("%s took %dms", name, durationMs);
-    }
+    LoggingContext.getInstance()
+        .addPerformanceLogRecord(() -> PerformanceLogRecord.create(name, durationMs));
+    logger.atFinest().log("%s took %dms", name, durationMs);
 
     doRecord(value, unit);
     RequestStateContext.abortIfCancelled();
   }
 
-  /** Suppress logging (debug log and performance log) when values are recorded. */
-  public final Timer0 suppressLogging() {
-    this.suppressLogging = true;
-    return this;
-  }
-
   /**
    * Record a value in the distribution.
    *
diff --git a/java/com/google/gerrit/server/logging/CallerFinder.java b/java/com/google/gerrit/server/logging/CallerFinder.java
index 4cb4b7f..a6c2131 100644
--- a/java/com/google/gerrit/server/logging/CallerFinder.java
+++ b/java/com/google/gerrit/server/logging/CallerFinder.java
@@ -195,15 +195,17 @@
     public abstract CallerFinder build();
   }
 
+  public String findCaller() {
+    return targets().stream()
+        .map(t -> findCallerOf(t, skip() + 1))
+        .filter(Optional::isPresent)
+        .findFirst()
+        .map(Optional::get)
+        .orElse("unknown");
+  }
+
   public LazyArg<String> findCallerLazy() {
-    return lazy(
-        () ->
-            targets().stream()
-                .map(t -> findCallerOf(t, skip() + 1))
-                .filter(Optional::isPresent)
-                .findFirst()
-                .map(Optional::get)
-                .orElse("unknown"));
+    return lazy(() -> findCaller());
   }
 
   private Optional<String> findCallerOf(Class<?> target, int skip) {
diff --git a/java/com/google/gerrit/server/logging/Metadata.java b/java/com/google/gerrit/server/logging/Metadata.java
index 381f1ca..b7f3404 100644
--- a/java/com/google/gerrit/server/logging/Metadata.java
+++ b/java/com/google/gerrit/server/logging/Metadata.java
@@ -41,6 +41,12 @@
    */
   public abstract Optional<String> actionType();
 
+  /**
+   * Number of attempt. The first execution has {@code attempt=1}, the first retry has {@code
+   * attempt=2}.
+   */
+  public abstract Optional<Integer> attempt();
+
   /** An authentication domain name. */
   public abstract Optional<String> authDomainName();
 
@@ -53,6 +59,9 @@
   /** The name of a cache. */
   public abstract Optional<String> cacheName();
 
+  /** The caller that triggered the operation. */
+  public abstract Optional<String> caller();
+
   /** The name of the implementation class. */
   public abstract Optional<String> className();
 
@@ -186,20 +195,20 @@
    * few are populated this leads to long string representations such as
    *
    * <pre>
-   * Metadata{accountId=Optional.empty, actionType=Optional.empty, authDomainName=Optional.empty,
-   * branchName=Optional.empty, cacheKey=Optional.empty, cacheName=Optional.empty,
-   * className=Optional.empty, cancellationReason=Optional.empty changeId=Optional[9212550],
-   * changeIdType=Optional.empty, cause=Optional.empty, diffAlgorithm=Optional.empty,
-   * eventType=Optional.empty, exportValue=Optional.empty, filePath=Optional.empty,
-   * garbageCollectorName=Optional.empty, gitOperation=Optional.empty, groupId=Optional.empty,
-   * groupName=Optional.empty, groupUuid=Optional.empty, httpStatus=Optional.empty,
-   * indexName=Optional.empty, indexVersion=Optional[0], methodName=Optional.empty,
-   * multiple=Optional.empty, operationName=Optional.empty, partial=Optional.empty,
-   * noteDbFilePath=Optional.empty, noteDbRefName=Optional.empty,
-   * noteDbSequenceType=Optional.empty, patchSetId=Optional.empty, pluginMetadata=[],
-   * pluginName=Optional.empty, projectName=Optional.empty, pushType=Optional.empty,
-   * requestType=Optional.empty, resourceCount=Optional.empty, restViewName=Optional.empty,
-   * revision=Optional.empty, username=Optional.empty}
+   * Metadata{accountId=Optional.empty, actionType=Optional.empty, attempt=Optional.empty,
+   * authDomainName=Optional.empty, branchName=Optional.empty, cacheKey=Optional.empty,
+   * cacheName=Optional.empty, caller=Optional.empty, className=Optional.empty,
+   * cancellationReason=Optional.empty, changeId=Optional[9212550], changeIdType=Optional.empty,
+   * cause=Optional.empty, diffAlgorithm=Optional.empty, eventType=Optional.empty,
+   * exportValue=Optional.empty, filePath=Optional.empty, garbageCollectorName=Optional.empty,
+   * gitOperation=Optional.empty, groupId=Optional.empty, groupName=Optional.empty,
+   * groupUuid=Optional.empty, httpStatus=Optional.empty, indexName=Optional.empty,
+   * indexVersion=Optional[0], methodName=Optional.empty, multiple=Optional.empty,
+   * operationName=Optional.empty, partial=Optional.empty, noteDbFilePath=Optional.empty,
+   * noteDbRefName=Optional.empty, noteDbSequenceType=Optional.empty, patchSetId=Optional.empty,
+   * pluginMetadata=[], pluginName=Optional.empty, projectName=Optional.empty,
+   * pushType=Optional.empty, requestType=Optional.empty, resourceCount=Optional.empty,
+   * restViewName=Optional.empty, revision=Optional.empty, username=Optional.empty}
    * </pre>
    *
    * <p>That's hard to read in logs. This is why this method
@@ -294,6 +303,8 @@
 
     public abstract Builder actionType(@Nullable String actionType);
 
+    public abstract Builder attempt(int attempt);
+
     public abstract Builder authDomainName(@Nullable String authDomainName);
 
     public abstract Builder branchName(@Nullable String branchName);
@@ -302,6 +313,8 @@
 
     public abstract Builder cacheName(@Nullable String cacheName);
 
+    public abstract Builder caller(@Nullable String caller);
+
     public abstract Builder className(@Nullable String className);
 
     public abstract Builder cancellationReason(@Nullable String cancellationReason);
diff --git a/java/com/google/gerrit/server/project/SubmitRequirementsEvaluatorImpl.java b/java/com/google/gerrit/server/project/SubmitRequirementsEvaluatorImpl.java
index 995bdcb..d4db78a 100644
--- a/java/com/google/gerrit/server/project/SubmitRequirementsEvaluatorImpl.java
+++ b/java/com/google/gerrit/server/project/SubmitRequirementsEvaluatorImpl.java
@@ -27,6 +27,9 @@
 import com.google.gerrit.entities.SubmitRequirementResult;
 import com.google.gerrit.index.query.Predicate;
 import com.google.gerrit.index.query.QueryParseException;
+import com.google.gerrit.server.logging.Metadata;
+import com.google.gerrit.server.logging.TraceContext;
+import com.google.gerrit.server.logging.TraceContext.TraceTimer;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.SubmitRequirementChangeQueryBuilder;
@@ -181,31 +184,36 @@
    * SubmitRequirement#allowOverrideInChildProjects} of global {@link SubmitRequirement}.
    */
   private ImmutableMap<SubmitRequirement, SubmitRequirementResult> getRequirements(ChangeData cd) {
-    ImmutableMap<String, SubmitRequirement> globalRequirements = getGlobalRequirements();
+    try (TraceTimer timer =
+        TraceContext.newTimer(
+            "Get submit requirements",
+            Metadata.builder().changeId(cd.change().getId().get()).build())) {
+      ImmutableMap<String, SubmitRequirement> globalRequirements = getGlobalRequirements();
 
-    ProjectState state = projectCache.get(cd.project()).orElseThrow(illegalState(cd.project()));
-    Map<String, SubmitRequirement> projectConfigRequirements = state.getSubmitRequirements();
+      ProjectState state = projectCache.get(cd.project()).orElseThrow(illegalState(cd.project()));
+      Map<String, SubmitRequirement> projectConfigRequirements = state.getSubmitRequirements();
 
-    ImmutableMap<String, SubmitRequirement> requirements =
-        Stream.concat(
-                globalRequirements.entrySet().stream(),
-                projectConfigRequirements.entrySet().stream())
-            .collect(
-                toImmutableMap(
-                    Map.Entry::getKey,
-                    Map.Entry::getValue,
-                    (globalSubmitRequirement, projectConfigRequirement) ->
-                        // Override with projectConfigRequirement if allowed by
-                        // globalSubmitRequirement configuration
-                        globalSubmitRequirement.allowOverrideInChildProjects()
-                            ? projectConfigRequirement
-                            : globalSubmitRequirement));
-    ImmutableMap.Builder<SubmitRequirement, SubmitRequirementResult> results =
-        ImmutableMap.builder();
-    for (SubmitRequirement requirement : requirements.values()) {
-      results.put(requirement, evaluateRequirementInternal(requirement, cd));
+      ImmutableMap<String, SubmitRequirement> requirements =
+          Stream.concat(
+                  globalRequirements.entrySet().stream(),
+                  projectConfigRequirements.entrySet().stream())
+              .collect(
+                  toImmutableMap(
+                      Map.Entry::getKey,
+                      Map.Entry::getValue,
+                      (globalSubmitRequirement, projectConfigRequirement) ->
+                          // Override with projectConfigRequirement if allowed by
+                          // globalSubmitRequirement configuration
+                          globalSubmitRequirement.allowOverrideInChildProjects()
+                              ? projectConfigRequirement
+                              : globalSubmitRequirement));
+      ImmutableMap.Builder<SubmitRequirement, SubmitRequirementResult> results =
+          ImmutableMap.builder();
+      for (SubmitRequirement requirement : requirements.values()) {
+        results.put(requirement, evaluateRequirementInternal(requirement, cd));
+      }
+      return results.build();
     }
-    return results.build();
   }
 
   /**
diff --git a/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java b/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
index 866ce14..9af80bd 100644
--- a/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
+++ b/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
@@ -17,7 +17,6 @@
 import static com.google.common.collect.ImmutableList.toImmutableList;
 
 import com.google.common.collect.Streams;
-import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.SubmitRecord;
 import com.google.gerrit.entities.SubmitTypeRecord;
@@ -30,6 +29,9 @@
 import com.google.gerrit.server.change.ChangeJson;
 import com.google.gerrit.server.index.OnlineReindexMode;
 import com.google.gerrit.server.logging.CallerFinder;
+import com.google.gerrit.server.logging.Metadata;
+import com.google.gerrit.server.logging.TraceContext;
+import com.google.gerrit.server.logging.TraceContext.TraceTimer;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.rules.DefaultSubmitRule;
@@ -46,8 +48,6 @@
  * the results through rules found in the parent projects, all the way up to All-Projects.
  */
 public class SubmitRuleEvaluator {
-  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
   public interface Factory {
     /** Returns a new {@link SubmitRuleEvaluator} with the specified options */
     SubmitRuleEvaluator create(SubmitRuleOptions options);
@@ -113,10 +113,14 @@
    * @param cd ChangeData to evaluate
    */
   public List<SubmitRecord> evaluate(ChangeData cd) {
-    logger.atFine().log(
-        "Evaluate submit rules for change %d (caller: %s)",
-        cd.change().getId().get(), callerFinder.findCallerLazy());
-    try (Timer0.Context ignored = metrics.submitRuleEvaluationLatency.start()) {
+    try (TraceTimer timer =
+            TraceContext.newTimer(
+                "Evaluate submit rules",
+                Metadata.builder()
+                    .changeId(cd.change().getId().get())
+                    .caller(callerFinder.findCaller())
+                    .build());
+        Timer0.Context ignored = metrics.submitRuleEvaluationLatency.start()) {
       if (cd.change() == null) {
         throw new StorageException("Change not found");
       }
diff --git a/java/com/google/gerrit/server/update/RetryableAction.java b/java/com/google/gerrit/server/update/RetryableAction.java
index b0fe112..1a713d2 100644
--- a/java/com/google/gerrit/server/update/RetryableAction.java
+++ b/java/com/google/gerrit/server/update/RetryableAction.java
@@ -20,6 +20,9 @@
 import com.google.common.base.Throwables;
 import com.google.errorprone.annotations.CanIgnoreReturnValue;
 import com.google.gerrit.server.ExceptionHook;
+import com.google.gerrit.server.logging.Metadata;
+import com.google.gerrit.server.logging.TraceContext;
+import com.google.gerrit.server.logging.TraceContext.TraceTimer;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.function.Consumer;
@@ -72,6 +75,8 @@
   private final RetryHelper.Options.Builder options = RetryHelper.options();
   private final List<Predicate<Throwable>> exceptionPredicates = new ArrayList<>();
 
+  private int numberOfCalls;
+
   RetryableAction(
       RetryHelper retryHelper, ActionType actionType, String actionName, Action<T> action) {
     this(retryHelper, requireNonNull(actionType, "actionType").name(), actionName, action);
@@ -80,7 +85,15 @@
   RetryableAction(RetryHelper retryHelper, String actionType, String actionName, Action<T> action) {
     this.retryHelper = requireNonNull(retryHelper, "retryHelper");
     this.actionType = requireNonNull(actionType, "actionType");
-    this.action = requireNonNull(action, "action");
+    this.action =
+        () -> {
+          numberOfCalls++;
+          try (TraceTimer timer =
+              TraceContext.newTimer(
+                  actionName, Metadata.builder().attempt(numberOfCalls).build())) {
+            return requireNonNull(action, "action").call();
+          }
+        };
     options.actionName(requireNonNull(actionName, "actionName"));
   }
 
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index edf21ba..01c5710 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -352,8 +352,13 @@
   }
 
   @UsedAt(UsedAt.Project.GOOGLE)
+  protected AccountIndexedCounter getAccountIndexedCounter() {
+    return new AccountIndexedCounter();
+  }
+
+  @UsedAt(UsedAt.Project.GOOGLE)
   protected Account.Id createByAccountCreator(int expectedAccountReindexCalls) throws Exception {
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       String name = "foo";
@@ -373,7 +378,7 @@
 
   @Test
   public void createAnonymousCowardByAccountCreator() throws Exception {
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       TestAccount anonymousCoward = accountCreator.create();
@@ -384,7 +389,7 @@
 
   @Test
   public void create() throws Exception {
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       AccountInput input = new AccountInput();
@@ -549,7 +554,7 @@
 
   @Test
   public void get() throws Exception {
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       AccountInfo info = gApi.accounts().id(admin.id().get()).get();
@@ -564,7 +569,7 @@
 
   @Test
   public void getByIntId() throws Exception {
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       AccountInfo info = gApi.accounts().id(admin.id().get()).get();
@@ -576,7 +581,7 @@
 
   @Test
   public void self() throws Exception {
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       AccountInfo info = gApi.accounts().self().get();
@@ -590,7 +595,7 @@
 
   @Test
   public void active() throws Exception {
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       int id = gApi.accounts().id(user.id().get()).get()._accountId;
@@ -620,7 +625,7 @@
 
   @Test
   public void shouldAllowQueryByEmailForInactiveUser() throws Exception {
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       Account.Id activatableAccountId =
@@ -634,7 +639,7 @@
 
   @Test
   public void shouldAllowQueryByUserNameForInactiveUser() throws Exception {
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       Account.Id activatableAccountId =
@@ -798,7 +803,7 @@
 
   @Test
   public void starUnstarChange() throws Exception {
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     RefUpdateCounter refUpdateCounter = new RefUpdateCounter(server.isRefSequenceSupported());
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter).add(refUpdateCounter)) {
@@ -919,7 +924,7 @@
 
   @Test
   public void suggestAccounts() throws Exception {
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       String adminUsername = "admin";
@@ -1040,7 +1045,7 @@
 
   @Test
   public void addEmail() throws Exception {
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       ImmutableList<String> emails =
@@ -1073,7 +1078,7 @@
 
             // Non-supported TLD  (see tlds-alpha-by-domain.txt)
             "new.email@example.africa");
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       for (String email : emails) {
@@ -1163,7 +1168,7 @@
 
   @Test
   public void addEmailAndSetPreferred() throws Exception {
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       String email = "foo.bar@example.com";
@@ -1184,7 +1189,7 @@
 
   @Test
   public void deleteEmail() throws Exception {
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       String email = "foo.bar@example.com";
@@ -1205,7 +1210,7 @@
 
   @Test
   public void deletePreferredEmail() throws Exception {
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       ImmutableSet<String> previous = getEmails();
@@ -1256,7 +1261,7 @@
 
   @Test
   public void deleteEmailFromCustomExternalIdSchemes() throws Exception {
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       String email = "foo.bar@example.com";
@@ -1373,7 +1378,7 @@
 
   @Test
   public void deleteEmailOfOtherUser() throws Exception {
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       String email = "foo.bar@example.com";
@@ -1466,7 +1471,7 @@
   public void putStatus() throws Exception {
     ImmutableList<String> statuses = ImmutableList.of("OOO", "Busy");
     AccountInfo info;
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       for (String status : statuses) {
@@ -1516,7 +1521,7 @@
 
   @Test
   public void fetchUserBranch() throws Exception {
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       requestScopeOperations.setApiUser(user.id());
@@ -1723,7 +1728,7 @@
 
   @Test
   public void addOtherUsersGpgKey_Conflict() throws Exception {
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       // Both users have a matching external ID for this key.
@@ -1752,7 +1757,7 @@
 
   @Test
   public void listGpgKeys() throws Exception {
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       ImmutableList<TestKey> keys = allValidKeys();
@@ -1771,7 +1776,7 @@
 
   @Test
   public void deleteGpgKey() throws Exception {
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       TestKey key = validKeyWithoutExpiration();
@@ -1797,7 +1802,7 @@
 
   @Test
   public void addAndRemoveGpgKeys() throws Exception {
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       for (TestKey key : allValidKeys()) {
@@ -1856,7 +1861,7 @@
   @Test
   @UseSsh
   public void sshKeys() throws Exception {
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       // The test account should initially have exactly one ssh key
@@ -1929,7 +1934,7 @@
   @Test
   @UseSsh
   public void adminCanAddOrRemoveSshKeyOnOtherAccount() throws Exception {
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       // The test account should initially have exactly one ssh key
@@ -1990,7 +1995,7 @@
   // reindex is tested by {@link AbstractQueryAccountsTest#reindex}
   @Test
   public void reindexPermissions() throws Exception {
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       // admin can reindex any account
@@ -3050,7 +3055,7 @@
     AccountsUpdate.UpdateArguments ua2 =
         new AccountsUpdate.UpdateArguments(
             "Add External ID", user.id(), (a, u) -> u.addExternalId(extId2));
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       accountsUpdateProvider.get().updateBatch(ImmutableList.of(ua1, ua2));
@@ -3092,7 +3097,7 @@
             "Remove external Id",
             user.id(),
             (a, u) -> u.deleteExternalId(createEmailExternalId(user.id(), user.email())));
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       accountsUpdateProvider.get().updateBatch(ImmutableList.of(ua1, ua2));
@@ -3582,7 +3587,7 @@
   }
 
   private void addExternalIdEmail(TestAccount account, String email) throws Exception {
-    AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+    AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
     try (Registration registration =
         extensionRegistry.newRegistration().add(accountIndexedCounter)) {
       requireNonNull(email);
@@ -3608,7 +3613,7 @@
   private Map<String, GpgKeyInfo> addGpgKey(TestAccount account, String armored) throws Exception {
     return testRefAction(
         () -> {
-          AccountIndexedCounter accountIndexedCounter = new AccountIndexedCounter();
+          AccountIndexedCounter accountIndexedCounter = getAccountIndexedCounter();
           try (Registration registration =
               extensionRegistry.newRegistration().add(accountIndexedCounter)) {
             Map<String, GpgKeyInfo> gpgKeys =
diff --git a/plugins/replication b/plugins/replication
index 56ea643..d91399e 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit 56ea64394e8a58b213c0d859c219036fe6c78b0c
+Subproject commit d91399e3ff6205e22d065dbe8757f5128890bfac