Extract RepoSequence into an interface and an implementation

Introduce a new IncrementingSequence interface and make RepoSequence an
implementation of it. This will allow us to swap the NoteDb
implementation with a different one. This change is a no-op.

The "lightweight" providers for accounts and groups sequences were
implemented because the usage of sequences in SequencesOnInit and
SchemaCreatorImpl cannot be substituted with an injected RepoSequence
because of the GitReferenceUpdated (for example see comment in #1). Both
usages had explicitly passed a GitReferenceUpdated.DISABLED for this
reason. I introduced the lightweight providers for the same purpose.

[1] https://gerrit.googlesource.com/gerrit/+/50984464fa8373e3df9905343bb403ee112694d1/java/com/google/gerrit/server/schema/SchemaCreatorImpl.java#109

Google-Bug-Id: b/289356628
Release-Notes: skip
Forward-Compatible: checked
Change-Id: Id6a89e15cbf2ac4852ba703b7e10f4530033cb8c
diff --git a/java/com/google/gerrit/pgm/init/InitModule.java b/java/com/google/gerrit/pgm/init/InitModule.java
index 473e3aa..a40a704 100644
--- a/java/com/google/gerrit/pgm/init/InitModule.java
+++ b/java/com/google/gerrit/pgm/init/InitModule.java
@@ -14,12 +14,19 @@
 
 package com.google.gerrit.pgm.init;
 
+import static com.google.gerrit.server.Sequence.LightweightAccounts;
+import static com.google.inject.Scopes.SINGLETON;
+
 import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.pgm.init.api.InitStep;
 import com.google.gerrit.pgm.init.api.Section;
+import com.google.gerrit.server.Sequence;
 import com.google.gerrit.server.account.externalids.ExternalIdFactory;
 import com.google.gerrit.server.account.externalids.storage.notedb.ExternalIdFactoryNoteDbImpl;
+import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.config.AllUsersNameProvider;
 import com.google.gerrit.server.config.SitePaths;
+import com.google.gerrit.server.notedb.RepoSequence.DisabledGitRefUpdatedRepoAccountsSequenceProvider;
 import com.google.inject.Singleton;
 import com.google.inject.binder.LinkedBindingBuilder;
 import com.google.inject.internal.UniqueAnnotations;
@@ -37,6 +44,10 @@
   @Override
   protected void configure() {
     bind(SitePaths.class);
+    bind(AllUsersName.class).toProvider(AllUsersNameProvider.class).in(SINGLETON);
+    bind(Sequence.class)
+        .annotatedWith(LightweightAccounts.class)
+        .toProvider(DisabledGitRefUpdatedRepoAccountsSequenceProvider.class);
     factory(Section.Factory.class);
     factory(VersionedAuthorizedKeysOnInit.Factory.class);
 
diff --git a/java/com/google/gerrit/pgm/init/api/SequencesOnInit.java b/java/com/google/gerrit/pgm/init/api/SequencesOnInit.java
index c11230c..f6e2b9c 100644
--- a/java/com/google/gerrit/pgm/init/api/SequencesOnInit.java
+++ b/java/com/google/gerrit/pgm/init/api/SequencesOnInit.java
@@ -14,34 +14,22 @@
 
 package com.google.gerrit.pgm.init.api;
 
-import com.google.gerrit.entities.Project;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.notedb.RepoSequence;
-import com.google.gerrit.server.notedb.Sequences;
+import static com.google.gerrit.server.Sequence.LightweightAccounts;
+
+import com.google.gerrit.server.Sequence;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
 @Singleton
 public class SequencesOnInit {
-  private final GitRepositoryManager repoManager;
-  private final AllUsersNameOnInitProvider allUsersName;
+  private final Sequence accountsSequence;
 
   @Inject
-  SequencesOnInit(GitRepositoryManagerOnInit repoManager, AllUsersNameOnInitProvider allUsersName) {
-    this.repoManager = repoManager;
-    this.allUsersName = allUsersName;
+  SequencesOnInit(@LightweightAccounts Sequence accountsSequence) {
+    this.accountsSequence = accountsSequence;
   }
 
   public int nextAccountId() {
-    RepoSequence accountSeq =
-        new RepoSequence(
-            repoManager,
-            GitReferenceUpdated.DISABLED,
-            Project.nameKey(allUsersName.get()),
-            Sequences.NAME_ACCOUNTS,
-            () -> Sequences.FIRST_ACCOUNT_ID,
-            1);
-    return accountSeq.next();
+    return accountsSequence.next();
   }
 }
diff --git a/java/com/google/gerrit/pgm/util/BatchProgramModule.java b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
index 82d1dda..718fa69 100644
--- a/java/com/google/gerrit/pgm/util/BatchProgramModule.java
+++ b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
@@ -74,6 +74,7 @@
 import com.google.gerrit.server.git.TagCache;
 import com.google.gerrit.server.notedb.ChangeDraftNotesUpdate;
 import com.google.gerrit.server.notedb.NoteDbModule;
+import com.google.gerrit.server.notedb.RepoSequence.RepoSequenceModule;
 import com.google.gerrit.server.patch.DiffExecutorModule;
 import com.google.gerrit.server.patch.DiffOperationsImpl;
 import com.google.gerrit.server.patch.PatchListCacheImpl;
@@ -196,6 +197,7 @@
     modules.add(TagCache.module());
     modules.add(PureRevertCache.module());
     modules.add(new ApprovalModule());
+    modules.add(new RepoSequenceModule());
     modules.add(SubmitRequirementsEvaluatorImpl.module());
     factory(CapabilityCollection.Factory.class);
     factory(ChangeData.AssistedFactory.class);
diff --git a/java/com/google/gerrit/server/Sequence.java b/java/com/google/gerrit/server/Sequence.java
new file mode 100644
index 0000000..844b583
--- /dev/null
+++ b/java/com/google/gerrit/server/Sequence.java
@@ -0,0 +1,82 @@
+// Copyright (C) 2023 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;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.common.collect.ImmutableList;
+import com.google.inject.BindingAnnotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * An incrementing sequence that's used to assign new unique numbers for change, account and group
+ * IDs.
+ */
+public interface Sequence {
+  String NAME_ACCOUNTS = "accounts";
+  String NAME_GROUPS = "groups";
+  String NAME_CHANGES = "changes";
+
+  /**
+   * Some callers cannot get the normal {@link #NAME_ACCOUNTS} sequence injected because some
+   * injected fields are not available at injection time. Allow for providing a light-weight
+   * injected instance.
+   */
+  @BindingAnnotation
+  @Target({FIELD, PARAMETER, METHOD})
+  @Retention(RUNTIME)
+  @interface LightweightAccounts {}
+
+  /**
+   * Some callers cannot get the normal {@link #NAME_GROUPS} sequence injected because some injected
+   * fields are not available at injection time. Allow for providing a light-weight injected
+   * instance.
+   */
+  @BindingAnnotation
+  @Target({FIELD, PARAMETER, METHOD})
+  @Retention(RUNTIME)
+  @interface LightweightGroups {}
+
+  enum SequenceType {
+    ACCOUNTS,
+    CHANGES,
+    GROUPS;
+  }
+
+  /** Returns the next available sequence value and increments the sequence for the next call. */
+  int next();
+
+  /** Similar to {@link #next()} but returns a {@code count} of next available values. */
+  ImmutableList<Integer> next(int count);
+
+  /** Returns the next available sequence value. */
+  int current();
+
+  /** Returns the last sequence number that was assigned. */
+  int last();
+
+  /**
+   * Stores a new {@code value} to be returned on the next calls for {@link #next()} or {@link
+   * #current()}.
+   */
+  void storeNew(int value);
+
+  /** Returns the batch size that was used to initialize the sequence. */
+  int getBatchSize();
+}
diff --git a/java/com/google/gerrit/server/config/GerritGlobalModule.java b/java/com/google/gerrit/server/config/GerritGlobalModule.java
index 81d48ad..b5048b4 100644
--- a/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -174,6 +174,7 @@
 import com.google.gerrit.server.notedb.ChangeDraftNotesUpdate;
 import com.google.gerrit.server.notedb.DeleteZombieCommentsRefs;
 import com.google.gerrit.server.notedb.NoteDbModule;
+import com.google.gerrit.server.notedb.RepoSequence.RepoSequenceModule;
 import com.google.gerrit.server.notedb.StoreSubmitRequirementsOp;
 import com.google.gerrit.server.patch.DiffFileSizeValidator;
 import com.google.gerrit.server.patch.DiffOperationsImpl;
@@ -255,6 +256,7 @@
 
     bind(IdGenerator.class);
     bind(BlameCache.class).to(BlameCacheImpl.class);
+    install(new RepoSequenceModule());
     install(BatchUpdate.module());
     install(ChangeKindCacheImpl.module());
     install(ChangeFinder.module());
diff --git a/java/com/google/gerrit/server/notedb/RepoSequence.java b/java/com/google/gerrit/server/notedb/RepoSequence.java
index 53ee58b..38ce3a0 100644
--- a/java/com/google/gerrit/server/notedb/RepoSequence.java
+++ b/java/com/google/gerrit/server/notedb/RepoSequence.java
@@ -34,11 +34,20 @@
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.exceptions.StorageException;
+import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.git.LockFailureException;
 import com.google.gerrit.git.RefUpdateUtil;
+import com.google.gerrit.server.Sequence;
+import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.update.context.RefUpdateContext;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Provides;
+import com.google.inject.name.Named;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
@@ -47,6 +56,7 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
+import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.Repository;
@@ -62,7 +72,7 @@
  * memory until they run out. This means concurrent processes will hand out somewhat non-monotonic
  * numbers.
  */
-public class RepoSequence {
+public class RepoSequence implements Sequence {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
   @FunctionalInterface
@@ -70,6 +80,134 @@
     int get();
   }
 
+  public static class RepoSequenceModule extends FactoryModule {
+    private static final String SECTION_NOTE_DB = "noteDb";
+    private static final String KEY_SEQUENCE_BATCH_SIZE = "sequenceBatchSize";
+    private static final int DEFAULT_ACCOUNTS_SEQUENCE_BATCH_SIZE = 1;
+    private static final int DEFAULT_GROUPS_SEQUENCE_BATCH_SIZE = 1;
+    private static final int DEFAULT_CHANGES_SEQUENCE_BATCH_SIZE = 20;
+
+    @Provides
+    @Named(NAME_ACCOUNTS)
+    Sequence getAccountSequence(
+        @GerritServerConfig Config cfg,
+        GitRepositoryManager repoManager,
+        AllUsersName allUsers,
+        GitReferenceUpdated gitReferenceUpdated) {
+      int accountBatchSize =
+          cfg.getInt(
+              SECTION_NOTE_DB,
+              NAME_ACCOUNTS,
+              KEY_SEQUENCE_BATCH_SIZE,
+              DEFAULT_ACCOUNTS_SEQUENCE_BATCH_SIZE);
+      return new RepoSequence(
+          repoManager,
+          gitReferenceUpdated,
+          allUsers,
+          NAME_ACCOUNTS,
+          () -> Sequences.FIRST_ACCOUNT_ID,
+          accountBatchSize);
+    }
+
+    @Provides
+    @Named(NAME_GROUPS)
+    Sequence getGroupSequence(
+        GitRepositoryManager repoManager,
+        AllUsersName allUsers,
+        GitReferenceUpdated gitReferenceUpdated) {
+      return new RepoSequence(
+          repoManager,
+          gitReferenceUpdated,
+          allUsers,
+          NAME_GROUPS,
+          () -> Sequences.FIRST_GROUP_ID,
+          DEFAULT_GROUPS_SEQUENCE_BATCH_SIZE);
+    }
+
+    @Provides
+    @Named(NAME_CHANGES)
+    Sequence getChangesSequence(
+        @GerritServerConfig Config cfg,
+        GitRepositoryManager repoManager,
+        AllProjectsName allProjects,
+        GitReferenceUpdated gitReferenceUpdated) {
+      int changeBatchSize =
+          cfg.getInt(
+              SECTION_NOTE_DB,
+              NAME_CHANGES,
+              KEY_SEQUENCE_BATCH_SIZE,
+              DEFAULT_CHANGES_SEQUENCE_BATCH_SIZE);
+      return new RepoSequence(
+          repoManager,
+          gitReferenceUpdated,
+          allProjects,
+          NAME_CHANGES,
+          () -> Sequences.FIRST_CHANGE_ID,
+          changeBatchSize);
+    }
+  }
+
+  /** A groups sequence provider that does not fire git reference updates. */
+  public static class DisabledGitRefUpdatedRepoGroupsSequenceProvider
+      implements Provider<Sequence> {
+    private static final int DEFAULT_GROUPS_SEQUENCE_BATCH_SIZE = 1;
+    private final GitRepositoryManager repoManager;
+    private final AllUsersName allUsers;
+
+    @Inject
+    DisabledGitRefUpdatedRepoGroupsSequenceProvider(
+        GitRepositoryManager repoManager, AllUsersName allUsersName) {
+      this.repoManager = repoManager;
+      this.allUsers = allUsersName;
+    }
+
+    @Override
+    public Sequence get() {
+      return new RepoSequence(
+          repoManager,
+          GitReferenceUpdated.DISABLED,
+          allUsers,
+          NAME_GROUPS,
+          () -> Sequences.FIRST_GROUP_ID,
+          DEFAULT_GROUPS_SEQUENCE_BATCH_SIZE);
+    }
+  }
+
+  /** A accounts sequence provider that does not fire git reference updates. */
+  public static class DisabledGitRefUpdatedRepoAccountsSequenceProvider
+      implements Provider<Sequence> {
+    private final GitRepositoryManager repoManager;
+    private final AllUsersName allUsers;
+    private final Config cfg;
+
+    @Inject
+    DisabledGitRefUpdatedRepoAccountsSequenceProvider(
+        @GerritServerConfig Config cfg,
+        GitRepositoryManager repoManager,
+        AllUsersName allUsersName) {
+      this.repoManager = repoManager;
+      this.allUsers = allUsersName;
+      this.cfg = cfg;
+    }
+
+    @Override
+    public Sequence get() {
+      int accountBatchSize =
+          cfg.getInt(
+              RepoSequenceModule.SECTION_NOTE_DB,
+              NAME_ACCOUNTS,
+              RepoSequenceModule.KEY_SEQUENCE_BATCH_SIZE,
+              RepoSequenceModule.DEFAULT_ACCOUNTS_SEQUENCE_BATCH_SIZE);
+      return new RepoSequence(
+          repoManager,
+          GitReferenceUpdated.DISABLED,
+          allUsers,
+          NAME_ACCOUNTS,
+          () -> Sequences.FIRST_ACCOUNT_ID,
+          accountBatchSize);
+    }
+  }
+
   @VisibleForTesting
   static RetryerBuilder<ImmutableList<Integer>> retryerBuilder() {
     return RetryerBuilder.<ImmutableList<Integer>>newBuilder()
@@ -136,7 +274,7 @@
     this(repoManager, gitRefUpdated, projectName, name, seed, batchSize, afterReadRef, retryer, 0);
   }
 
-  RepoSequence(
+  private RepoSequence(
       GitRepositoryManager repoManager,
       GitReferenceUpdated gitRefUpdated,
       Project.NameKey projectName,
@@ -177,6 +315,7 @@
    *
    * @return the next available sequence number
    */
+  @Override
   public int next() {
     return Iterables.getOnlyElement(next(1));
   }
@@ -189,6 +328,7 @@
    * @param count the number of sequence numbers which should be returned
    * @return the next N available sequence numbers
    */
+  @Override
   public ImmutableList<Integer> next(int count) {
     if (count == 0) {
       return ImmutableList.of();
@@ -271,6 +411,7 @@
     }
   }
 
+  @Override
   public void storeNew(int value) {
     counterLock.lock();
     try (Repository repo = repoManager.openRepository(projectName);
@@ -296,6 +437,12 @@
     }
   }
 
+  @Override
+  public int getBatchSize() {
+    return batchSize;
+  }
+
+  @Override
   public int current() {
     counterLock.lock();
     try (Repository repo = repoManager.openRepository(projectName);
@@ -320,6 +467,7 @@
    *
    * <p>Explicitly calls {@link #next()} if this instance didn't return sequence number until now.
    */
+  @Override
   public int last() {
     if (counter == 0) {
       next();
diff --git a/java/com/google/gerrit/server/notedb/Sequences.java b/java/com/google/gerrit/server/notedb/Sequences.java
index b42253e..780998b 100644
--- a/java/com/google/gerrit/server/notedb/Sequences.java
+++ b/java/com/google/gerrit/server/notedb/Sequences.java
@@ -14,98 +14,43 @@
 
 package com.google.gerrit.server.notedb;
 
+import static com.google.gerrit.server.Sequence.NAME_ACCOUNTS;
+import static com.google.gerrit.server.Sequence.NAME_CHANGES;
+import static com.google.gerrit.server.Sequence.NAME_GROUPS;
+
 import com.google.common.collect.ImmutableList;
 import com.google.gerrit.metrics.Description;
 import com.google.gerrit.metrics.Description.Units;
 import com.google.gerrit.metrics.Field;
 import com.google.gerrit.metrics.MetricMaker;
 import com.google.gerrit.metrics.Timer2;
-import com.google.gerrit.server.config.AllProjectsName;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.Sequence;
+import com.google.gerrit.server.Sequence.SequenceType;
 import com.google.gerrit.server.logging.Metadata;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
-import org.eclipse.jgit.lib.Config;
+import com.google.inject.name.Named;
 
 @Singleton
 public class Sequences {
-  private static final String SECTION_NOTEDB = "noteDb";
-  private static final String KEY_SEQUENCE_BATCH_SIZE = "sequenceBatchSize";
-  private static final int DEFAULT_ACCOUNTS_SEQUENCE_BATCH_SIZE = 1;
-  private static final int DEFAULT_CHANGES_SEQUENCE_BATCH_SIZE = 20;
-
-  public static final String NAME_ACCOUNTS = "accounts";
-  public static final String NAME_GROUPS = "groups";
-  public static final String NAME_CHANGES = "changes";
-
-  public static final int FIRST_ACCOUNT_ID = 1000000;
-  public static final int FIRST_GROUP_ID = 1;
   public static final int FIRST_CHANGE_ID = 1;
+  public static final int FIRST_GROUP_ID = 1;
+  public static final int FIRST_ACCOUNT_ID = 1000000;
 
-  private enum SequenceType {
-    ACCOUNTS,
-    CHANGES,
-    GROUPS;
-  }
-
-  private final RepoSequence accountSeq;
-  private final RepoSequence changeSeq;
-  private final RepoSequence groupSeq;
+  private final Sequence accountSeq;
+  private final Sequence changeSeq;
+  private final Sequence groupSeq;
   private final Timer2<SequenceType, Boolean> nextIdLatency;
-  private final int accountBatchSize;
-  private final int changeBatchSize;
-  private final int groupBatchSize = 1;
 
   @Inject
   public Sequences(
-      @GerritServerConfig Config cfg,
-      GitRepositoryManager repoManager,
-      GitReferenceUpdated gitRefUpdated,
-      AllProjectsName allProjects,
-      AllUsersName allUsers,
-      MetricMaker metrics) {
-
-    accountBatchSize =
-        cfg.getInt(
-            SECTION_NOTEDB,
-            NAME_ACCOUNTS,
-            KEY_SEQUENCE_BATCH_SIZE,
-            DEFAULT_ACCOUNTS_SEQUENCE_BATCH_SIZE);
-    accountSeq =
-        new RepoSequence(
-            repoManager,
-            gitRefUpdated,
-            allUsers,
-            NAME_ACCOUNTS,
-            () -> FIRST_ACCOUNT_ID,
-            accountBatchSize);
-
-    changeBatchSize =
-        cfg.getInt(
-            SECTION_NOTEDB,
-            NAME_CHANGES,
-            KEY_SEQUENCE_BATCH_SIZE,
-            DEFAULT_CHANGES_SEQUENCE_BATCH_SIZE);
-    changeSeq =
-        new RepoSequence(
-            repoManager,
-            gitRefUpdated,
-            allProjects,
-            NAME_CHANGES,
-            () -> FIRST_CHANGE_ID,
-            changeBatchSize);
-
-    groupSeq =
-        new RepoSequence(
-            repoManager,
-            gitRefUpdated,
-            allUsers,
-            NAME_GROUPS,
-            () -> FIRST_GROUP_ID,
-            groupBatchSize);
+      MetricMaker metrics,
+      @Named(NAME_ACCOUNTS) Sequence accountsSeq,
+      @Named(NAME_GROUPS) Sequence groupsSeq,
+      @Named(NAME_CHANGES) Sequence changesSeq) {
+    this.accountSeq = accountsSeq;
+    this.groupSeq = groupsSeq;
+    this.changeSeq = changesSeq;
 
     nextIdLatency =
         metrics.newTimer(
@@ -123,42 +68,42 @@
 
   public int nextAccountId() {
     try (Timer2.Context<SequenceType, Boolean> timer =
-        nextIdLatency.start(SequenceType.ACCOUNTS, false)) {
+        nextIdLatency.start(Sequence.SequenceType.ACCOUNTS, false)) {
       return accountSeq.next();
     }
   }
 
   public int nextChangeId() {
     try (Timer2.Context<SequenceType, Boolean> timer =
-        nextIdLatency.start(SequenceType.CHANGES, false)) {
+        nextIdLatency.start(Sequence.SequenceType.CHANGES, false)) {
       return changeSeq.next();
     }
   }
 
   public ImmutableList<Integer> nextChangeIds(int count) {
     try (Timer2.Context<SequenceType, Boolean> timer =
-        nextIdLatency.start(SequenceType.CHANGES, count > 1)) {
+        nextIdLatency.start(Sequence.SequenceType.CHANGES, count > 1)) {
       return changeSeq.next(count);
     }
   }
 
   public int nextGroupId() {
     try (Timer2.Context<SequenceType, Boolean> timer =
-        nextIdLatency.start(SequenceType.GROUPS, false)) {
+        nextIdLatency.start(Sequence.SequenceType.GROUPS, false)) {
       return groupSeq.next();
     }
   }
 
   public int changeBatchSize() {
-    return changeBatchSize;
+    return changeSeq.getBatchSize();
   }
 
   public int groupBatchSize() {
-    return groupBatchSize;
+    return groupSeq.getBatchSize();
   }
 
   public int accountBatchSize() {
-    return accountBatchSize;
+    return accountSeq.getBatchSize();
   }
 
   public int currentChangeId() {
diff --git a/java/com/google/gerrit/server/schema/AllProjectsCreator.java b/java/com/google/gerrit/server/schema/AllProjectsCreator.java
index 5b6dbf8..123a873 100644
--- a/java/com/google/gerrit/server/schema/AllProjectsCreator.java
+++ b/java/com/google/gerrit/server/schema/AllProjectsCreator.java
@@ -34,12 +34,12 @@
 import com.google.gerrit.entities.PermissionRule.Action;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.server.GerritPersonIdent;
+import com.google.gerrit.server.Sequence;
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.meta.MetaDataUpdate;
 import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gerrit.server.notedb.Sequences;
 import com.google.gerrit.server.project.ProjectConfig;
 import com.google.gerrit.server.update.context.RefUpdateContext;
 import com.google.inject.Inject;
@@ -244,7 +244,7 @@
 
   private void initSequences(Repository git, BatchRefUpdate bru, int firstChangeId)
       throws IOException {
-    if (git.exactRef(REFS_SEQUENCES + Sequences.NAME_CHANGES) == null) {
+    if (git.exactRef(REFS_SEQUENCES + Sequence.NAME_CHANGES) == null) {
       // Can't easily reuse the inserter from MetaDataUpdate, but this shouldn't slow down site
       // initialization unduly.
       try (ObjectInserter ins = git.newObjectInserter()) {
@@ -257,7 +257,7 @@
   private ReceiveCommand createNewChangeSequence(ObjectInserter ins, int val) throws IOException {
     ObjectId newId = ins.insert(OBJ_BLOB, Integer.toString(val).getBytes(UTF_8));
     return new ReceiveCommand(
-        ObjectId.zeroId(), newId, RefNames.REFS_SEQUENCES + Sequences.NAME_CHANGES);
+        ObjectId.zeroId(), newId, RefNames.REFS_SEQUENCES + Sequence.NAME_CHANGES);
   }
 
   private void execute(Repository git, BatchRefUpdate bru) throws IOException {
diff --git a/java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java b/java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java
index 57ec7ef..d60a10a 100644
--- a/java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java
+++ b/java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java
@@ -23,10 +23,10 @@
 import com.google.common.collect.ImmutableSortedSet;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.exceptions.StorageException;
+import com.google.gerrit.server.Sequence;
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.notedb.Sequences;
 import com.google.gerrit.server.update.context.RefUpdateContext;
 import com.google.inject.Inject;
 import java.io.IOException;
@@ -151,7 +151,7 @@
     //    In this case the server literally will not start under 2.16. We assume the user will fix
     //    this and get 2.16 running rather than abandoning 2.16 and jumping to 3.0 at this point.
     try (Repository allUsers = repoManager.openRepository(allUsersName)) {
-      if (allUsers.exactRef(RefNames.REFS_SEQUENCES + Sequences.NAME_GROUPS) == null) {
+      if (allUsers.exactRef(RefNames.REFS_SEQUENCES + Sequence.NAME_GROUPS) == null) {
         throw new StorageException(
             "You appear to be upgrading to 3.x from a version prior to 2.16; you must upgrade to"
                 + " 2.16.x first");
diff --git a/java/com/google/gerrit/server/schema/SchemaCreatorImpl.java b/java/com/google/gerrit/server/schema/SchemaCreatorImpl.java
index 38e45ab..56c6fa8 100644
--- a/java/com/google/gerrit/server/schema/SchemaCreatorImpl.java
+++ b/java/com/google/gerrit/server/schema/SchemaCreatorImpl.java
@@ -14,6 +14,8 @@
 
 package com.google.gerrit.server.schema;
 
+import static com.google.gerrit.server.Sequence.LightweightGroups;
+
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.entities.AccountGroup;
@@ -21,8 +23,8 @@
 import com.google.gerrit.entities.InternalGroup;
 import com.google.gerrit.exceptions.DuplicateKeyException;
 import com.google.gerrit.git.RefUpdateUtil;
-import com.google.gerrit.metrics.MetricMaker;
 import com.google.gerrit.server.GerritPersonIdent;
+import com.google.gerrit.server.Sequence;
 import com.google.gerrit.server.account.GroupUuid;
 import com.google.gerrit.server.account.ServiceUserClassifier;
 import com.google.gerrit.server.config.AllProjectsName;
@@ -37,7 +39,6 @@
 import com.google.gerrit.server.group.db.InternalGroupCreation;
 import com.google.gerrit.server.index.group.GroupIndex;
 import com.google.gerrit.server.index.group.GroupIndexCollection;
-import com.google.gerrit.server.notedb.Sequences;
 import com.google.gerrit.server.update.context.RefUpdateContext;
 import com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType;
 import com.google.inject.Inject;
@@ -45,7 +46,6 @@
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
 import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.Repository;
 
@@ -58,12 +58,10 @@
   private final AllProjectsCreator allProjectsCreator;
   private final AllUsersCreator allUsersCreator;
   private final AllUsersName allUsersName;
+  private final Sequence groupsSequence;
   private final PersonIdent serverUser;
   private final GroupIndexCollection indexCollection;
   private final String serverId;
-
-  private final Config config;
-  private final MetricMaker metricMaker;
   private final AllProjectsName allProjectsName;
 
   @Inject
@@ -72,23 +70,21 @@
       AllProjectsCreator ap,
       AllUsersCreator auc,
       AllUsersName allUsersName,
+      @LightweightGroups Sequence groupsSequence,
       @GerritPersonIdent PersonIdent au,
       GroupIndexCollection ic,
       String serverId,
-      Config config,
-      MetricMaker metricMaker,
       AllProjectsName apName) {
     this.repoManager = repoManager;
     allProjectsCreator = ap;
     allUsersCreator = auc;
     this.allUsersName = allUsersName;
+    this.groupsSequence = groupsSequence;
     serverUser = au;
     indexCollection = ic;
     this.serverId = serverId;
 
-    this.config = config;
     this.allProjectsName = apName;
-    this.metricMaker = metricMaker;
   }
 
   @Override
@@ -106,19 +102,9 @@
       // We have to create the All-Users repository before we can use it to store the groups in it.
       allUsersCreator.setAdministrators(admins).create();
 
-      // Don't rely on injection to construct Sequences, as the default GitReferenceUpdated has a
-      // thick dependency stack which may not all be available at schema creation time.
-      Sequences seqs =
-          new Sequences(
-              config,
-              repoManager,
-              GitReferenceUpdated.DISABLED,
-              allProjectsName,
-              allUsersName,
-              metricMaker);
       try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
-        createAdminsGroup(seqs, allUsersRepo, admins);
-        createBatchUsersGroup(seqs, allUsersRepo, serviceUsers, admins.getUUID());
+        createAdminsGroup(allUsersRepo, admins);
+        createBatchUsersGroup(allUsersRepo, serviceUsers, admins.getUUID());
       }
     }
   }
@@ -132,10 +118,9 @@
     }
   }
 
-  private void createAdminsGroup(
-      Sequences seqs, Repository allUsersRepo, GroupReference groupReference)
+  private void createAdminsGroup(Repository allUsersRepo, GroupReference groupReference)
       throws IOException, ConfigInvalidException {
-    InternalGroupCreation groupCreation = getGroupCreation(seqs, groupReference);
+    InternalGroupCreation groupCreation = getGroupCreation(groupReference);
     GroupDelta groupDelta =
         GroupDelta.builder().setDescription("Gerrit Site Administrators").build();
 
@@ -143,12 +128,9 @@
   }
 
   private void createBatchUsersGroup(
-      Sequences seqs,
-      Repository allUsersRepo,
-      GroupReference groupReference,
-      AccountGroup.UUID adminsGroupUuid)
+      Repository allUsersRepo, GroupReference groupReference, AccountGroup.UUID adminsGroupUuid)
       throws IOException, ConfigInvalidException {
-    InternalGroupCreation groupCreation = getGroupCreation(seqs, groupReference);
+    InternalGroupCreation groupCreation = getGroupCreation(groupReference);
     GroupDelta groupDelta =
         GroupDelta.builder()
             .setDescription("Users who perform batch actions on Gerrit")
@@ -223,8 +205,8 @@
     return GroupReference.create(groupUuid, name);
   }
 
-  private InternalGroupCreation getGroupCreation(Sequences seqs, GroupReference groupReference) {
-    int next = seqs.nextGroupId();
+  private InternalGroupCreation getGroupCreation(GroupReference groupReference) {
+    int next = groupsSequence.next();
     return InternalGroupCreation.builder()
         .setNameKey(AccountGroup.nameKey(groupReference.getName()))
         .setId(AccountGroup.id(next))
diff --git a/java/com/google/gerrit/server/schema/SchemaModule.java b/java/com/google/gerrit/server/schema/SchemaModule.java
index 9593522..7afc1f5 100644
--- a/java/com/google/gerrit/server/schema/SchemaModule.java
+++ b/java/com/google/gerrit/server/schema/SchemaModule.java
@@ -14,12 +14,14 @@
 
 package com.google.gerrit.server.schema;
 
+import static com.google.gerrit.server.Sequence.LightweightGroups;
 import static com.google.inject.Scopes.SINGLETON;
 
 import com.google.common.collect.ImmutableList;
 import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.server.GerritPersonIdent;
 import com.google.gerrit.server.GerritPersonIdentProvider;
+import com.google.gerrit.server.Sequence;
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.config.AllProjectsNameProvider;
 import com.google.gerrit.server.config.AllUsersName;
@@ -31,6 +33,7 @@
 import com.google.gerrit.server.config.GerritServerId;
 import com.google.gerrit.server.config.GerritServerIdProvider;
 import com.google.gerrit.server.index.group.GroupIndexCollection;
+import com.google.gerrit.server.notedb.RepoSequence.DisabledGitRefUpdatedRepoGroupsSequenceProvider;
 import com.google.inject.TypeLiteral;
 import org.eclipse.jgit.lib.PersonIdent;
 
@@ -64,6 +67,9 @@
     // SchemaCreatorImpl, so it's needed.
     // TODO(dborowitz): Is there any way to untangle this?
     bind(GroupIndexCollection.class);
+    bind(Sequence.class)
+        .annotatedWith(LightweightGroups.class)
+        .toProvider(DisabledGitRefUpdatedRepoGroupsSequenceProvider.class);
     bind(SchemaCreator.class).to(SchemaCreatorImpl.class);
   }
 }
diff --git a/java/com/google/gerrit/testing/InMemoryModule.java b/java/com/google/gerrit/testing/InMemoryModule.java
index fa879d6..0f57fac 100644
--- a/java/com/google/gerrit/testing/InMemoryModule.java
+++ b/java/com/google/gerrit/testing/InMemoryModule.java
@@ -16,6 +16,7 @@
 
 import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.util.concurrent.MoreExecutors.newDirectExecutorService;
+import static com.google.gerrit.server.Sequence.LightweightGroups;
 import static com.google.inject.Scopes.SINGLETON;
 
 import com.google.common.base.Strings;
@@ -44,6 +45,7 @@
 import com.google.gerrit.server.GerritPersonIdentProvider;
 import com.google.gerrit.server.LibModuleType;
 import com.google.gerrit.server.PluginUser;
+import com.google.gerrit.server.Sequence;
 import com.google.gerrit.server.account.AccountCacheImpl;
 import com.google.gerrit.server.account.GroupBackend;
 import com.google.gerrit.server.account.storage.notedb.AccountNoteDbStorageModule;
@@ -99,6 +101,7 @@
 import com.google.gerrit.server.index.group.GroupSchemaDefinitions;
 import com.google.gerrit.server.mail.EmailModule;
 import com.google.gerrit.server.mail.SignedTokenEmailTokenVerifier.SignedTokenEmailTokenVerifierModule;
+import com.google.gerrit.server.notedb.RepoSequence.DisabledGitRefUpdatedRepoGroupsSequenceProvider;
 import com.google.gerrit.server.patch.DiffExecutor;
 import com.google.gerrit.server.permissions.DefaultPermissionBackendModule;
 import com.google.gerrit.server.plugins.ServerInformationImpl;
@@ -312,6 +315,9 @@
           .toProvider(AnonymousCowardNameProvider.class);
 
       bind(GroupIndexCollection.class);
+      bind(Sequence.class)
+          .annotatedWith(LightweightGroups.class)
+          .toProvider(DisabledGitRefUpdatedRepoGroupsSequenceProvider.class);
       bind(SchemaCreator.class).to(SchemaCreatorImpl.class);
     }
 
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index 6e4594c..498daae 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -132,6 +132,7 @@
 import com.google.gerrit.gpg.testing.TestKey;
 import com.google.gerrit.httpd.CacheBasedWebSession;
 import com.google.gerrit.server.ExceptionHook;
+import com.google.gerrit.server.Sequence;
 import com.google.gerrit.server.ServerInitiated;
 import com.google.gerrit.server.account.AccountControl;
 import com.google.gerrit.server.account.AccountProperties;
@@ -331,7 +332,7 @@
       refUpdateCounter.assertRefUpdateFor(
           RefUpdateCounter.projectRef(allUsers, RefNames.refsUsers(accountId)),
           RefUpdateCounter.projectRef(allUsers, RefNames.REFS_EXTERNAL_IDS),
-          RefUpdateCounter.projectRef(allUsers, RefNames.REFS_SEQUENCES + Sequences.NAME_ACCOUNTS));
+          RefUpdateCounter.projectRef(allUsers, RefNames.REFS_SEQUENCES + Sequence.NAME_ACCOUNTS));
     }
   }
 
diff --git a/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java b/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
index c0531e5..3bec694 100644
--- a/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
@@ -49,11 +49,11 @@
 import com.google.gerrit.extensions.api.groups.GroupInput;
 import com.google.gerrit.extensions.api.projects.BranchInput;
 import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.server.Sequence;
 import com.google.gerrit.server.account.ServiceUserClassifier;
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.git.receive.ReceiveCommitsAdvertiseRefsHookChain;
 import com.google.gerrit.server.git.receive.testing.TestRefAdvertiser;
-import com.google.gerrit.server.notedb.Sequences;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackend.RefFilterOptions;
 import com.google.gerrit.server.query.change.ChangeData;
@@ -1413,8 +1413,8 @@
             RefNames.REFS_GROUPNAMES,
             RefNames.refsGroups(admins),
             RefNames.refsGroups(nonInteractiveUsers),
-            RefNames.REFS_SEQUENCES + Sequences.NAME_ACCOUNTS,
-            RefNames.REFS_SEQUENCES + Sequences.NAME_GROUPS,
+            RefNames.REFS_SEQUENCES + Sequence.NAME_ACCOUNTS,
+            RefNames.REFS_SEQUENCES + Sequence.NAME_GROUPS,
             RefNames.REFS_CONFIG,
             Constants.HEAD);
 
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/GetBranchIT.java b/javatests/com/google/gerrit/acceptance/rest/project/GetBranchIT.java
index 13c20dd..cf3bf89 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/GetBranchIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/GetBranchIT.java
@@ -43,8 +43,8 @@
 import com.google.gerrit.extensions.api.projects.TagInput;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.server.Sequence;
 import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gerrit.server.notedb.Sequences;
 import com.google.gerrit.testing.ConfigSuite;
 import com.google.inject.Inject;
 import org.eclipse.jgit.junit.TestRepository;
@@ -458,7 +458,7 @@
   public void getAccountSequenceRef() throws Exception {
     // a user without the 'Access Database' capability cannot see the refs/sequences/accounts ref
     requestScopeOperations.setApiUser(user.id());
-    String accountSequenceRef = RefNames.REFS_SEQUENCES + Sequences.NAME_ACCOUNTS;
+    String accountSequenceRef = RefNames.REFS_SEQUENCES + Sequence.NAME_ACCOUNTS;
     assertBranchNotFound(allUsers, accountSequenceRef);
 
     // a user with the 'Access Database' capability can see the refs/sequences/accounts ref
@@ -469,7 +469,7 @@
   public void getChangeSequenceRef() throws Exception {
     // a user without the 'Access Database' capability cannot see the refs/sequences/changes ref
     requestScopeOperations.setApiUser(user.id());
-    String changeSequenceRef = RefNames.REFS_SEQUENCES + Sequences.NAME_CHANGES;
+    String changeSequenceRef = RefNames.REFS_SEQUENCES + Sequence.NAME_CHANGES;
     assertBranchNotFound(allProjects, changeSequenceRef);
 
     // a user with the 'Access Database' capability can see the refs/sequences/changes ref
@@ -480,7 +480,7 @@
   public void getGroupSequenceRef() throws Exception {
     // a user without the 'Access Database' capability cannot see the refs/sequences/groups ref
     requestScopeOperations.setApiUser(user.id());
-    String groupSequenceRef = RefNames.REFS_SEQUENCES + Sequences.NAME_GROUPS;
+    String groupSequenceRef = RefNames.REFS_SEQUENCES + Sequence.NAME_GROUPS;
     assertBranchNotFound(allUsers, groupSequenceRef);
 
     // a user with the 'Access Database' capability can see the refs/sequences/groups ref
diff --git a/javatests/com/google/gerrit/server/schema/NoteDbSchemaUpdaterTest.java b/javatests/com/google/gerrit/server/schema/NoteDbSchemaUpdaterTest.java
index 767ac28..5d5ef4e 100644
--- a/javatests/com/google/gerrit/server/schema/NoteDbSchemaUpdaterTest.java
+++ b/javatests/com/google/gerrit/server/schema/NoteDbSchemaUpdaterTest.java
@@ -24,13 +24,13 @@
 import com.google.common.collect.ImmutableSortedSet;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.exceptions.StorageException;
+import com.google.gerrit.server.Sequence;
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.notedb.IntBlob;
 import com.google.gerrit.server.notedb.RepoSequence;
-import com.google.gerrit.server.notedb.Sequences;
 import com.google.gerrit.testing.InMemoryRepositoryManager;
 import com.google.gerrit.testing.TestUpdateUI;
 import java.io.IOException;
@@ -150,7 +150,7 @@
               repoManager,
               GitReferenceUpdated.DISABLED,
               allUsersName,
-              Sequences.NAME_GROUPS,
+              Sequence.NAME_GROUPS,
               () -> 1,
               1)
           .next();