mirror of
https://github.com/corda/corda.git
synced 2025-01-18 02:39:51 +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
|
||||
----------
|
||||
* 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
|
||||
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:
|
||||
|
||||
: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.
|
||||
:transactionIsolationLevel: Transaction isolation level as defined by the ``TRANSACTION_`` constants in
|
||||
``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``
|
||||
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
|
||||
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
|
||||
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
|
||||
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 {
|
||||
|
@ -79,7 +79,9 @@ class CordappLoader private constructor(private val cordappJarPaths: List<Restri
|
||||
*/
|
||||
@VisibleForTesting
|
||||
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)
|
||||
return cordappLoadersCache.computeIfAbsent(paths, { CordappLoader(paths) })
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ interface NodeConfiguration : NodeSSLConfiguration {
|
||||
val emailAddress: String
|
||||
val exportJMXto: String
|
||||
val dataSourceProperties: Properties
|
||||
val database: DatabaseConfig
|
||||
val rpcUsers: List<User>
|
||||
val devMode: Boolean
|
||||
val devModeOptions: DevModeOptions?
|
||||
@ -38,12 +37,13 @@ interface NodeConfiguration : NodeSSLConfiguration {
|
||||
val useTestClock: Boolean get() = false
|
||||
val detectPublicIp: Boolean get() = true
|
||||
val sshd: SSHDConfiguration?
|
||||
val database: DatabaseConfig
|
||||
}
|
||||
|
||||
data class DevModeOptions(val disableCheckpointChecker: Boolean = false)
|
||||
|
||||
data class DatabaseConfig(
|
||||
val initDatabase: Boolean = true,
|
||||
val initialiseSchema: Boolean = true,
|
||||
val serverNameTablePrefix: String = "",
|
||||
val transactionIsolationLevel: TransactionIsolationLevel = TransactionIsolationLevel.REPEATABLE_READ
|
||||
)
|
||||
@ -108,7 +108,6 @@ data class NodeConfigurationImpl(
|
||||
override val keyStorePassword: String,
|
||||
override val trustStorePassword: String,
|
||||
override val dataSourceProperties: Properties,
|
||||
override val database: DatabaseConfig = DatabaseConfig(),
|
||||
override val compatibilityZoneURL: URL? = null,
|
||||
override val rpcUsers: List<User>,
|
||||
override val verifierType: VerifierType,
|
||||
@ -130,9 +129,9 @@ data class NodeConfigurationImpl(
|
||||
override val activeMQServer: ActiveMqServerConfiguration,
|
||||
// TODO See TODO above. Rename this to nodeInfoPollingFrequency and make it of type Duration
|
||||
override val additionalNodeInfoPollingFrequencyMsec: Long = 5.seconds.toMillis(),
|
||||
override val sshd: SSHDConfiguration? = null
|
||||
|
||||
) : NodeConfiguration {
|
||||
override val sshd: SSHDConfiguration? = null,
|
||||
override val database: DatabaseConfig = DatabaseConfig(initialiseSchema = devMode)
|
||||
) : NodeConfiguration {
|
||||
override val exportJMXto: String get() = "http"
|
||||
|
||||
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.
|
||||
// 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)
|
||||
.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.connection.isolation", databaseConfig.transactionIsolationLevel.jdbcValue.toString())
|
||||
|
||||
|
@ -90,6 +90,7 @@ object VaultSchemaV1 : MappedSchema(schemaFamily = VaultSchema.javaClass, versio
|
||||
var externalId: String?,
|
||||
|
||||
@Column(name = "uuid", nullable = false)
|
||||
@Type(type = "uuid-char")
|
||||
var uuid: UUID
|
||||
) : PersistentState() {
|
||||
constructor(uid: UniqueIdentifier, _participants: List<AbstractParty>) :
|
||||
|
@ -11,7 +11,6 @@ dataSourceProperties = {
|
||||
}
|
||||
database = {
|
||||
transactionIsolationLevel = "REPEATABLE_READ"
|
||||
initDatabase = true
|
||||
}
|
||||
devMode = true
|
||||
useHTTPS = false
|
||||
|
@ -38,7 +38,6 @@ class NodeConfigurationImplTest {
|
||||
keyStorePassword = "cordacadevpass",
|
||||
trustStorePassword = "trustpass",
|
||||
dataSourceProperties = makeTestDataSourceProperties(ALICE.name.organisation),
|
||||
database = DatabaseConfig(),
|
||||
rpcUsers = emptyList(),
|
||||
verifierType = VerifierType.InMemory,
|
||||
useHTTPS = false,
|
||||
|
Loading…
Reference in New Issue
Block a user