Add support for upgrading database directly from DSpace 5.x to 7.x

This commit is contained in:
Tim Donohue
2020-09-15 14:16:10 -05:00
parent ae7ef8577b
commit d137ce179f
4 changed files with 189 additions and 1 deletions

View File

@@ -562,6 +562,12 @@ public class DatabaseUtils {
log.info("Loading Flyway DB migrations from: " + StringUtils.join(scriptLocations, ", "));
flywayConfiguration.locations(scriptLocations.toArray(new String[scriptLocations.size()]));
// Tell Flyway NOT to throw a validation error if it finds older "Ignored" migrations.
// For DSpace, we sometimes have to insert "old" migrations in after a major release
// if further development/bug fixes are needed in older versions. So, "Ignored" migrations are
// nothing to worry about...you can always trigger them to run using "database migrate ignored" from CLI
flywayConfiguration.ignoreIgnoredMigrations(true);
// Set flyway callbacks (i.e. classes which are called post-DB migration and similar)
// In this situation, we have a Registry Updater that runs PRE-migration
// NOTE: DatabaseLegacyReindexer only indexes in Legacy Lucene & RDBMS indexes. It can be removed
@@ -691,8 +697,13 @@ public class DatabaseUtils {
flyway = flywayConfiguration.load();
flyway.baseline();
} else {
// Otherwise, database already initialized with Flyway. Just load our configuration.
// Otherwise, this database already ran Flyway before
// So, just load our Flyway configuration, initializing latest Flyway.
flyway = flywayConfiguration.load();
// Now, check our Flyway database table to see if it needs upgrading
// *before* any other Flyway commands can be run.
FlywayUpgradeUtils.upgradeFlywayTable(flyway, connection);
}
// Determine pending Database migrations

View File

@@ -0,0 +1,119 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.storage.rdbms;
import static org.dspace.storage.rdbms.DatabaseUtils.FLYWAY_TABLE;
import static org.dspace.storage.rdbms.DatabaseUtils.executeSql;
import static org.dspace.storage.rdbms.DatabaseUtils.getCurrentFlywayState;
import static org.dspace.storage.rdbms.DatabaseUtils.getDbType;
import static org.dspace.storage.rdbms.DatabaseUtils.getSchemaName;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.text.StringSubstitutor;
import org.apache.logging.log4j.Logger;
import org.dspace.storage.rdbms.migration.MigrationUtils;
import org.flywaydb.core.Flyway;
import org.springframework.core.io.ClassPathResource;
/**
* Utility class used to detect issues with the Flyway migration history table and attempt to correct/fix them.
* These issues can occur when attempting to upgrade your database across multiple versions/releases of Flyway.
* <p>
* As documented in this issue ticket, Flyway does not normally support skipping over any
* major release (for example going from v3 to v5 is unsuppored): https://github.com/flyway/flyway/issues/2126
* <p>
* This class allows us to do a migration (where needed) through multiple major versions of Flyway.
*
* @author Tim Donohue
*/
public class FlywayUpgradeUtils {
/**
* log4j category
*/
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(FlywayUpgradeUtils.class);
// Resource path of all Flyway upgrade scripts
private static final String UPGRADE_SCRIPT_PATH = "org/dspace/storage/rdbms/flywayupgrade/";
/**
* Default constructor
*/
private FlywayUpgradeUtils() { }
/**
* Ensures the Flyway migration history table (FLYWAY_TABLE) is upgraded to the latest version of Flyway safely.
* <P>
* Unfortunately, Flyway does not always support skipping major versions (e.g. upgrading directly from Flyway
* v3.x to 5.x is not possible, see https://github.com/flyway/flyway/issues/2126).
* <P>
* While sometimes it's possible to do so, other times you MUST upgrade through each major version. This method
* ensures we upgrade the Flyway history table through each version of Flyway where deemed necessary.
*
* @param flyway initialized/configured Flyway object
* @param connection current database connection
*/
protected static synchronized void upgradeFlywayTable(Flyway flyway, Connection connection)
throws SQLException {
// Whether the Flyway table needs updating or not
boolean needsUpgrade = false;
// Determine if Flyway needs updating by running a simple info() command.
// This command will not run any pending migrations, but it will throw an exception
// if the Flyway migration history table is NOT valid for the current version of Flyway
try {
flyway.info();
} catch (Exception e) {
// ignore error, but log info statement to say we will try to upgrade to fix problem
log.info("Flyway table '{}' appears to be outdated. Will attempt to upgrade it automatically. " +
"Flyway Exception was '{}'", FLYWAY_TABLE, e.toString());
needsUpgrade = true;
}
if (needsUpgrade) {
// Get the DSpace version info from the LAST migration run.
String lastMigration = getCurrentFlywayState(connection);
// If this is an older DSpace 5.x compatible database, then it used Flyway 3.x.
// Because we cannot upgrade directly from Flyway 3.x -> 6.x, we need to FIRST update this
// database to be compatible with Flyway 4.2.0 (which can be upgraded directly to Flyway 6.x)
if (lastMigration.startsWith("5.")) {
// Based on type of DB, get path to our Flyway 4.x upgrade script
String dbtype = getDbType(connection);
String scriptPath = UPGRADE_SCRIPT_PATH + dbtype + "/upgradeToFlyway4x.sql";
log.info("Attempting to upgrade Flyway table '{}' using script at '{}'",
FLYWAY_TABLE, scriptPath);
// Load the Flyway v4.2.0 upgrade SQL script as a String
String flywayUpgradeSQL = MigrationUtils.resourceToString(
new ClassPathResource(scriptPath, FlywayUpgradeUtils.class.getClassLoader()));
// As this Flyway upgrade SQL was borrowed from Flyway v4.2.0 directly, it contains some inline
// variables which need replacing, namely ${schema} and ${table} variables.
// We'll use the StringSubstitutor to replace those variables with their proper values.
Map<String, String> valuesMap = new HashMap<>();
valuesMap.put("schema", getSchemaName(connection));
valuesMap.put("table", FLYWAY_TABLE);
StringSubstitutor sub = new StringSubstitutor(valuesMap);
flywayUpgradeSQL = sub.replace(flywayUpgradeSQL);
// Run the script to update the Flyway table to be compatible with FLyway v4.x
executeSql(connection, flywayUpgradeSQL);
}
// NOTE: no other DSpace versions require a specialized Flyway upgrade script at this time.
// DSpace 4 didn't use Flyway. DSpace 6 used Flyway v4, which Flyway v6 can update automatically.
// After any Flyway table upgrade, we MUST run a Flyway repair() to cleanup migration checksums if needed
log.info("Repairing Flyway table '{}' after upgrade...", FLYWAY_TABLE);
flyway.repair();
}
}
}

View File

@@ -0,0 +1,29 @@
--
-- Copyright 2010-2017 Boxfuse GmbH
--
-- 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.
--
-----------------
-- This is the Oracle upgrade script from Flyway v4.2.0, copied/borrowed from:
-- https://github.com/flyway/flyway/blob/flyway-4.2.0/flyway-core/src/main/resources/org/flywaydb/core/internal/dbsupport/oracle/upgradeMetaDataTable.sql
--
-- The variables in this script are replaced in FlywayUpgradeUtils.upgradeFlywayTable()
------------------
DROP INDEX "${schema}"."${table}_vr_idx";
DROP INDEX "${schema}"."${table}_ir_idx";
ALTER TABLE "${schema}"."${table}" DROP COLUMN "version_rank";
ALTER TABLE "${schema}"."${table}" DROP PRIMARY KEY DROP INDEX;
ALTER TABLE "${schema}"."${table}" MODIFY "version" NULL;
ALTER TABLE "${schema}"."${table}" ADD CONSTRAINT "${table}_pk" PRIMARY KEY ("installed_rank");
UPDATE "${schema}"."${table}" SET "type"='BASELINE' WHERE "type"='INIT';

View File

@@ -0,0 +1,29 @@
--
-- Copyright 2010-2017 Boxfuse GmbH
--
-- 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.
--
-----------------
-- This is the PostgreSQL upgrade script from Flyway v4.2.0, copied/borrowed from:
-- https://github.com/flyway/flyway/blob/flyway-4.2.0/flyway-core/src/main/resources/org/flywaydb/core/internal/dbsupport/oracle/upgradeMetaDataTable.sql
--
-- The variables in this script are replaced in FlywayUpgradeUtils.upgradeFlywayTable()
------------------
DROP INDEX "${schema}"."${table}_vr_idx";
DROP INDEX "${schema}"."${table}_ir_idx";
ALTER TABLE "${schema}"."${table}" DROP COLUMN "version_rank";
ALTER TABLE "${schema}"."${table}" DROP CONSTRAINT "${table}_pk";
ALTER TABLE "${schema}"."${table}" ALTER COLUMN "version" DROP NOT NULL;
ALTER TABLE "${schema}"."${table}" ADD CONSTRAINT "${table}_pk" PRIMARY KEY ("installed_rank");
UPDATE "${schema}"."${table}" SET "type"='BASELINE' WHERE "type"='INIT';