ENT-8983 Upgrade H2 and liquibase to latest version (#7298)

This commit is contained in:
Mahmoud Almahroum 2023-03-03 15:10:25 +00:00 committed by GitHub
parent c777e77962
commit 1a0d354903
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 76 additions and 39 deletions

View File

@ -77,9 +77,9 @@ mockitoKotlinVersion=1.6.0
hamkrestVersion=1.7.0.0
joptSimpleVersion=5.0.2
jansiVersion=1.18
hibernateVersion=5.4.32.Final
hibernateVersion=5.6.5.Final
# h2Version - Update docs if renamed or removed.
h2Version=1.4.199
h2Version=2.1.212
rxjavaVersion=1.3.8
dokkaVersion=0.10.1
eddsaVersion=0.3.0
@ -88,7 +88,7 @@ commonsCollectionsVersion=4.3
beanutilsVersion=1.9.4
shiroVersion=1.10.0
hikariVersion=3.3.1
liquibaseVersion=3.6.3
liquibaseVersion=4.18.0
dockerComposeRuleVersion=1.5.0
seleniumVersion=3.141.59
ghostdriverVersion=2.1.0

Binary file not shown.

View File

@ -4,9 +4,13 @@ import com.fasterxml.jackson.databind.ObjectMapper
import liquibase.Contexts
import liquibase.LabelExpression
import liquibase.Liquibase
import liquibase.Scope
import liquibase.ThreadLocalScopeManager
import liquibase.database.jvm.JdbcConnection
import liquibase.exception.LiquibaseException
import liquibase.resource.ClassLoaderResourceAccessor
import liquibase.resource.Resource
import liquibase.resource.URIResource
import net.corda.core.identity.CordaX500Name
import net.corda.core.schemas.MappedSchema
import net.corda.core.utilities.contextLogger
@ -14,8 +18,10 @@ import net.corda.nodeapi.internal.MigrationHelpers.getMigrationResource
import net.corda.nodeapi.internal.cordapp.CordappLoader
import java.io.ByteArrayInputStream
import java.io.InputStream
import java.net.URI
import java.nio.file.Path
import java.sql.Connection
import java.util.Collections
import java.util.concurrent.locks.ReentrantLock
import javax.sql.DataSource
import kotlin.concurrent.withLock
@ -36,6 +42,10 @@ open class SchemaMigration(
const val NODE_BASE_DIR_KEY = "liquibase.nodeDaseDir"
const val NODE_X500_NAME = "liquibase.nodeName"
val loader = ThreadLocal<CordappLoader>()
init {
Scope.setScopeManager(ThreadLocalScopeManager())
}
@JvmStatic
protected val mutex = ReentrantLock()
}
@ -46,31 +56,31 @@ open class SchemaMigration(
private val classLoader = cordappLoader?.appClassLoader ?: Thread.currentThread().contextClassLoader
/**
/**
* Will run the Liquibase migration on the actual database.
* @param existingCheckpoints Whether checkpoints exist that would prohibit running a migration
* @param schemas The set of MappedSchemas to check
* @param forceThrowOnMissingMigration throws an exception if a mapped schema is missing the migration resource. Can be set to false
* when allowing hibernate to create missing schemas in dev or tests.
* @param existingCheckpoints Whether checkpoints exist that would prohibit running a migration
* @param schemas The set of MappedSchemas to check
* @param forceThrowOnMissingMigration throws an exception if a mapped schema is missing the migration resource. Can be set to false
* when allowing hibernate to create missing schemas in dev or tests.
*/
fun runMigration(existingCheckpoints: Boolean, schemas: Set<MappedSchema>, forceThrowOnMissingMigration: Boolean) {
val resourcesAndSourceInfo = prepareResources(schemas, forceThrowOnMissingMigration)
// current version of Liquibase appears to be non-threadsafe
// this is apparent when multiple in-process nodes are all running migrations simultaneously
mutex.withLock {
dataSource.connection.use { connection ->
val (runner, _, shouldBlockOnCheckpoints) = prepareRunner(connection, resourcesAndSourceInfo)
if (shouldBlockOnCheckpoints && existingCheckpoints)
throw CheckpointsException()
try {
runner.update(Contexts().toString())
} catch (exp: LiquibaseException) {
throw DatabaseMigrationException(exp.message, exp)
}
}
}
}
fun runMigration(existingCheckpoints: Boolean, schemas: Set<MappedSchema>, forceThrowOnMissingMigration: Boolean) {
val resourcesAndSourceInfo = prepareResources(schemas, forceThrowOnMissingMigration)
Scope.enter(mapOf(Scope.Attr.classLoader.name to classLoader))
// current version of Liquibase appears to be non-threadsafe
// this is apparent when multiple in-process nodes are all running migrations simultaneously
mutex.withLock {
dataSource.connection.use { connection ->
val (runner, _, shouldBlockOnCheckpoints) = prepareRunner(connection, resourcesAndSourceInfo)
if (shouldBlockOnCheckpoints && existingCheckpoints)
throw CheckpointsException()
try {
runner.update(Contexts().toString())
} catch (exp: LiquibaseException) {
throw DatabaseMigrationException(exp.message, exp)
}
}
}
}
/**
* Ensures that the database is up to date with the latest migration changes.
@ -98,7 +108,7 @@ open class SchemaMigration(
* @param forceThrowOnMissingMigration throws an exception if a mapped schema is missing the migration resource. Can be set to false
* when allowing hibernate to create missing schemas in dev or tests.
*/
fun getPendingChangesCount(schemas: Set<MappedSchema>, forceThrowOnMissingMigration: Boolean) : Int {
fun getPendingChangesCount(schemas: Set<MappedSchema>, forceThrowOnMissingMigration: Boolean): Int {
val resourcesAndSourceInfo = prepareResources(schemas, forceThrowOnMissingMigration)
// current version of Liquibase appears to be non-threadsafe
@ -140,19 +150,42 @@ open class SchemaMigration(
/** Create a resource accessor that aggregates the changelogs included in the schemas into one dynamic stream. */
protected class CustomResourceAccessor(val dynamicInclude: String, val changelogList: List<String?>, classLoader: ClassLoader) :
ClassLoaderResourceAccessor(classLoader) {
override fun getResourcesAsStream(path: String): Set<InputStream> {
override fun getAll(path: String?): List<Resource> {
if (path == dynamicInclude) {
// Create a map in Liquibase format including all migration files.
val includeAllFiles = mapOf("databaseChangeLog"
to changelogList.filterNotNull().map { file -> mapOf("include" to mapOf("file" to file)) })
// Transform it to json.
val includeAllFilesJson = ObjectMapper().writeValueAsBytes(includeAllFiles)
// Return the json as a stream.
return setOf(ByteArrayInputStream(includeAllFilesJson))
val inputStream = getPathAsStream()
val resource = object : URIResource(path, URI(path)) {
override fun openInputStream(): InputStream {
return inputStream
}
}
return Collections.singletonList(resource)
}
return super.getResourcesAsStream(path)?.take(1)?.toSet() ?: emptySet()
// Take 1 resource due to LiquidBase find duplicate files which throws an error
return super.getAll(path).take(1)
}
override fun get(path: String?): Resource {
if (path == dynamicInclude) {
// Return the json as a stream.
val inputStream = getPathAsStream()
return object : URIResource(path, URI(path)) {
override fun openInputStream(): InputStream {
return inputStream
}
}
}
return super.get(path)
}
private fun getPathAsStream(): InputStream {
// Create a map in Liquibase format including all migration files.
val includeAllFiles = mapOf("databaseChangeLog"
to changelogList.filterNotNull().map { file -> mapOf("include" to mapOf("file" to file)) })
val includeAllFilesJson = ObjectMapper().writeValueAsBytes(includeAllFiles)
return ByteArrayInputStream(includeAllFilesJson)
}
}
@ -184,6 +217,7 @@ open class SchemaMigration(
if (ourName != null) {
System.setProperty(NODE_X500_NAME, ourName.toString())
}
Scope.enter(mapOf(Scope.Attr.classLoader.name to classLoader))
val customResourceAccessor = CustomResourceAccessor(dynamicInclude, changelogList, classLoader)
checkResourcesInClassPath(changelogList)
return listOf(Pair(customResourceAccessor, ""))

View File

@ -8,6 +8,7 @@ import liquibase.database.Database
import liquibase.database.core.H2Database
import liquibase.database.jvm.JdbcConnection
import liquibase.resource.ClassLoaderResourceAccessor
import liquibase.resource.Resource
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.toStringShort
import net.corda.core.identity.CordaX500Name
@ -84,7 +85,9 @@ class IdenityServiceKeyRotationMigrationTest {
persist(charlie2.party.dbParty())
Liquibase("migration/node-core.changelog-v20.xml", object : ClassLoaderResourceAccessor() {
override fun getResourcesAsStream(path: String) = super.getResourcesAsStream(path)?.firstOrNull()?.let { setOf(it) }
override fun getAll(path: String?): List<Resource> {
return super.getAll(path).take(1).toList()
}
}, liquibaseDB).update(Contexts().toString())
val dummyKey = Crypto.generateKeyPair().public

View File

@ -114,7 +114,7 @@ open class MockServices private constructor(
}
val props = Properties()
props.setProperty("dataSourceClassName", "org.h2.jdbcx.JdbcDataSource")
props.setProperty("dataSource.url", "jdbc:h2:file:$dbPath;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE")
props.setProperty("dataSource.url", "jdbc:h2:file:$dbPath;NON_KEYWORDS=KEY,VALUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE")
props.setProperty("dataSource.user", "sa")
props.setProperty("dataSource.password", "")
return props