LCOV - code coverage report
Current view: top level - server/schema - NoteDbSchemaUpdater.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 56 63 88.9 %
Date: 2022-11-19 15:00:39 Functions: 7 7 100.0 %

          Line data    Source code
       1             : // Copyright (C) 2018 The Android Open Source Project
       2             : //
       3             : // Licensed under the Apache License, Version 2.0 (the "License");
       4             : // you may not use this file except in compliance with the License.
       5             : // You may obtain a copy of the License at
       6             : //
       7             : // http://www.apache.org/licenses/LICENSE-2.0
       8             : //
       9             : // Unless required by applicable law or agreed to in writing, software
      10             : // distributed under the License is distributed on an "AS IS" BASIS,
      11             : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             : // See the License for the specific language governing permissions and
      13             : // limitations under the License.
      14             : 
      15             : package com.google.gerrit.server.schema;
      16             : 
      17             : import static com.google.common.collect.ImmutableList.toImmutableList;
      18             : 
      19             : import com.google.common.annotations.VisibleForTesting;
      20             : import com.google.common.collect.ImmutableList;
      21             : import com.google.common.collect.ImmutableSortedMap;
      22             : import com.google.common.collect.ImmutableSortedSet;
      23             : import com.google.gerrit.entities.RefNames;
      24             : import com.google.gerrit.exceptions.StorageException;
      25             : import com.google.gerrit.server.config.AllUsersName;
      26             : import com.google.gerrit.server.config.GerritServerConfig;
      27             : import com.google.gerrit.server.git.GitRepositoryManager;
      28             : import com.google.gerrit.server.notedb.Sequences;
      29             : import com.google.inject.Inject;
      30             : import java.io.IOException;
      31             : import java.util.stream.IntStream;
      32             : import org.eclipse.jgit.errors.ConfigInvalidException;
      33             : import org.eclipse.jgit.lib.Config;
      34             : import org.eclipse.jgit.lib.Repository;
      35             : 
      36             : public class NoteDbSchemaUpdater {
      37             :   private final Config cfg;
      38             :   private final AllUsersName allUsersName;
      39             :   private final GitRepositoryManager repoManager;
      40             :   private final SchemaCreator schemaCreator;
      41             :   private final NoteDbSchemaVersionManager versionManager;
      42             :   private final NoteDbSchemaVersion.Arguments args;
      43             :   private final ImmutableSortedMap<Integer, Class<? extends NoteDbSchemaVersion>> schemaVersions;
      44             : 
      45             :   @Inject
      46             :   NoteDbSchemaUpdater(
      47             :       @GerritServerConfig Config cfg,
      48             :       AllUsersName allUsersName,
      49             :       GitRepositoryManager repoManager,
      50             :       SchemaCreator schemaCreator,
      51             :       NoteDbSchemaVersionManager versionManager,
      52             :       NoteDbSchemaVersion.Arguments args) {
      53          15 :     this(
      54             :         cfg,
      55             :         allUsersName,
      56             :         repoManager,
      57             :         schemaCreator,
      58             :         versionManager,
      59             :         args,
      60             :         NoteDbSchemaVersions.ALL);
      61          15 :   }
      62             : 
      63             :   NoteDbSchemaUpdater(
      64             :       Config cfg,
      65             :       AllUsersName allUsersName,
      66             :       GitRepositoryManager repoManager,
      67             :       SchemaCreator schemaCreator,
      68             :       NoteDbSchemaVersionManager versionManager,
      69             :       NoteDbSchemaVersion.Arguments args,
      70          16 :       ImmutableSortedMap<Integer, Class<? extends NoteDbSchemaVersion>> schemaVersions) {
      71          16 :     this.cfg = cfg;
      72          16 :     this.allUsersName = allUsersName;
      73          16 :     this.repoManager = repoManager;
      74          16 :     this.schemaCreator = schemaCreator;
      75          16 :     this.versionManager = versionManager;
      76          16 :     this.args = args;
      77          16 :     this.schemaVersions = schemaVersions;
      78          16 :   }
      79             : 
      80             :   public void update(UpdateUI ui) {
      81          16 :     ensureSchemaCreated();
      82             : 
      83          16 :     int currentVersion = versionManager.read();
      84          16 :     if (currentVersion == 0) {
      85             :       // The only valid case where there is no refs/meta/version is when running 3.x init for the
      86             :       // first time on a site that previously ran init on 2.16. A freshly created 3.x site will have
      87             :       // seeded refs/meta/version during AllProjectsCreator, so it won't hit this block.
      88           1 :       checkNoteDbConfigFor216();
      89             :     }
      90             : 
      91          16 :     for (int nextVersion : requiredUpgrades(currentVersion, schemaVersions.keySet())) {
      92             :       try {
      93           1 :         ui.message(String.format("Migrating data to schema %d ...", nextVersion));
      94           1 :         NoteDbSchemaVersions.get(schemaVersions, nextVersion).upgrade(args, ui);
      95           1 :         versionManager.increment(nextVersion - 1);
      96           0 :       } catch (Exception e) {
      97           0 :         throw new StorageException(
      98           0 :             String.format("Failed to upgrade to schema version %d", nextVersion), e);
      99           1 :       }
     100           1 :     }
     101          16 :   }
     102             : 
     103             :   private void ensureSchemaCreated() {
     104             :     try {
     105          16 :       schemaCreator.ensureCreated();
     106           0 :     } catch (IOException | ConfigInvalidException e) {
     107           0 :       throw new StorageException("Cannot initialize Gerrit site", e);
     108          16 :     }
     109          16 :   }
     110             : 
     111             :   // Config#getEnum requires this to be public, so give it an off-putting name.
     112           1 :   public enum PrimaryStorageFor216Compatibility {
     113           1 :     REVIEW_DB,
     114           1 :     NOTE_DB
     115             :   }
     116             : 
     117             :   private void checkNoteDbConfigFor216() {
     118             :     // Check that the NoteDb migration config matches what we expect from a site that both:
     119             :     // * Completed the change migration to NoteDB.
     120             :     // * Ran schema upgrades from a 2.16 final release.
     121             : 
     122           1 :     if (!cfg.getBoolean("noteDb", "changes", "write", false)
     123           1 :         || !cfg.getBoolean("noteDb", "changes", "read", false)
     124           1 :         || cfg.getEnum(
     125             :                 "noteDb", "changes", "primaryStorage", PrimaryStorageFor216Compatibility.REVIEW_DB)
     126             :             != PrimaryStorageFor216Compatibility.NOTE_DB
     127           1 :         || !cfg.getBoolean("noteDb", "changes", "disableReviewDb", false)) {
     128           1 :       throw new StorageException(
     129             :           "You appear to be upgrading from a 2.x site, but the NoteDb change migration was"
     130             :               + " not completed. See documentation:\n"
     131             :               + "https://gerrit-review.googlesource.com/Documentation/note-db.html#migration");
     132             :     }
     133             : 
     134             :     // We don't have a direct way to check that 2.16 init was run; the most obvious side effect
     135             :     // would be upgrading the *ReviewDb* schema to the latest 2.16 schema version. But in 3.x we can
     136             :     // no longer access ReviewDb, so we can't check that directly.
     137             :     //
     138             :     // Instead, check for a NoteDb-specific side effect of the migration process: the presence of
     139             :     // the NoteDb group sequence ref. This is created by the schema 163 migration, which was part of
     140             :     // 2.16 and not 2.15.
     141             :     //
     142             :     // There are a few corner cases where we will proceed even if the schema is not fully up to
     143             :     // date:
     144             :     //  * If a user happened to run init from master after schema 163 was added but before 2.16
     145             :     //    final. We assume that someone savvy enough to do that has followed the documented
     146             :     //    requirement of upgrading to 2.16 final before 3.0.
     147             :     //  * If a user ran init in 2.16.x and the upgrade to 163 succeeded but a later update failed.
     148             :     //    In this case the server literally will not start under 2.16. We assume the user will fix
     149             :     //    this and get 2.16 running rather than abandoning 2.16 and jumping to 3.0 at this point.
     150           1 :     try (Repository allUsers = repoManager.openRepository(allUsersName)) {
     151           1 :       if (allUsers.exactRef(RefNames.REFS_SEQUENCES + Sequences.NAME_GROUPS) == null) {
     152           1 :         throw new StorageException(
     153             :             "You appear to be upgrading to 3.x from a version prior to 2.16; you must upgrade to"
     154             :                 + " 2.16.x first");
     155             :       }
     156           0 :     } catch (IOException e) {
     157           0 :       throw new StorageException("Failed to check NoteDb migration state", e);
     158           1 :     }
     159           1 :   }
     160             : 
     161             :   @VisibleForTesting
     162             :   static ImmutableList<Integer> requiredUpgrades(
     163             :       int currentVersion, ImmutableSortedSet<Integer> allVersions) {
     164          16 :     int firstVersion = allVersions.first();
     165          16 :     int latestVersion = allVersions.last();
     166          16 :     if (currentVersion == latestVersion) {
     167          16 :       return ImmutableList.of();
     168           1 :     } else if (currentVersion > latestVersion) {
     169           1 :       throw new StorageException(
     170           1 :           String.format(
     171             :               "Cannot downgrade NoteDb schema from version %d to %d",
     172           1 :               currentVersion, latestVersion));
     173             :     }
     174             : 
     175             :     int firstUpgradeVersion;
     176           1 :     if (currentVersion == 0) {
     177             :       // Bootstrap NoteDb version to minimum supported schema number.
     178           1 :       firstUpgradeVersion = firstVersion;
     179             :     } else {
     180           1 :       if (currentVersion < firstVersion - 1) {
     181           1 :         throw new StorageException(
     182           1 :             String.format(
     183           1 :                 "Cannot skip NoteDb schema from version %d to %d", currentVersion, firstVersion));
     184             :       }
     185           1 :       firstUpgradeVersion = currentVersion + 1;
     186             :     }
     187           1 :     return IntStream.rangeClosed(firstUpgradeVersion, latestVersion)
     188           1 :         .boxed()
     189           1 :         .collect(toImmutableList());
     190             :   }
     191             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750