mirror of
https://github.com/corda/corda.git
synced 2025-05-02 08:43:15 +00:00
Corda now works with H2 without the need to allow Hibernate to create the database automatically. (#2124)
[CORDA-815]: Corda now instructs Hibernate to either adjust or validate the schema based on `devMode` property. Also renamed property `database.initDatabase` to `database.createSchemaAutomatically`. * [CORDA-815]: Renamed database.initDatabase to database.adjustSchemas. * Code review changes: removed property `database.initDatabase` altogether. * Code review changes: removed property `database.initDatabase` altogether. * Code review changes: removed property `database.initDatabase` altogether. * Code review changes: removed property `database.initDatabase` altogether.
This commit is contained in:
parent
74bf00c155
commit
cb1fa2e017
@ -6,6 +6,9 @@ from the previous milestone release.
|
|||||||
|
|
||||||
UNRELEASED
|
UNRELEASED
|
||||||
----------
|
----------
|
||||||
|
* Removed confusing property database.initDatabase, enabling its guarded behaviour with the dev-mode.
|
||||||
|
In devMode Hibernate will try to create or update database schemas, otherwise it will expect relevant schemas to be present
|
||||||
|
in the database (pre configured via DDL scripts or equivalent), and validate these are correct.
|
||||||
|
|
||||||
* ``AttachmentStorage`` now allows providing metadata on attachments upload - username and filename, currently as plain
|
* ``AttachmentStorage`` now allows providing metadata on attachments upload - username and filename, currently as plain
|
||||||
strings. Those can be then used for querying, utilizing ``queryAttachments`` method of the same interface.
|
strings. Those can be then used for querying, utilizing ``queryAttachments`` method of the same interface.
|
||||||
|
@ -70,7 +70,6 @@ path to the node's base directory.
|
|||||||
|
|
||||||
:database: Database configuration:
|
:database: Database configuration:
|
||||||
|
|
||||||
:initDatabase: Boolean on whether to initialise the database or just validate the schema. Defaults to true.
|
|
||||||
:serverNameTablePrefix: Prefix string to apply to all the database tables. The default is no prefix.
|
:serverNameTablePrefix: Prefix string to apply to all the database tables. The default is no prefix.
|
||||||
:transactionIsolationLevel: Transaction isolation level as defined by the ``TRANSACTION_`` constants in
|
:transactionIsolationLevel: Transaction isolation level as defined by the ``TRANSACTION_`` constants in
|
||||||
``java.sql.Connection``, but without the "TRANSACTION_" prefix. Defaults to REPEATABLE_READ.
|
``java.sql.Connection``, but without the "TRANSACTION_" prefix. Defaults to REPEATABLE_READ.
|
||||||
@ -141,7 +140,9 @@ path to the node's base directory.
|
|||||||
:devMode: This flag sets the node to run in development mode. On startup, if the keystore ``<workspace>/certificates/sslkeystore.jks``
|
:devMode: This flag sets the node to run in development mode. On startup, if the keystore ``<workspace>/certificates/sslkeystore.jks``
|
||||||
does not exist, a developer keystore will be used if ``devMode`` is true. The node will exit if ``devMode`` is false
|
does not exist, a developer keystore will be used if ``devMode`` is true. The node will exit if ``devMode`` is false
|
||||||
and the keystore does not exist. ``devMode`` also turns on background checking of flow checkpoints to shake out any
|
and the keystore does not exist. ``devMode`` also turns on background checking of flow checkpoints to shake out any
|
||||||
bugs in the checkpointing process.
|
bugs in the checkpointing process. Also, if ``devMode`` is true, Hibernate will try to automatically create the schema required by Corda
|
||||||
|
or update an existing schema in the SQL database; if ``devMode`` is false, Hibernate will simply validate an existing schema
|
||||||
|
failing on node start if this schema is either not present or not compatible.
|
||||||
|
|
||||||
:detectPublicIp: This flag toggles the auto IP detection behaviour, it is enabled by default. On startup the node will
|
:detectPublicIp: This flag toggles the auto IP detection behaviour, it is enabled by default. On startup the node will
|
||||||
attempt to discover its externally visible IP address first by looking for any public addresses on its network
|
attempt to discover its externally visible IP address first by looking for any public addresses on its network
|
||||||
|
@ -65,6 +65,40 @@ class NodeStatePersistenceTests {
|
|||||||
val retrievedMessage = stateAndRef!!.state.data.message
|
val retrievedMessage = stateAndRef!!.state.data.message
|
||||||
assertEquals(message, retrievedMessage)
|
assertEquals(message, retrievedMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `persistent state survives node restart without reinitialising database schema`() {
|
||||||
|
// Temporary disable this test when executed on Windows. It is known to be sporadically failing.
|
||||||
|
// More investigation is needed to establish why.
|
||||||
|
assumeFalse(System.getProperty("os.name").toLowerCase().startsWith("win"))
|
||||||
|
|
||||||
|
val user = User("mark", "dadada", setOf(startFlow<SendMessageFlow>(), invokeRpc("vaultQuery")))
|
||||||
|
val message = Message("Hello world!")
|
||||||
|
val stateAndRef: StateAndRef<MessageState>? = driver(isDebug = true, startNodesInProcess = isQuasarAgentSpecified()) {
|
||||||
|
val nodeName = {
|
||||||
|
val nodeHandle = startNode(rpcUsers = listOf(user)).getOrThrow()
|
||||||
|
val nodeName = nodeHandle.nodeInfo.chooseIdentity().name
|
||||||
|
// Ensure the notary node has finished starting up, before starting a flow that needs a notary
|
||||||
|
defaultNotaryNode.getOrThrow()
|
||||||
|
nodeHandle.rpcClientToNode().start(user.username, user.password).use {
|
||||||
|
it.proxy.startFlow(::SendMessageFlow, message, defaultNotaryIdentity).returnValue.getOrThrow()
|
||||||
|
}
|
||||||
|
nodeHandle.stop()
|
||||||
|
nodeName
|
||||||
|
}()
|
||||||
|
|
||||||
|
val nodeHandle = startNode(providedName = nodeName, rpcUsers = listOf(user), customOverrides = mapOf("devMode" to "false")).getOrThrow()
|
||||||
|
val result = nodeHandle.rpcClientToNode().start(user.username, user.password).use {
|
||||||
|
val page = it.proxy.vaultQuery(MessageState::class.java)
|
||||||
|
page.states.singleOrNull()
|
||||||
|
}
|
||||||
|
nodeHandle.stop()
|
||||||
|
result
|
||||||
|
}
|
||||||
|
assertNotNull(stateAndRef)
|
||||||
|
val retrievedMessage = stateAndRef!!.state.data.message
|
||||||
|
assertEquals(message, retrievedMessage)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isQuasarAgentSpecified(): Boolean {
|
fun isQuasarAgentSpecified(): Boolean {
|
||||||
|
@ -79,7 +79,9 @@ class CordappLoader private constructor(private val cordappJarPaths: List<Restri
|
|||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
fun createDefaultWithTestPackages(configuration: NodeConfiguration, testPackages: List<String>): CordappLoader {
|
fun createDefaultWithTestPackages(configuration: NodeConfiguration, testPackages: List<String>): CordappLoader {
|
||||||
check(configuration.devMode) { "Package scanning can only occur in dev mode" }
|
if (!configuration.devMode) {
|
||||||
|
logger.warn("Package scanning should only occur in dev mode!")
|
||||||
|
}
|
||||||
val paths = getCordappsInDirectory(getCordappsPath(configuration.baseDirectory)) + testPackages.flatMap(this::createScanPackage)
|
val paths = getCordappsInDirectory(getCordappsPath(configuration.baseDirectory)) + testPackages.flatMap(this::createScanPackage)
|
||||||
return cordappLoadersCache.computeIfAbsent(paths, { CordappLoader(paths) })
|
return cordappLoadersCache.computeIfAbsent(paths, { CordappLoader(paths) })
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ interface NodeConfiguration : NodeSSLConfiguration {
|
|||||||
val emailAddress: String
|
val emailAddress: String
|
||||||
val exportJMXto: String
|
val exportJMXto: String
|
||||||
val dataSourceProperties: Properties
|
val dataSourceProperties: Properties
|
||||||
val database: DatabaseConfig
|
|
||||||
val rpcUsers: List<User>
|
val rpcUsers: List<User>
|
||||||
val devMode: Boolean
|
val devMode: Boolean
|
||||||
val devModeOptions: DevModeOptions?
|
val devModeOptions: DevModeOptions?
|
||||||
@ -38,12 +37,13 @@ interface NodeConfiguration : NodeSSLConfiguration {
|
|||||||
val useTestClock: Boolean get() = false
|
val useTestClock: Boolean get() = false
|
||||||
val detectPublicIp: Boolean get() = true
|
val detectPublicIp: Boolean get() = true
|
||||||
val sshd: SSHDConfiguration?
|
val sshd: SSHDConfiguration?
|
||||||
|
val database: DatabaseConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
data class DevModeOptions(val disableCheckpointChecker: Boolean = false)
|
data class DevModeOptions(val disableCheckpointChecker: Boolean = false)
|
||||||
|
|
||||||
data class DatabaseConfig(
|
data class DatabaseConfig(
|
||||||
val initDatabase: Boolean = true,
|
val initialiseSchema: Boolean = true,
|
||||||
val serverNameTablePrefix: String = "",
|
val serverNameTablePrefix: String = "",
|
||||||
val transactionIsolationLevel: TransactionIsolationLevel = TransactionIsolationLevel.REPEATABLE_READ
|
val transactionIsolationLevel: TransactionIsolationLevel = TransactionIsolationLevel.REPEATABLE_READ
|
||||||
)
|
)
|
||||||
@ -108,7 +108,6 @@ data class NodeConfigurationImpl(
|
|||||||
override val keyStorePassword: String,
|
override val keyStorePassword: String,
|
||||||
override val trustStorePassword: String,
|
override val trustStorePassword: String,
|
||||||
override val dataSourceProperties: Properties,
|
override val dataSourceProperties: Properties,
|
||||||
override val database: DatabaseConfig = DatabaseConfig(),
|
|
||||||
override val compatibilityZoneURL: URL? = null,
|
override val compatibilityZoneURL: URL? = null,
|
||||||
override val rpcUsers: List<User>,
|
override val rpcUsers: List<User>,
|
||||||
override val verifierType: VerifierType,
|
override val verifierType: VerifierType,
|
||||||
@ -130,9 +129,9 @@ data class NodeConfigurationImpl(
|
|||||||
override val activeMQServer: ActiveMqServerConfiguration,
|
override val activeMQServer: ActiveMqServerConfiguration,
|
||||||
// TODO See TODO above. Rename this to nodeInfoPollingFrequency and make it of type Duration
|
// TODO See TODO above. Rename this to nodeInfoPollingFrequency and make it of type Duration
|
||||||
override val additionalNodeInfoPollingFrequencyMsec: Long = 5.seconds.toMillis(),
|
override val additionalNodeInfoPollingFrequencyMsec: Long = 5.seconds.toMillis(),
|
||||||
override val sshd: SSHDConfiguration? = null
|
override val sshd: SSHDConfiguration? = null,
|
||||||
|
override val database: DatabaseConfig = DatabaseConfig(initialiseSchema = devMode)
|
||||||
) : NodeConfiguration {
|
) : NodeConfiguration {
|
||||||
override val exportJMXto: String get() = "http"
|
override val exportJMXto: String get() = "http"
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
@ -54,7 +54,7 @@ class HibernateConfiguration(val schemaService: SchemaService, private val datab
|
|||||||
// necessarily remain and would likely be replaced by something like Liquibase. For now it is very convenient though.
|
// necessarily remain and would likely be replaced by something like Liquibase. For now it is very convenient though.
|
||||||
// TODO: replace auto schema generation as it isn't intended for production use, according to Hibernate docs.
|
// TODO: replace auto schema generation as it isn't intended for production use, according to Hibernate docs.
|
||||||
val config = Configuration(metadataSources).setProperty("hibernate.connection.provider_class", NodeDatabaseConnectionProvider::class.java.name)
|
val config = Configuration(metadataSources).setProperty("hibernate.connection.provider_class", NodeDatabaseConnectionProvider::class.java.name)
|
||||||
.setProperty("hibernate.hbm2ddl.auto", if (databaseConfig.initDatabase) "update" else "validate")
|
.setProperty("hibernate.hbm2ddl.auto", if (databaseConfig.initialiseSchema) "update" else "validate")
|
||||||
.setProperty("hibernate.format_sql", "true")
|
.setProperty("hibernate.format_sql", "true")
|
||||||
.setProperty("hibernate.connection.isolation", databaseConfig.transactionIsolationLevel.jdbcValue.toString())
|
.setProperty("hibernate.connection.isolation", databaseConfig.transactionIsolationLevel.jdbcValue.toString())
|
||||||
|
|
||||||
|
@ -90,6 +90,7 @@ object VaultSchemaV1 : MappedSchema(schemaFamily = VaultSchema.javaClass, versio
|
|||||||
var externalId: String?,
|
var externalId: String?,
|
||||||
|
|
||||||
@Column(name = "uuid", nullable = false)
|
@Column(name = "uuid", nullable = false)
|
||||||
|
@Type(type = "uuid-char")
|
||||||
var uuid: UUID
|
var uuid: UUID
|
||||||
) : PersistentState() {
|
) : PersistentState() {
|
||||||
constructor(uid: UniqueIdentifier, _participants: List<AbstractParty>) :
|
constructor(uid: UniqueIdentifier, _participants: List<AbstractParty>) :
|
||||||
|
@ -11,7 +11,6 @@ dataSourceProperties = {
|
|||||||
}
|
}
|
||||||
database = {
|
database = {
|
||||||
transactionIsolationLevel = "REPEATABLE_READ"
|
transactionIsolationLevel = "REPEATABLE_READ"
|
||||||
initDatabase = true
|
|
||||||
}
|
}
|
||||||
devMode = true
|
devMode = true
|
||||||
useHTTPS = false
|
useHTTPS = false
|
||||||
|
@ -38,7 +38,6 @@ class NodeConfigurationImplTest {
|
|||||||
keyStorePassword = "cordacadevpass",
|
keyStorePassword = "cordacadevpass",
|
||||||
trustStorePassword = "trustpass",
|
trustStorePassword = "trustpass",
|
||||||
dataSourceProperties = makeTestDataSourceProperties(ALICE.name.organisation),
|
dataSourceProperties = makeTestDataSourceProperties(ALICE.name.organisation),
|
||||||
database = DatabaseConfig(),
|
|
||||||
rpcUsers = emptyList(),
|
rpcUsers = emptyList(),
|
||||||
verifierType = VerifierType.InMemory,
|
verifierType = VerifierType.InMemory,
|
||||||
useHTTPS = false,
|
useHTTPS = false,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user