INFRA-604: Switch tests to use database snapshots (#6671)

Created a database snapshot of a clean Corda OS 4.5.1 database, which can now be used for testing by both the node driver and mock network.

The MockNetwork was changed from using an in memory database to using an on disk database, and makes use of the snapshot to speed up setup times.

The Node Driver was changed from defaulting to an in-memory database to defaulting to an on-disk database. Tests that do not specify the type of database to use will thus use an on-disk database. Tests that opt in for an in-memory database will continue to use an in-memory database as before.

The database snapshots are copied to the node directory inside the build folder, therefore, they should be cleaned up after a build.

Co-authored-by: Ross Nicoll <ross.nicoll@r3.com>
This commit is contained in:
Jonathan Locke 2020-09-03 14:43:55 +01:00 committed by GitHub
parent 9962c9085d
commit e562c5828b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 184 additions and 22 deletions

View File

@ -50,7 +50,8 @@ class H2SecurityTests {
inMemoryDB = false, inMemoryDB = false,
startNodesInProcess = false, startNodesInProcess = false,
notarySpecs = emptyList(), notarySpecs = emptyList(),
cordappsForAllNodes = emptyList() cordappsForAllNodes = emptyList(),
premigrateH2Database = false
)) { )) {
val port = getFreePort() val port = getFreePort()
startNode(customOverrides = mapOf(h2AddressKey to "localhost:$port", dbPasswordKey to "x")).getOrThrow() startNode(customOverrides = mapOf(h2AddressKey to "localhost:$port", dbPasswordKey to "x")).getOrThrow()
@ -71,7 +72,8 @@ class H2SecurityTests {
inMemoryDB = false, inMemoryDB = false,
startNodesInProcess = false, startNodesInProcess = false,
notarySpecs = emptyList(), notarySpecs = emptyList(),
cordappsForAllNodes = listOf(enclosedCordapp()) cordappsForAllNodes = listOf(enclosedCordapp()),
premigrateH2Database = false
)) { )) {
val port = getFreePort() val port = getFreePort()
val nodeHandle = startNode(rpcUsers = listOf(user), customOverrides = mapOf(h2AddressKey to "localhost:$port", val nodeHandle = startNode(rpcUsers = listOf(user), customOverrides = mapOf(h2AddressKey to "localhost:$port",

View File

@ -82,7 +82,8 @@ class LargeTransactionsTest {
driver(DriverParameters( driver(DriverParameters(
startNodesInProcess = true, startNodesInProcess = true,
cordappsForAllNodes = listOf(DUMMY_CONTRACTS_CORDAPP, enclosedCordapp()), cordappsForAllNodes = listOf(DUMMY_CONTRACTS_CORDAPP, enclosedCordapp()),
networkParameters = testNetworkParameters(maxMessageSize = 15.MB.toInt(), maxTransactionSize = 13.MB.toInt()) networkParameters = testNetworkParameters(maxMessageSize = 15.MB.toInt(), maxTransactionSize = 13.MB.toInt()),
premigrateH2Database = false
)) { )) {
val rpcUser = User("admin", "admin", setOf("ALL")) val rpcUser = User("admin", "admin", setOf("ALL"))
val (alice, _) = listOf(ALICE_NAME, BOB_NAME).map { startNode(providedName = it, rpcUsers = listOf(rpcUser)) }.transpose().getOrThrow() val (alice, _) = listOf(ALICE_NAME, BOB_NAME).map { startNode(providedName = it, rpcUsers = listOf(rpcUser)) }.transpose().getOrThrow()

View File

@ -43,21 +43,28 @@ data class NotaryHandle(val identity: Party, val validating: Boolean, val nodeHa
interface NodeHandle : AutoCloseable { interface NodeHandle : AutoCloseable {
/** Get the [NodeInfo] for this node */ /** Get the [NodeInfo] for this node */
val nodeInfo: NodeInfo val nodeInfo: NodeInfo
/** /**
* Interface to the node's RPC system. The first RPC user will be used to login if are any, otherwise a default one * Interface to the node's RPC system. The first RPC user will be used to login if are any, otherwise a default one
* will be added and that will be used. * will be added and that will be used.
*/ */
val rpc: CordaRPCOps val rpc: CordaRPCOps
/** Get the p2p address for this node **/ /** Get the p2p address for this node **/
val p2pAddress: NetworkHostAndPort val p2pAddress: NetworkHostAndPort
/** Get the rpc address for this node **/ /** Get the rpc address for this node **/
val rpcAddress: NetworkHostAndPort val rpcAddress: NetworkHostAndPort
/** Get the rpc admin address for this node **/ /** Get the rpc admin address for this node **/
val rpcAdminAddress: NetworkHostAndPort val rpcAdminAddress: NetworkHostAndPort
/** Get the JMX server address for this node, if JMX is enabled **/ /** Get the JMX server address for this node, if JMX is enabled **/
val jmxAddress: NetworkHostAndPort? val jmxAddress: NetworkHostAndPort?
/** Get a [List] of [User]'s for this node **/ /** Get a [List] of [User]'s for this node **/
val rpcUsers: List<User> val rpcUsers: List<User>
/** The location of the node's base directory **/ /** The location of the node's base directory **/
val baseDirectory: Path val baseDirectory: Path
@ -67,7 +74,8 @@ interface NodeHandle : AutoCloseable {
fun stop() fun stop()
} }
fun NodeHandle.logFile(): File = (baseDirectory / "logs").toFile().walk().filter { it.name.startsWith("node-") && it.extension == "log" }.single() fun NodeHandle.logFile(): File = (baseDirectory / "logs").toFile().walk().filter { it.name.startsWith("node-") && it.extension == "log" }
.single()
/** Interface which represents an out of process node and exposes its process handle. **/ /** Interface which represents an out of process node and exposes its process handle. **/
@DoNotImplement @DoNotImplement
@ -91,7 +99,8 @@ interface InProcess : NodeHandle {
* Starts an already constructed flow. Note that you must be on the server thread to call this method. * Starts an already constructed flow. Note that you must be on the server thread to call this method.
* @param context indicates who started the flow, see: [InvocationContext]. * @param context indicates who started the flow, see: [InvocationContext].
*/ */
fun <T> startFlow(logic: FlowLogic<T>): CordaFuture<T> = internalServices.startFlow(logic, internalServices.newContext()).getOrThrow().resultFuture fun <T> startFlow(logic: FlowLogic<T>): CordaFuture<T> = internalServices.startFlow(logic, internalServices.newContext())
.getOrThrow().resultFuture
} }
/** /**
@ -206,7 +215,8 @@ fun <A> driver(defaultParameters: DriverParameters = DriverParameters(), dsl: Dr
djvmBootstrapSource = defaultParameters.djvmBootstrapSource, djvmBootstrapSource = defaultParameters.djvmBootstrapSource,
djvmCordaSource = defaultParameters.djvmCordaSource, djvmCordaSource = defaultParameters.djvmCordaSource,
environmentVariables = defaultParameters.environmentVariables, environmentVariables = defaultParameters.environmentVariables,
allowHibernateToManageAppSchema = defaultParameters.allowHibernateToManageAppSchema allowHibernateToManageAppSchema = defaultParameters.allowHibernateToManageAppSchema,
premigrateH2Database = defaultParameters.premigrateH2Database
), ),
coerce = { it }, coerce = { it },
dsl = dsl dsl = dsl
@ -245,6 +255,8 @@ fun <A> driver(defaultParameters: DriverParameters = DriverParameters(), dsl: Dr
* @property cordappsForAllNodes [TestCordapp]s that will be added to each node started by the [DriverDSL]. * @property cordappsForAllNodes [TestCordapp]s that will be added to each node started by the [DriverDSL].
* @property djvmBootstrapSource Location of a JAR containing the Java APIs for the DJVM to use. * @property djvmBootstrapSource Location of a JAR containing the Java APIs for the DJVM to use.
* @property djvmCordaSource Locations of JARs of user-supplied classes to execute within the DJVM sandbox. * @property djvmCordaSource Locations of JARs of user-supplied classes to execute within the DJVM sandbox.
* @property premigrateH2Database Whether to use a prebuilt H2 database schema or start from an empty schema.
* This can save time for tests which do not need to migrate from a blank schema.
*/ */
@Suppress("unused") @Suppress("unused")
data class DriverParameters( data class DriverParameters(
@ -263,12 +275,13 @@ data class DriverParameters(
@Suppress("DEPRECATION") val jmxPolicy: JmxPolicy = JmxPolicy(), @Suppress("DEPRECATION") val jmxPolicy: JmxPolicy = JmxPolicy(),
val networkParameters: NetworkParameters = testNetworkParameters(notaries = emptyList()), val networkParameters: NetworkParameters = testNetworkParameters(notaries = emptyList()),
val notaryCustomOverrides: Map<String, Any?> = emptyMap(), val notaryCustomOverrides: Map<String, Any?> = emptyMap(),
val inMemoryDB: Boolean = true, val inMemoryDB: Boolean = false,
val cordappsForAllNodes: Collection<TestCordapp>? = null, val cordappsForAllNodes: Collection<TestCordapp>? = null,
val djvmBootstrapSource: Path? = null, val djvmBootstrapSource: Path? = null,
val djvmCordaSource: List<Path> = emptyList(), val djvmCordaSource: List<Path> = emptyList(),
val environmentVariables : Map<String, String> = emptyMap(), val environmentVariables: Map<String, String> = emptyMap(),
val allowHibernateToManageAppSchema: Boolean = true val allowHibernateToManageAppSchema: Boolean = true,
val premigrateH2Database: Boolean = true
) { ) {
constructor(cordappsForAllNodes: Collection<TestCordapp>) : this(isDebug = false, cordappsForAllNodes = cordappsForAllNodes) constructor(cordappsForAllNodes: Collection<TestCordapp>) : this(isDebug = false, cordappsForAllNodes = cordappsForAllNodes)
@ -376,6 +389,49 @@ data class DriverParameters(
cordappsForAllNodes = null cordappsForAllNodes = null
) )
constructor(
isDebug: Boolean = false,
driverDirectory: Path = Paths.get("build") / "node-driver" / getTimestampAsDirectoryName(),
portAllocation: PortAllocation = incrementalPortAllocation(),
debugPortAllocation: PortAllocation = incrementalPortAllocation(),
systemProperties: Map<String, String> = emptyMap(),
useTestClock: Boolean = false,
startNodesInProcess: Boolean = false,
waitForAllNodesToFinish: Boolean = false,
notarySpecs: List<NotarySpec> = listOf(NotarySpec(DUMMY_NOTARY_NAME)),
extraCordappPackagesToScan: List<String> = emptyList(),
@Suppress("DEPRECATION") jmxPolicy: JmxPolicy = JmxPolicy(),
networkParameters: NetworkParameters = testNetworkParameters(notaries = emptyList()),
notaryCustomOverrides: Map<String, Any?> = emptyMap(),
inMemoryDB: Boolean = false,
cordappsForAllNodes: Collection<TestCordapp>? = null,
djvmBootstrapSource: Path? = null,
djvmCordaSource: List<Path> = emptyList(),
environmentVariables: Map<String, String> = emptyMap(),
allowHibernateToManageAppSchema: Boolean = true
) : this(
isDebug,
driverDirectory,
portAllocation,
debugPortAllocation,
systemProperties,
useTestClock,
startNodesInProcess,
waitForAllNodesToFinish,
notarySpecs,
extraCordappPackagesToScan,
jmxPolicy,
networkParameters,
notaryCustomOverrides,
inMemoryDB,
cordappsForAllNodes,
djvmBootstrapSource,
djvmCordaSource,
environmentVariables,
allowHibernateToManageAppSchema,
premigrateH2Database = true
)
constructor( constructor(
isDebug: Boolean, isDebug: Boolean,
driverDirectory: Path, driverDirectory: Path,
@ -417,6 +473,7 @@ data class DriverParameters(
fun withStartNodesInProcess(startNodesInProcess: Boolean): DriverParameters = copy(startNodesInProcess = startNodesInProcess) fun withStartNodesInProcess(startNodesInProcess: Boolean): DriverParameters = copy(startNodesInProcess = startNodesInProcess)
fun withWaitForAllNodesToFinish(waitForAllNodesToFinish: Boolean): DriverParameters = copy(waitForAllNodesToFinish = waitForAllNodesToFinish) fun withWaitForAllNodesToFinish(waitForAllNodesToFinish: Boolean): DriverParameters = copy(waitForAllNodesToFinish = waitForAllNodesToFinish)
fun withNotarySpecs(notarySpecs: List<NotarySpec>): DriverParameters = copy(notarySpecs = notarySpecs) fun withNotarySpecs(notarySpecs: List<NotarySpec>): DriverParameters = copy(notarySpecs = notarySpecs)
@Deprecated("extraCordappPackagesToScan does not preserve the original CorDapp's versioning and metadata, which may lead to " + @Deprecated("extraCordappPackagesToScan does not preserve the original CorDapp's versioning and metadata, which may lead to " +
"misleading results in tests. Use withCordappsForAllNodes instead.") "misleading results in tests. Use withCordappsForAllNodes instead.")
fun withExtraCordappPackagesToScan(extraCordappPackagesToScan: List<String>): DriverParameters = copy(extraCordappPackagesToScan = extraCordappPackagesToScan) fun withExtraCordappPackagesToScan(extraCordappPackagesToScan: List<String>): DriverParameters = copy(extraCordappPackagesToScan = extraCordappPackagesToScan)
@ -428,7 +485,7 @@ data class DriverParameters(
fun withCordappsForAllNodes(cordappsForAllNodes: Collection<TestCordapp>?): DriverParameters = copy(cordappsForAllNodes = cordappsForAllNodes) fun withCordappsForAllNodes(cordappsForAllNodes: Collection<TestCordapp>?): DriverParameters = copy(cordappsForAllNodes = cordappsForAllNodes)
fun withDjvmBootstrapSource(djvmBootstrapSource: Path?): DriverParameters = copy(djvmBootstrapSource = djvmBootstrapSource) fun withDjvmBootstrapSource(djvmBootstrapSource: Path?): DriverParameters = copy(djvmBootstrapSource = djvmBootstrapSource)
fun withDjvmCordaSource(djvmCordaSource: List<Path>): DriverParameters = copy(djvmCordaSource = djvmCordaSource) fun withDjvmCordaSource(djvmCordaSource: List<Path>): DriverParameters = copy(djvmCordaSource = djvmCordaSource)
fun withEnvironmentVariables(variables : Map<String, String>): DriverParameters = copy(environmentVariables = variables) fun withEnvironmentVariables(variables: Map<String, String>): DriverParameters = copy(environmentVariables = variables)
fun withAllowHibernateToManageAppSchema(value: Boolean): DriverParameters = copy(allowHibernateToManageAppSchema = value) fun withAllowHibernateToManageAppSchema(value: Boolean): DriverParameters = copy(allowHibernateToManageAppSchema = value)
fun copy( fun copy(
@ -530,4 +587,48 @@ data class DriverParameters(
djvmCordaSource = djvmCordaSource, djvmCordaSource = djvmCordaSource,
environmentVariables = environmentVariables environmentVariables = environmentVariables
) )
// Legacy copy() from v4.5
@Suppress("LongParameterList")
fun copy(isDebug: Boolean,
driverDirectory: Path,
portAllocation: PortAllocation,
debugPortAllocation: PortAllocation,
systemProperties: Map<String, String>,
useTestClock: Boolean,
startNodesInProcess: Boolean,
waitForAllNodesToFinish: Boolean,
notarySpecs: List<NotarySpec>,
extraCordappPackagesToScan: List<String>,
@Suppress("DEPRECATION") jmxPolicy: JmxPolicy,
networkParameters: NetworkParameters,
notaryCustomOverrides: Map<String, Any?>,
inMemoryDB: Boolean,
cordappsForAllNodes: Collection<TestCordapp>?,
djvmBootstrapSource: Path?,
djvmCordaSource: List<Path>,
environmentVariables: Map<String, String>,
allowHibernateToManageAppSchema: Boolean
) = this.copy(
isDebug = isDebug,
driverDirectory = driverDirectory,
portAllocation = portAllocation,
debugPortAllocation = debugPortAllocation,
systemProperties = systemProperties,
useTestClock = useTestClock,
startNodesInProcess = startNodesInProcess,
waitForAllNodesToFinish = waitForAllNodesToFinish,
notarySpecs = notarySpecs,
extraCordappPackagesToScan = extraCordappPackagesToScan,
jmxPolicy = jmxPolicy,
networkParameters = networkParameters,
notaryCustomOverrides = notaryCustomOverrides,
inMemoryDB = inMemoryDB,
cordappsForAllNodes = cordappsForAllNodes,
djvmBootstrapSource = djvmBootstrapSource,
djvmCordaSource = djvmCordaSource,
environmentVariables = environmentVariables,
allowHibernateToManageAppSchema = allowHibernateToManageAppSchema,
premigrateH2Database = true
)
} }

View File

@ -0,0 +1,23 @@
package net.corda.testing.node
import java.io.InputStream
import java.nio.file.Files
import java.nio.file.Path
object DatabaseSnapshot {
private const val previousCordaVersion: String = "4.5.1"
private const val databaseName: String = "persistence.mv.db"
private fun getDatabaseSnapshotStream(): InputStream {
val resourceUri = this::class.java.getResource("/databasesnapshots/${previousCordaVersion}/$databaseName")
return resourceUri.openStream()
}
fun copyDatabaseSnapshot(baseDirectory: Path) {
getDatabaseSnapshotStream().use { stream ->
Files.createDirectories(baseDirectory)
val path = baseDirectory.resolve(databaseName)
Files.copy(stream, path)
}
}
}

View File

@ -48,6 +48,7 @@ import net.corda.testing.internal.configureDatabase
import net.corda.testing.node.internal.* import net.corda.testing.node.internal.*
import net.corda.testing.services.MockAttachmentStorage import net.corda.testing.services.MockAttachmentStorage
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.nio.file.Paths
import java.security.KeyPair import java.security.KeyPair
import java.sql.Connection import java.sql.Connection
import java.time.Clock import java.time.Clock
@ -99,9 +100,17 @@ open class MockServices private constructor(
// TODO: Can we use an X509 principal generator here? // TODO: Can we use an X509 principal generator here?
@JvmStatic @JvmStatic
fun makeTestDataSourceProperties(nodeName: String = SecureHash.randomSHA256().toString()): Properties { fun makeTestDataSourceProperties(nodeName: String = SecureHash.randomSHA256().toString()): Properties {
val dbDir = Paths.get("","build", "mocknetworktestdb", nodeName)
.toAbsolutePath()
val dbPath = dbDir.resolve("persistence")
try {
DatabaseSnapshot.copyDatabaseSnapshot(dbDir)
} catch (ex: java.nio.file.FileAlreadyExistsException) {
DriverDSLImpl.log.warn("Database already exists on disk, not attempting to pre-migrate database.")
}
val props = Properties() val props = Properties()
props.setProperty("dataSourceClassName", "org.h2.jdbcx.JdbcDataSource") props.setProperty("dataSourceClassName", "org.h2.jdbcx.JdbcDataSource")
props.setProperty("dataSource.url", "jdbc:h2:mem:${nodeName}_persistence;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE") props.setProperty("dataSource.url", "jdbc:h2:file:$dbPath;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE")
props.setProperty("dataSource.user", "sa") props.setProperty("dataSource.user", "sa")
props.setProperty("dataSource.password", "") props.setProperty("dataSource.password", "")
return props return props
@ -357,7 +366,6 @@ open class MockServices private constructor(
constructor(cordappPackages: List<String>, initialIdentityName: CordaX500Name, identityService: IdentityService, networkParameters: NetworkParameters) constructor(cordappPackages: List<String>, initialIdentityName: CordaX500Name, identityService: IdentityService, networkParameters: NetworkParameters)
: this(cordappPackages, TestIdentity(initialIdentityName), identityService, networkParameters) : this(cordappPackages, TestIdentity(initialIdentityName), identityService, networkParameters)
constructor(cordappPackages: List<String>, initialIdentityName: CordaX500Name, identityService: IdentityService, networkParameters: NetworkParameters, key: KeyPair) constructor(cordappPackages: List<String>, initialIdentityName: CordaX500Name, identityService: IdentityService, networkParameters: NetworkParameters, key: KeyPair)
: this(cordappPackages, TestIdentity(initialIdentityName, key), identityService, networkParameters) : this(cordappPackages, TestIdentity(initialIdentityName, key), identityService, networkParameters)
@ -428,7 +436,8 @@ open class MockServices private constructor(
private val mockCordappProvider: MockCordappProvider = MockCordappProvider(cordappLoader, attachments).also { private val mockCordappProvider: MockCordappProvider = MockCordappProvider(cordappLoader, attachments).also {
it.start() it.start()
} }
override val transactionVerifierService: TransactionVerifierService get() = InMemoryTransactionVerifierService( override val transactionVerifierService: TransactionVerifierService
get() = InMemoryTransactionVerifierService(
numberOfWorkers = 2, numberOfWorkers = 2,
cordappProvider = mockCordappProvider, cordappProvider = mockCordappProvider,
attachments = attachments attachments = attachments

View File

@ -53,6 +53,7 @@ import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.millis import net.corda.core.utilities.millis
import net.corda.core.utilities.toHexString
import net.corda.coretesting.internal.stubs.CertificateStoreStubs import net.corda.coretesting.internal.stubs.CertificateStoreStubs
import net.corda.node.NodeRegistrationOption import net.corda.node.NodeRegistrationOption
import net.corda.node.VersionInfo import net.corda.node.VersionInfo
@ -97,6 +98,7 @@ import net.corda.testing.driver.internal.InProcessImpl
import net.corda.testing.driver.internal.NodeHandleInternal import net.corda.testing.driver.internal.NodeHandleInternal
import net.corda.testing.driver.internal.OutOfProcessImpl import net.corda.testing.driver.internal.OutOfProcessImpl
import net.corda.testing.node.ClusterSpec import net.corda.testing.node.ClusterSpec
import net.corda.testing.node.DatabaseSnapshot
import net.corda.testing.node.NotarySpec import net.corda.testing.node.NotarySpec
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
@ -149,7 +151,8 @@ class DriverDSLImpl(
val djvmBootstrapSource: Path?, val djvmBootstrapSource: Path?,
val djvmCordaSource: List<Path>, val djvmCordaSource: List<Path>,
val environmentVariables: Map<String, String>, val environmentVariables: Map<String, String>,
val allowHibernateToManageAppSchema: Boolean = true val allowHibernateToManageAppSchema: Boolean = true,
val premigrateH2Database: Boolean = true
) : InternalDriverDSL { ) : InternalDriverDSL {
private var _executorService: ScheduledExecutorService? = null private var _executorService: ScheduledExecutorService? = null
@ -195,7 +198,7 @@ class DriverDSLImpl(
} }
private fun NodeConfig.checkAndOverrideForInMemoryDB(): NodeConfig = this.run { private fun NodeConfig.checkAndOverrideForInMemoryDB(): NodeConfig = this.run {
if (inMemoryDB && corda.dataSourceProperties.getProperty("dataSource.url").startsWith("jdbc:h2:")) { if (inMemoryDB && isH2Database(corda)) {
val jdbcUrl = "jdbc:h2:mem:persistence${inMemoryCounter.getAndIncrement()};DB_CLOSE_ON_EXIT=FALSE;LOCK_TIMEOUT=10000;WRITE_DELAY=100" val jdbcUrl = "jdbc:h2:mem:persistence${inMemoryCounter.getAndIncrement()};DB_CLOSE_ON_EXIT=FALSE;LOCK_TIMEOUT=10000;WRITE_DELAY=100"
corda.dataSourceProperties.setProperty("dataSource.url", jdbcUrl) corda.dataSourceProperties.setProperty("dataSource.url", jdbcUrl)
NodeConfig(typesafe + mapOf("dataSourceProperties" to mapOf("dataSource.url" to jdbcUrl))) NodeConfig(typesafe + mapOf("dataSourceProperties" to mapOf("dataSource.url" to jdbcUrl)))
@ -269,6 +272,15 @@ class DriverDSLImpl(
val name = parameters.providedName ?: CordaX500Name("${oneOf(names).organisation}-${p2pAddress.port}", "London", "GB") val name = parameters.providedName ?: CordaX500Name("${oneOf(names).organisation}-${p2pAddress.port}", "London", "GB")
val config = createConfig(name, parameters, p2pAddress) val config = createConfig(name, parameters, p2pAddress)
if (premigrateH2Database && isH2Database(config)) {
if (!inMemoryDB) {
try {
DatabaseSnapshot.copyDatabaseSnapshot(config.corda.baseDirectory)
} catch (ex: java.nio.file.FileAlreadyExistsException) {
log.warn("Database already exists on disk, not attempting to pre-migrate database.")
}
}
}
val registrationFuture = if (compatibilityZone?.rootCert != null) { val registrationFuture = if (compatibilityZone?.rootCert != null) {
// We don't need the network map to be available to be able to register the node // We don't need the network map to be available to be able to register the node
createSchema(config, false).flatMap { startNodeRegistration(it, compatibilityZone.rootCert, compatibilityZone.config()) } createSchema(config, false).flatMap { startNodeRegistration(it, compatibilityZone.rootCert, compatibilityZone.config()) }
@ -1032,6 +1044,12 @@ class DriverDSLImpl(
) )
} }
private fun isH2Database(config: NodeConfiguration)
= config.dataSourceProperties.getProperty("dataSource.url").startsWith("jdbc:h2:")
private fun isH2Database(config: NodeConfig)
= isH2Database(config.corda)
// Obvious test artifacts. This is NOT intended to be an exhaustive list! // Obvious test artifacts. This is NOT intended to be an exhaustive list!
// It is only intended to remove those FEW jars which BLATANTLY do not // It is only intended to remove those FEW jars which BLATANTLY do not
// belong inside a Corda Node. // belong inside a Corda Node.
@ -1298,7 +1316,8 @@ fun <DI : DriverDSL, D : InternalDriverDSL, A> genericDriver(
djvmBootstrapSource = defaultParameters.djvmBootstrapSource, djvmBootstrapSource = defaultParameters.djvmBootstrapSource,
djvmCordaSource = defaultParameters.djvmCordaSource, djvmCordaSource = defaultParameters.djvmCordaSource,
environmentVariables = defaultParameters.environmentVariables, environmentVariables = defaultParameters.environmentVariables,
allowHibernateToManageAppSchema = defaultParameters.allowHibernateToManageAppSchema allowHibernateToManageAppSchema = defaultParameters.allowHibernateToManageAppSchema,
premigrateH2Database = defaultParameters.premigrateH2Database
) )
) )
val shutdownHook = addShutdownHook(driverDsl::shutdown) val shutdownHook = addShutdownHook(driverDsl::shutdown)
@ -1397,6 +1416,7 @@ fun <A> internalDriver(
djvmCordaSource: List<Path> = emptyList(), djvmCordaSource: List<Path> = emptyList(),
environmentVariables: Map<String, String> = emptyMap(), environmentVariables: Map<String, String> = emptyMap(),
allowHibernateToManageAppSchema: Boolean = true, allowHibernateToManageAppSchema: Boolean = true,
premigrateH2Database: Boolean = true,
dsl: DriverDSLImpl.() -> A dsl: DriverDSLImpl.() -> A
): A { ): A {
return genericDriver( return genericDriver(
@ -1420,15 +1440,21 @@ fun <A> internalDriver(
djvmBootstrapSource = djvmBootstrapSource, djvmBootstrapSource = djvmBootstrapSource,
djvmCordaSource = djvmCordaSource, djvmCordaSource = djvmCordaSource,
environmentVariables = environmentVariables, environmentVariables = environmentVariables,
allowHibernateToManageAppSchema = allowHibernateToManageAppSchema allowHibernateToManageAppSchema = allowHibernateToManageAppSchema,
premigrateH2Database = premigrateH2Database
), ),
coerce = { it }, coerce = { it },
dsl = dsl dsl = dsl
) )
} }
val DIRECTORY_TIMESTAMP_FORMAT: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd-HHmmss.SSS").withZone(UTC)
private val directoryRandom = Random()
fun getTimestampAsDirectoryName(): String { fun getTimestampAsDirectoryName(): String {
return DateTimeFormatter.ofPattern("yyyyMMdd-HHmmss.SSS").withZone(UTC).format(Instant.now()) val base = DIRECTORY_TIMESTAMP_FORMAT.format(Instant.now())
// Introduce some randomness so starting two nodes in the same ms doesn't use the same path
val random = directoryRandom.nextLong().toBigInteger().toByteArray().toHexString()
return "$base-$random"
} }
fun writeConfig(path: Path, filename: String, config: Config) { fun writeConfig(path: Path, filename: String, config: Config) {