Define a floor for NoteDb sequence generation

Make sure that new entities created with NoteDb sequences
are never below the floor value defined in ReviewDb when performing
a NoteDb migration.

Prevents any possible conflicts with existing change sequences
on ReviewDb once NoteDb is set as main active review store.

Bug: Issue 8861
Change-Id: Ica4529771fae6487b01708fb22193cf16ce73f82
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java
index b8fb4c3..388c240 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java
@@ -335,7 +335,7 @@
 
   @Test
   public void enableSequencesNoGap() throws Exception {
-    testEnableSequences(0, 2, "12");
+    testEnableSequences(0, 3, "13");
   }
 
   @Test
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RepoSequence.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RepoSequence.java
index 777624a..11266f9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RepoSequence.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RepoSequence.java
@@ -91,6 +91,7 @@
   private final Project.NameKey projectName;
   private final String refName;
   private final Seed seed;
+  private final int floor;
   private final int batchSize;
   private final Runnable afterReadRef;
   private final Retryer<RefUpdate.Result> retryer;
@@ -118,7 +119,28 @@
         seed,
         batchSize,
         Runnables.doNothing(),
-        RETRYER);
+        RETRYER,
+        0);
+  }
+
+  public RepoSequence(
+      GitRepositoryManager repoManager,
+      GitReferenceUpdated gitRefUpdated,
+      Project.NameKey projectName,
+      String name,
+      Seed seed,
+      int batchSize,
+      int floor) {
+    this(
+        repoManager,
+        gitRefUpdated,
+        projectName,
+        name,
+        seed,
+        batchSize,
+        Runnables.doNothing(),
+        RETRYER,
+        floor);
   }
 
   @VisibleForTesting
@@ -131,6 +153,19 @@
       int batchSize,
       Runnable afterReadRef,
       Retryer<RefUpdate.Result> retryer) {
+    this(repoManager, gitRefUpdated, projectName, name, seed, batchSize, afterReadRef, retryer, 0);
+  }
+
+  RepoSequence(
+      GitRepositoryManager repoManager,
+      GitReferenceUpdated gitRefUpdated,
+      Project.NameKey projectName,
+      String name,
+      Seed seed,
+      int batchSize,
+      Runnable afterReadRef,
+      Retryer<RefUpdate.Result> retryer,
+      int floor) {
     this.repoManager = checkNotNull(repoManager, "repoManager");
     this.gitRefUpdated = checkNotNull(gitRefUpdated, "gitRefUpdated");
     this.projectName = checkNotNull(projectName, "projectName");
@@ -144,6 +179,7 @@
     this.refName = RefNames.REFS_SEQUENCES + name;
 
     this.seed = checkNotNull(seed, "seed");
+    this.floor = floor;
 
     checkArgument(batchSize > 0, "expected batchSize > 0, got: %s", batchSize);
     this.batchSize = batchSize;
@@ -251,15 +287,17 @@
     @Override
     public RefUpdate.Result call() throws Exception {
       Ref ref = repo.exactRef(refName);
+      int nextCandidate;
       afterReadRef.run();
       ObjectId oldId;
       if (ref == null) {
         oldId = ObjectId.zeroId();
-        next = seed.get();
+        nextCandidate = seed.get();
       } else {
         oldId = ref.getObjectId();
-        next = parse(oldId);
+        nextCandidate = parse(oldId);
       }
+      next = Math.max(floor, nextCandidate);
       return store(repo, rw, oldId, next + count);
     }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/NoteDbMigrator.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/NoteDbMigrator.java
index df415ce..6db9e76 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/NoteDbMigrator.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/NoteDbMigrator.java
@@ -560,6 +560,8 @@
       throws OrmException, IOException {
     try (ReviewDb db = schemaFactory.open()) {
       @SuppressWarnings("deprecation")
+      final int nextChangeId = db.nextChangeId();
+
       RepoSequence seq =
           new RepoSequence(
               repoManager,
@@ -569,8 +571,9 @@
               // If sequenceGap is 0, this writes into the sequence ref the same ID that is returned
               // by the call to seq.next() below. If we actually used this as a change ID, that
               // would be a problem, but we just discard it, so this is safe.
-              () -> db.nextChangeId() + sequenceGap - 1,
-              1);
+              () -> nextChangeId + sequenceGap - 1,
+              1,
+              nextChangeId);
       seq.next();
     }
     return saveState(prev, READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY);