From 7620874e5fec99e9c66c3f0c0832448157e45e13 Mon Sep 17 00:00:00 2001 From: szymonsztuka Date: Mon, 31 Jul 2017 16:36:34 +0100 Subject: [PATCH] Szymon db confing (#1141) database transaction isolation level and database initialisation are configurable --- .../corda/contracts/CommercialPaperTests.kt | 5 +-- .../net/corda/contracts/asset/CashTests.kt | 3 +- .../node/utilities/JDBCHashMapTestSuite.kt | 9 +++--- .../net/corda/node/internal/AbstractNode.kt | 10 +++--- .../node/services/config/NodeConfiguration.kt | 2 ++ .../database/HibernateConfiguration.kt | 9 +++--- .../services/database/RequeryConfiguration.kt | 21 ++++++++++-- .../persistence/NodeAttachmentService.kt | 4 +-- .../statemachine/FlowStateMachineImpl.kt | 2 +- .../node/services/vault/NodeVaultService.kt | 26 ++++++++------- .../corda/node/utilities/CordaPersistence.kt | 32 +++++++++++++------ .../utilities/DatabaseTransactionManager.kt | 4 +-- node/src/main/resources/reference.conf | 4 +++ .../services/vault/VaultQueryJavaTests.java | 5 +-- .../corda/node/messaging/AttachmentTests.kt | 3 +- .../config/FullNodeConfigurationTest.kt | 2 ++ .../database/HibernateConfigurationTest.kt | 8 +++-- .../database/RequeryConfigurationTest.kt | 7 ++-- .../events/NodeSchedulerServiceTest.kt | 11 +++---- .../messaging/ArtemisMessagingTests.kt | 4 ++- .../persistence/DBCheckpointStorageTests.kt | 3 +- .../persistence/DBTransactionStorageTests.kt | 3 +- .../persistence/NodeAttachmentStorageTest.kt | 13 ++++---- .../services/schema/HibernateObserverTests.kt | 5 +-- .../DistributedImmutableMapTests.kt | 3 +- .../PersistentUniquenessProviderTests.kt | 3 +- .../services/vault/NodeVaultServiceTest.kt | 5 +-- .../node/services/vault/VaultQueryTests.kt | 7 ++-- .../node/services/vault/VaultWithCashTest.kt | 5 +-- .../corda/node/utilities/ObservablesTests.kt | 3 +- .../corda/irs/api/NodeInterestRatesTest.kt | 3 +- .../kotlin/net/corda/testing/CoreTestUtils.kt | 2 ++ .../net/corda/testing/node/MockServices.kt | 10 ++++-- .../net/corda/testing/node/SimpleNode.kt | 2 +- 34 files changed, 153 insertions(+), 85 deletions(-) diff --git a/finance/src/test/kotlin/net/corda/contracts/CommercialPaperTests.kt b/finance/src/test/kotlin/net/corda/contracts/CommercialPaperTests.kt index 06e9c0ee4e..eb53ce9305 100644 --- a/finance/src/test/kotlin/net/corda/contracts/CommercialPaperTests.kt +++ b/finance/src/test/kotlin/net/corda/contracts/CommercialPaperTests.kt @@ -15,6 +15,7 @@ import net.corda.node.utilities.configureDatabase import net.corda.testing.* import net.corda.testing.node.MockServices import net.corda.testing.node.makeTestDataSourceProperties +import net.corda.testing.node.makeTestDatabaseProperties import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized @@ -212,7 +213,7 @@ class CommercialPaperTestsGeneric { fun `issue move and then redeem`() { initialiseTestSerialization() val dataSourcePropsAlice = makeTestDataSourceProperties() - val databaseAlice = configureDatabase(dataSourcePropsAlice) + val databaseAlice = configureDatabase(dataSourcePropsAlice, makeTestDatabaseProperties()) databaseAlice.transaction { aliceServices = object : MockServices(ALICE_KEY) { @@ -231,7 +232,7 @@ class CommercialPaperTestsGeneric { } val dataSourcePropsBigCorp = makeTestDataSourceProperties() - val databaseBigCorp = configureDatabase(dataSourcePropsBigCorp) + val databaseBigCorp = configureDatabase(dataSourcePropsBigCorp, makeTestDatabaseProperties()) databaseBigCorp.transaction { bigCorpServices = object : MockServices(BIG_CORP_KEY) { diff --git a/finance/src/test/kotlin/net/corda/contracts/asset/CashTests.kt b/finance/src/test/kotlin/net/corda/contracts/asset/CashTests.kt index 105a9f5cca..c2cc2904cc 100644 --- a/finance/src/test/kotlin/net/corda/contracts/asset/CashTests.kt +++ b/finance/src/test/kotlin/net/corda/contracts/asset/CashTests.kt @@ -21,6 +21,7 @@ import net.corda.testing.contracts.fillWithSomeTestCash import net.corda.testing.node.MockKeyManagementService import net.corda.testing.node.MockServices import net.corda.testing.node.makeTestDataSourceProperties +import net.corda.testing.node.makeTestDatabaseProperties import org.junit.Before import org.junit.Test import java.security.KeyPair @@ -51,7 +52,7 @@ class CashTests : TestDependencyInjectionBase() { fun setUp() { LogHelper.setLevel(NodeVaultService::class) val dataSourceProps = makeTestDataSourceProperties() - database = configureDatabase(dataSourceProps) + database = configureDatabase(dataSourceProps, makeTestDatabaseProperties()) database.transaction { miniCorpServices = object : MockServices(MINI_CORP_KEY) { override val keyManagementService: MockKeyManagementService = MockKeyManagementService(identityService, MINI_CORP_KEY, MEGA_CORP_KEY, OUR_KEY) diff --git a/node/src/integration-test/kotlin/net/corda/node/utilities/JDBCHashMapTestSuite.kt b/node/src/integration-test/kotlin/net/corda/node/utilities/JDBCHashMapTestSuite.kt index 029eb68cff..a66304388e 100644 --- a/node/src/integration-test/kotlin/net/corda/node/utilities/JDBCHashMapTestSuite.kt +++ b/node/src/integration-test/kotlin/net/corda/node/utilities/JDBCHashMapTestSuite.kt @@ -13,6 +13,7 @@ import junit.framework.TestSuite import net.corda.testing.TestDependencyInjectionBase import net.corda.testing.initialiseTestSerialization import net.corda.testing.node.makeTestDataSourceProperties +import net.corda.testing.node.makeTestDatabaseProperties import net.corda.testing.resetTestSerialization import org.assertj.core.api.Assertions.assertThat import org.jetbrains.exposed.sql.Transaction @@ -33,7 +34,7 @@ import java.util.* JDBCHashMapTestSuite.SetConstrained::class) class JDBCHashMapTestSuite { companion object { - lateinit var transaction: Transaction + lateinit var transaction: DatabaseTransaction lateinit var database: CordaPersistence lateinit var loadOnInitFalseMap: JDBCHashMap lateinit var memoryConstrainedMap: JDBCHashMap @@ -46,7 +47,7 @@ class JDBCHashMapTestSuite { @BeforeClass fun before() { initialiseTestSerialization() - database = configureDatabase(makeTestDataSourceProperties()) + database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties()) setUpDatabaseTx() loadOnInitFalseMap = JDBCHashMap("test_map_false", loadOnInit = false) memoryConstrainedMap = JDBCHashMap("test_map_constrained", loadOnInit = false, maxBuckets = 1) @@ -105,7 +106,7 @@ class JDBCHashMapTestSuite { .createTestSuite() private fun setUpDatabaseTx() { - transaction = TransactionManager.currentOrNew(Connection.TRANSACTION_REPEATABLE_READ) + transaction = DatabaseTransactionManager.currentOrNew() } private fun closeDatabaseTx() { @@ -232,7 +233,7 @@ class JDBCHashMapTestSuite { @Before fun before() { - database = configureDatabase(makeTestDataSourceProperties()) + database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties()) } @After diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 6f38b9d1e2..21f9952e41 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -485,7 +485,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, private fun makeVaultObservers() { VaultSoftLockManager(services.vaultService, smm) ScheduledActivityObserver(services) - HibernateObserver(services.vaultService.rawUpdates, HibernateConfiguration(services.schemaService)) + HibernateObserver(services.vaultService.rawUpdates, HibernateConfiguration(services.schemaService, configuration.database ?: Properties())) } private fun makeInfo(): NodeInfo { @@ -544,7 +544,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, protected open fun initialiseDatabasePersistence(insideTransaction: () -> Unit) { val props = configuration.dataSourceProperties if (props.isNotEmpty()) { - this.database = configureDatabase(props) + this.database = configureDatabase(props, configuration.database) // Now log the vendor string as this will also cause a connection to be tested eagerly. database.transaction { log.info("Connected to ${database.database.vendor} database.") @@ -764,7 +764,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, private fun createAttachmentStorage(): NodeAttachmentService { val attachmentsDir = (configuration.baseDirectory / "attachments").createDirectories() - return NodeAttachmentService(attachmentsDir, configuration.dataSourceProperties, services.monitoringService.metrics) + return NodeAttachmentService(attachmentsDir, configuration.dataSourceProperties, services.monitoringService.metrics, configuration.database) } private inner class ServiceHubInternalImpl : ServiceHubInternal, SingletonSerializeAsToken() { @@ -776,9 +776,9 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, override val validatedTransactions = makeTransactionStorage() override val transactionVerifierService by lazy { makeTransactionVerifierService() } override val networkMapCache by lazy { InMemoryNetworkMapCache(this) } - override val vaultService by lazy { NodeVaultService(this, configuration.dataSourceProperties) } + override val vaultService by lazy { NodeVaultService(this, configuration.dataSourceProperties, configuration.database) } override val vaultQueryService by lazy { - HibernateVaultQueryImpl(HibernateConfiguration(schemaService), vaultService.updatesPublisher) + HibernateVaultQueryImpl(HibernateConfiguration(schemaService, configuration.database ?: Properties()), vaultService.updatesPublisher) } // Place the long term identity key in the KMS. Eventually, this is likely going to be separated again because // the KMS is meant for derived temporary keys used in transactions, and we're not supposed to sign things with diff --git a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt index 615df372c4..99f4159cbc 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt @@ -25,6 +25,7 @@ interface NodeConfiguration : NodeSSLConfiguration { val emailAddress: String val exportJMXto: String val dataSourceProperties: Properties + val database: Properties? val rpcUsers: List val devMode: Boolean val certificateSigningService: URL @@ -47,6 +48,7 @@ data class FullNodeConfiguration( override val keyStorePassword: String, override val trustStorePassword: String, override val dataSourceProperties: Properties, + override val database: Properties?, override val certificateSigningService: URL, override val networkMapService: NetworkMapInfo?, override val minimumPlatformVersion: Int = 1, diff --git a/node/src/main/kotlin/net/corda/node/services/database/HibernateConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/database/HibernateConfiguration.kt index 00d72cd0b4..e14b3c2ea7 100644 --- a/node/src/main/kotlin/net/corda/node/services/database/HibernateConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/database/HibernateConfiguration.kt @@ -15,10 +15,11 @@ import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment import org.hibernate.service.UnknownUnwrapTypeException import java.sql.Connection +import java.util.* import java.util.concurrent.ConcurrentHashMap -class HibernateConfiguration(val schemaService: SchemaService, val useDefaultLogging: Boolean = false) { - constructor(schemaService: SchemaService) : this(schemaService, false) +class HibernateConfiguration(val schemaService: SchemaService, val useDefaultLogging: Boolean = false, val databaseProperties: Properties) { + constructor(schemaService: SchemaService, databaseProperties: Properties) : this(schemaService, false, databaseProperties) companion object { val logger = loggerFor() @@ -57,7 +58,7 @@ class HibernateConfiguration(val schemaService: SchemaService, val useDefaultLog // 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", HibernateConfiguration.NodeDatabaseConnectionProvider::class.java.name) - .setProperty("hibernate.hbm2ddl.auto", "update") + .setProperty("hibernate.hbm2ddl.auto", if (databaseProperties.getProperty("initDatabase","true") == "true") "update" else "validate") .setProperty("hibernate.show_sql", "$useDefaultLogging") .setProperty("hibernate.format_sql", "$useDefaultLogging") schemas.forEach { schema -> @@ -102,7 +103,7 @@ class HibernateConfiguration(val schemaService: SchemaService, val useDefaultLog override fun supportsAggressiveRelease(): Boolean = true override fun getConnection(): Connection { - return DatabaseTransactionManager.newTransaction(Connection.TRANSACTION_REPEATABLE_READ).connection + return DatabaseTransactionManager.newTransaction().connection } override fun unwrap(unwrapType: Class): T { diff --git a/node/src/main/kotlin/net/corda/node/services/database/RequeryConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/database/RequeryConfiguration.kt index 49d0893abf..8e2cc192a0 100644 --- a/node/src/main/kotlin/net/corda/node/services/database/RequeryConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/database/RequeryConfiguration.kt @@ -3,6 +3,7 @@ package net.corda.node.services.database import com.zaxxer.hikari.HikariConfig import com.zaxxer.hikari.HikariDataSource import io.requery.Persistable +import io.requery.TransactionIsolation import io.requery.meta.EntityModel import io.requery.sql.KotlinEntityDataStore import io.requery.sql.SchemaModifier @@ -13,7 +14,7 @@ import java.sql.Connection import java.util.* import java.util.concurrent.ConcurrentHashMap -class RequeryConfiguration(val properties: Properties, val useDefaultLogging: Boolean = false) { +class RequeryConfiguration(val properties: Properties, val useDefaultLogging: Boolean = false, val databaseProperties: Properties) { companion object { val logger = loggerFor() @@ -40,11 +41,25 @@ class RequeryConfiguration(val properties: Properties, val useDefaultLogging: Bo fun makeSessionFactoryForModel(model: EntityModel): KotlinEntityDataStore { val configuration = KotlinConfigurationTransactionWrapper(model, dataSource, useDefaultLogging = this.useDefaultLogging) val tables = SchemaModifier(configuration) - val mode = TableCreationMode.CREATE_NOT_EXISTS - tables.createTables(mode) + if (databaseProperties.getProperty("initDatabase","true") == "true" ) { + val mode = TableCreationMode.CREATE_NOT_EXISTS + tables.createTables(mode) + } return KotlinEntityDataStore(configuration) } // TODO: remove once Requery supports QUERY WITH COMPOSITE_KEY IN fun jdbcSession(): Connection = DatabaseTransactionManager.current().connection } + +fun parserTransactionIsolationLevel(property: String?) : TransactionIsolation = + when (property) { + "none" -> TransactionIsolation.NONE + "readUncommitted" -> TransactionIsolation.READ_UNCOMMITTED + "readCommitted" -> TransactionIsolation.READ_COMMITTED + "repeatableRead" -> TransactionIsolation.REPEATABLE_READ + "serializable" -> TransactionIsolation.SERIALIZABLE + else -> { + TransactionIsolation.REPEATABLE_READ + } + } diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/NodeAttachmentService.kt b/node/src/main/kotlin/net/corda/node/services/persistence/NodeAttachmentService.kt index 9021f45af6..5d6672a32b 100644 --- a/node/src/main/kotlin/net/corda/node/services/persistence/NodeAttachmentService.kt +++ b/node/src/main/kotlin/net/corda/node/services/persistence/NodeAttachmentService.kt @@ -32,13 +32,13 @@ import javax.annotation.concurrent.ThreadSafe * Stores attachments in H2 database. */ @ThreadSafe -class NodeAttachmentService(val storePath: Path, dataSourceProperties: Properties, metrics: MetricRegistry) +class NodeAttachmentService(val storePath: Path, dataSourceProperties: Properties, metrics: MetricRegistry, databaseProperties: Properties?) : AttachmentStorage, AcceptsFileUpload, SingletonSerializeAsToken() { companion object { private val log = loggerFor() } - val configuration = RequeryConfiguration(dataSourceProperties) + val configuration = RequeryConfiguration(dataSourceProperties, databaseProperties = databaseProperties ?: Properties()) val session = configuration.sessionForModel(Models.PERSISTENCE) @VisibleForTesting diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt index 80db78a8bd..c7361d879c 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt @@ -57,7 +57,7 @@ class FlowStateMachineImpl(override val id: StateMachineRunId, DatabaseTransactionManager.current().close() Strand.sleep(millis) DatabaseTransactionManager.dataSource = db - DatabaseTransactionManager.newTransaction(Connection.TRANSACTION_REPEATABLE_READ) + DatabaseTransactionManager.newTransaction() } else Strand.sleep(millis) } } diff --git a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt index 38ce105bd4..58aa05035f 100644 --- a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt +++ b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt @@ -34,6 +34,7 @@ import net.corda.core.serialization.serialize import net.corda.core.transactions.* import net.corda.core.utilities.* import net.corda.node.services.database.RequeryConfiguration +import net.corda.node.services.database.parserTransactionIsolationLevel import net.corda.node.services.statemachine.FlowStateMachineImpl import net.corda.node.services.vault.schemas.requery.Models import net.corda.node.services.vault.schemas.requery.VaultSchema @@ -60,7 +61,7 @@ import kotlin.concurrent.withLock * TODO: keep an audit trail with time stamps of previously unconsumed states "as of" a particular point in time. * TODO: have transaction storage do some caching. */ -class NodeVaultService(private val services: ServiceHub, dataSourceProperties: Properties) : SingletonSerializeAsToken(), VaultService { +class NodeVaultService(private val services: ServiceHub, dataSourceProperties: Properties, databaseProperties: Properties?) : SingletonSerializeAsToken(), VaultService { private companion object { val log = loggerFor() @@ -69,8 +70,9 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P val stateRefCompositeColumn: RowExpression = RowExpression.of(listOf(VaultStatesEntity.TX_ID, VaultStatesEntity.INDEX)) } - val configuration = RequeryConfiguration(dataSourceProperties) + val configuration = RequeryConfiguration(dataSourceProperties, databaseProperties = databaseProperties ?: Properties()) val session = configuration.sessionForModel(Models.VAULT) + private val transactionIsolationLevel = parserTransactionIsolationLevel(databaseProperties?.getProperty("transactionIsolationLevel") ?:"") private class InnerState { val _updatesPublisher = PublishSubject.create>()!! @@ -89,7 +91,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P val consumedStateRefs = update.consumed.map { it.ref } log.trace { "Removing $consumedStateRefs consumed contract states and adding $producedStateRefs produced contract states to the database." } - session.withTransaction(TransactionIsolation.REPEATABLE_READ) { + session.withTransaction(transactionIsolationLevel) { producedStateRefsMap.forEach { it -> val state = VaultStatesEntity().apply { txId = it.key.txhash.toString() @@ -142,7 +144,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P override fun states(clazzes: Set>, statuses: EnumSet, includeSoftLockedStates: Boolean): Iterable> { val stateAndRefs = - session.withTransaction(TransactionIsolation.REPEATABLE_READ) { + session.withTransaction(transactionIsolationLevel) { val query = select(VaultSchema.VaultStates::class) .where(VaultSchema.VaultStates::stateStatus `in` statuses) // TODO: temporary fix to continue supporting track() function (until becomes Typed) @@ -164,7 +166,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P override fun statesForRefs(refs: List): Map?> { val stateAndRefs = - session.withTransaction(TransactionIsolation.REPEATABLE_READ) { + session.withTransaction(transactionIsolationLevel) { var results: List> = emptyList() refs.forEach { val result = select(VaultSchema.VaultStates::class) @@ -273,7 +275,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P private fun loadStates(refs: Collection): HashSet> { val states = HashSet>() if (refs.isNotEmpty()) { - session.withTransaction(TransactionIsolation.REPEATABLE_READ) { + session.withTransaction(transactionIsolationLevel) { val result = select(VaultStatesEntity::class). where(stateRefCompositeColumn.`in`(stateRefArgs(refs))). and(VaultSchema.VaultStates::stateStatus eq Vault.StateStatus.UNCONSUMED) @@ -301,7 +303,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P } override fun addNoteToTransaction(txnId: SecureHash, noteText: String) { - session.withTransaction(TransactionIsolation.REPEATABLE_READ) { + session.withTransaction(transactionIsolationLevel) { val txnNoteEntity = VaultTxnNoteEntity() txnNoteEntity.txId = txnId.toString() txnNoteEntity.note = noteText @@ -310,7 +312,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P } override fun getTransactionNotes(txnId: SecureHash): Iterable { - return session.withTransaction(TransactionIsolation.REPEATABLE_READ) { + return session.withTransaction(transactionIsolationLevel) { (select(VaultSchema.VaultTxnNote::class) where (VaultSchema.VaultTxnNote::txId eq txnId.toString())).get().asIterable().map { it.note } } } @@ -320,7 +322,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P val softLockTimestamp = services.clock.instant() val stateRefArgs = stateRefArgs(stateRefs) try { - session.withTransaction(TransactionIsolation.REPEATABLE_READ) { + session.withTransaction(transactionIsolationLevel) { val updatedRows = update(VaultStatesEntity::class) .set(VaultStatesEntity.LOCK_ID, lockId.toString()) .set(VaultStatesEntity.LOCK_UPDATE_TIME, softLockTimestamp) @@ -353,7 +355,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P override fun softLockRelease(lockId: UUID, stateRefs: NonEmptySet?) { if (stateRefs == null) { - session.withTransaction(TransactionIsolation.REPEATABLE_READ) { + session.withTransaction(transactionIsolationLevel) { val update = update(VaultStatesEntity::class) .set(VaultStatesEntity.LOCK_ID, null) .set(VaultStatesEntity.LOCK_UPDATE_TIME, services.clock.instant()) @@ -365,7 +367,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P } } else { try { - session.withTransaction(TransactionIsolation.REPEATABLE_READ) { + session.withTransaction(transactionIsolationLevel) { val updatedRows = update(VaultStatesEntity::class) .set(VaultStatesEntity.LOCK_ID, null) .set(VaultStatesEntity.LOCK_UPDATE_TIME, services.clock.instant()) @@ -482,7 +484,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P override fun softLockedStates(lockId: UUID?): List> { val stateAndRefs = - session.withTransaction(TransactionIsolation.REPEATABLE_READ) { + session.withTransaction(transactionIsolationLevel) { val query = select(VaultSchema.VaultStates::class) .where(VaultSchema.VaultStates::stateStatus eq Vault.StateStatus.UNCONSUMED) .and(VaultSchema.VaultStates::contractStateClassName eq Cash.State::class.java.name) diff --git a/node/src/main/kotlin/net/corda/node/utilities/CordaPersistence.kt b/node/src/main/kotlin/net/corda/node/utilities/CordaPersistence.kt index c45b1b9fc2..9f65aa345a 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/CordaPersistence.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/CordaPersistence.kt @@ -14,15 +14,16 @@ import java.util.* import java.util.concurrent.CopyOnWriteArrayList -//HikariDataSource implements also Closeable which allows CordaPersistence to be Closeable -class CordaPersistence(var dataSource: HikariDataSource): Closeable { +//HikariDataSource implements Closeable which allows CordaPersistence to be Closeable +class CordaPersistence(var dataSource: HikariDataSource, databaseProperties: Properties): Closeable { /** Holds Exposed database, the field will be removed once Exposed library is removed */ lateinit var database: Database + var transactionIsolationLevel = parserTransactionIsolationLevel(databaseProperties.getProperty("transactionIsolationLevel")) companion object { - fun connect(dataSource: HikariDataSource): CordaPersistence { - return CordaPersistence(dataSource).apply { + fun connect(dataSource: HikariDataSource, databaseProperties: Properties): CordaPersistence { + return CordaPersistence(dataSource, databaseProperties).apply { DatabaseTransactionManager(this) } } @@ -31,7 +32,7 @@ class CordaPersistence(var dataSource: HikariDataSource): Closeable { fun createTransaction(): DatabaseTransaction { // We need to set the database for the current [Thread] or [Fiber] here as some tests share threads across databases. DatabaseTransactionManager.dataSource = this - return DatabaseTransactionManager.currentOrNew(Connection.TRANSACTION_REPEATABLE_READ) + return DatabaseTransactionManager.currentOrNew(transactionIsolationLevel) } fun isolatedTransaction(block: DatabaseTransaction.() -> T): T { @@ -45,7 +46,7 @@ class CordaPersistence(var dataSource: HikariDataSource): Closeable { fun transaction(statement: DatabaseTransaction.() -> T): T { DatabaseTransactionManager.dataSource = this - return transaction(Connection.TRANSACTION_REPEATABLE_READ, 3, statement) + return transaction(transactionIsolationLevel, 3, statement) } private fun transaction(transactionIsolation: Int, repetitionAttempts: Int, statement: DatabaseTransaction.() -> T): T { @@ -90,10 +91,10 @@ class CordaPersistence(var dataSource: HikariDataSource): Closeable { } } -fun configureDatabase(props: Properties): CordaPersistence { - val config = HikariConfig(props) +fun configureDatabase(dataSourceProperties: Properties, databaseProperties: Properties?): CordaPersistence { + val config = HikariConfig(dataSourceProperties) val dataSource = HikariDataSource(config) - val persistence = CordaPersistence.connect(dataSource) + val persistence = CordaPersistence.connect(dataSource, databaseProperties ?: Properties()) //org.jetbrains.exposed.sql.Database will be removed once Exposed library is removed val database = Database.connect(dataSource) { _ -> ExposedTransactionManager() } @@ -191,3 +192,16 @@ fun rx.Observable.wrapWithDatabaseTransaction(db: CordaPersistence? } } } + + +fun parserTransactionIsolationLevel(property: String?) : Int = + when (property) { + "none" -> Connection.TRANSACTION_NONE + "readUncommitted" -> Connection.TRANSACTION_READ_UNCOMMITTED + "readCommitted" -> Connection.TRANSACTION_READ_COMMITTED + "repeatableRead" -> Connection.TRANSACTION_REPEATABLE_READ + "serializable" -> Connection.TRANSACTION_SERIALIZABLE + else -> { + Connection.TRANSACTION_REPEATABLE_READ + } + } diff --git a/node/src/main/kotlin/net/corda/node/utilities/DatabaseTransactionManager.kt b/node/src/main/kotlin/net/corda/node/utilities/DatabaseTransactionManager.kt index 79a47a534c..c530f956a3 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/DatabaseTransactionManager.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/DatabaseTransactionManager.kt @@ -74,11 +74,11 @@ class DatabaseTransactionManager(initDataSource: CordaPersistence) { fun currentOrNull(): DatabaseTransaction? = manager.currentOrNull() - fun currentOrNew(isolation: Int) = currentOrNull() ?: manager.newTransaction(isolation) + fun currentOrNew(isolation: Int = dataSource.transactionIsolationLevel) = currentOrNull() ?: manager.newTransaction(isolation) fun current(): DatabaseTransaction = currentOrNull() ?: error("No transaction in context.") - fun newTransaction(isolation: Int) = manager.newTransaction(isolation) + fun newTransaction(isolation: Int = dataSource.transactionIsolationLevel) = manager.newTransaction(isolation) } data class Boundary(val txId: UUID) diff --git a/node/src/main/resources/reference.conf b/node/src/main/resources/reference.conf index de5d36ac05..11e2269fce 100644 --- a/node/src/main/resources/reference.conf +++ b/node/src/main/resources/reference.conf @@ -9,6 +9,10 @@ dataSourceProperties = { "dataSource.user" = sa "dataSource.password" = "" } +database = { + transactionIsolationLevel = "repeatableRead" + initDatabase = true +} devMode = true certificateSigningService = "https://cordaci-netperm.corda.r3cev.com" useHTTPS = false diff --git a/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java b/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java index 1e72e17900..6603736f30 100644 --- a/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java +++ b/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java @@ -52,6 +52,7 @@ import static net.corda.core.utilities.ByteArrays.toHexString; import static net.corda.node.utilities.CordaPersistenceKt.configureDatabase; import static net.corda.testing.CoreTestUtils.*; import static net.corda.testing.node.MockServicesKt.makeTestDataSourceProperties; +import static net.corda.testing.node.MockServicesKt.makeTestDatabaseProperties; import static org.assertj.core.api.Assertions.assertThat; public class VaultQueryJavaTests extends TestDependencyInjectionBase { @@ -64,10 +65,10 @@ public class VaultQueryJavaTests extends TestDependencyInjectionBase { @Before public void setUp() { Properties dataSourceProps = makeTestDataSourceProperties(SecureHash.randomSHA256().toString()); - database = configureDatabase(dataSourceProps); + database = configureDatabase(dataSourceProps, makeTestDatabaseProperties()); Set customSchemas = new HashSet<>(Collections.singletonList(DummyLinearStateSchemaV1.INSTANCE)); - HibernateConfiguration hibernateConfig = new HibernateConfiguration(new NodeSchemaService(customSchemas)); + HibernateConfiguration hibernateConfig = new HibernateConfiguration(new NodeSchemaService(customSchemas), makeTestDatabaseProperties()); database.transaction( statement -> { services = new MockServices(getMEGA_CORP_KEY()) { @NotNull diff --git a/node/src/test/kotlin/net/corda/node/messaging/AttachmentTests.kt b/node/src/test/kotlin/net/corda/node/messaging/AttachmentTests.kt index 782b06c205..ec55bba2eb 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/AttachmentTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/AttachmentTests.kt @@ -15,6 +15,7 @@ import net.corda.node.services.persistence.schemas.requery.AttachmentEntity import net.corda.node.services.transactions.SimpleNotaryService import net.corda.testing.node.MockNetwork import net.corda.testing.node.makeTestDataSourceProperties +import net.corda.testing.node.makeTestDatabaseProperties import org.junit.After import org.junit.Before import org.junit.Test @@ -35,7 +36,7 @@ class AttachmentTests { fun setUp() { mockNet = MockNetwork() val dataSourceProperties = makeTestDataSourceProperties() - configuration = RequeryConfiguration(dataSourceProperties) + configuration = RequeryConfiguration(dataSourceProperties, databaseProperties = makeTestDatabaseProperties()) } @After diff --git a/node/src/test/kotlin/net/corda/node/services/config/FullNodeConfigurationTest.kt b/node/src/test/kotlin/net/corda/node/services/config/FullNodeConfigurationTest.kt index 7bc9c3246d..8da05d3899 100644 --- a/node/src/test/kotlin/net/corda/node/services/config/FullNodeConfigurationTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/config/FullNodeConfigurationTest.kt @@ -5,6 +5,7 @@ import net.corda.core.utilities.NetworkHostAndPort import net.corda.testing.ALICE import net.corda.nodeapi.User import net.corda.testing.node.makeTestDataSourceProperties +import net.corda.testing.node.makeTestDatabaseProperties import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Test import java.net.URL @@ -21,6 +22,7 @@ class FullNodeConfigurationTest { keyStorePassword = "cordacadevpass", trustStorePassword = "trustpass", dataSourceProperties = makeTestDataSourceProperties(ALICE.name.commonName), + database = makeTestDatabaseProperties(), certificateSigningService = URL("http://localhost"), rpcUsers = emptyList(), verifierType = VerifierType.InMemory, diff --git a/node/src/test/kotlin/net/corda/node/services/database/HibernateConfigurationTest.kt b/node/src/test/kotlin/net/corda/node/services/database/HibernateConfigurationTest.kt index fe17999d5c..1622a08a30 100644 --- a/node/src/test/kotlin/net/corda/node/services/database/HibernateConfigurationTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/database/HibernateConfigurationTest.kt @@ -27,6 +27,7 @@ import net.corda.testing.contracts.fillWithSomeTestDeals import net.corda.testing.contracts.fillWithSomeTestLinearStates import net.corda.testing.node.MockServices import net.corda.testing.node.makeTestDataSourceProperties +import net.corda.testing.node.makeTestDatabaseProperties import net.corda.testing.schemas.DummyLinearStateSchemaV1 import net.corda.testing.schemas.DummyLinearStateSchemaV2 import org.assertj.core.api.Assertions @@ -60,16 +61,17 @@ class HibernateConfigurationTest : TestDependencyInjectionBase() { @Before fun setUp() { val dataSourceProps = makeTestDataSourceProperties() - database = configureDatabase(dataSourceProps) + val defaultDatabaseProperties = makeTestDatabaseProperties() + database = configureDatabase(dataSourceProps, defaultDatabaseProperties) val customSchemas = setOf(VaultSchemaV1, CashSchemaV1, SampleCashSchemaV2, SampleCashSchemaV3) database.transaction { - hibernateConfig = HibernateConfiguration(NodeSchemaService(customSchemas)) + hibernateConfig = HibernateConfiguration(NodeSchemaService(customSchemas), makeTestDatabaseProperties()) services = object : MockServices(BOB_KEY) { override val vaultService: VaultService get() { - val vaultService = NodeVaultService(this, dataSourceProps) + val vaultService = NodeVaultService(this, dataSourceProps, makeTestDatabaseProperties()) hibernatePersister = HibernateObserver(vaultService.rawUpdates, hibernateConfig) return vaultService } diff --git a/node/src/test/kotlin/net/corda/node/services/database/RequeryConfigurationTest.kt b/node/src/test/kotlin/net/corda/node/services/database/RequeryConfigurationTest.kt index 2e93ab22c3..3b36e4bb07 100644 --- a/node/src/test/kotlin/net/corda/node/services/database/RequeryConfigurationTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/database/RequeryConfigurationTest.kt @@ -25,6 +25,7 @@ import net.corda.testing.DUMMY_PUBKEY_1 import net.corda.testing.TestDependencyInjectionBase import net.corda.testing.contracts.DummyContract import net.corda.testing.node.makeTestDataSourceProperties +import net.corda.testing.node.makeTestDatabaseProperties import org.assertj.core.api.Assertions import org.junit.After import org.junit.Assert.assertEquals @@ -43,7 +44,7 @@ class RequeryConfigurationTest : TestDependencyInjectionBase() { @Before fun setUp() { val dataSourceProperties = makeTestDataSourceProperties() - database = configureDatabase(dataSourceProperties) + database = configureDatabase(dataSourceProperties, makeTestDatabaseProperties()) newTransactionStorage() newRequeryStorage(dataSourceProperties) } @@ -157,7 +158,7 @@ class RequeryConfigurationTest : TestDependencyInjectionBase() { val nativeQuery = "SELECT v.transaction_id, v.output_index FROM vault_states v WHERE v.state_status = 0" database.transaction { - val configuration = RequeryConfiguration(dataSourceProperties, true) + val configuration = RequeryConfiguration(dataSourceProperties, true, makeTestDatabaseProperties()) val jdbcSession = configuration.jdbcSession() val prepStatement = jdbcSession.prepareStatement(nativeQuery) val rs = prepStatement.executeQuery() @@ -197,7 +198,7 @@ class RequeryConfigurationTest : TestDependencyInjectionBase() { private fun newRequeryStorage(dataSourceProperties: Properties) { database.transaction { - val configuration = RequeryConfiguration(dataSourceProperties, true) + val configuration = RequeryConfiguration(dataSourceProperties, true, makeTestDatabaseProperties()) requerySession = configuration.sessionForModel(Models.VAULT) } } diff --git a/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt index d876665071..413721a695 100644 --- a/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt @@ -21,12 +21,10 @@ import net.corda.node.services.statemachine.StateMachineManager import net.corda.node.services.vault.NodeVaultService import net.corda.node.utilities.* import net.corda.testing.getTestX509Name -import net.corda.testing.node.InMemoryMessagingNetwork -import net.corda.testing.node.MockKeyManagementService -import net.corda.testing.node.TestClock -import net.corda.testing.node.makeTestDataSourceProperties import net.corda.testing.testNodeConfiguration import net.corda.testing.initialiseTestSerialization +import net.corda.testing.node.* +import net.corda.testing.node.TestClock import net.corda.testing.resetTestSerialization import org.assertj.core.api.Assertions.assertThat import org.bouncycastle.asn1.x500.X500Name @@ -37,6 +35,7 @@ import java.nio.file.Paths import java.security.PublicKey import java.time.Clock import java.time.Instant +import java.util.* import java.util.concurrent.CountDownLatch import java.util.concurrent.Executors import java.util.concurrent.TimeUnit @@ -75,7 +74,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() { smmHasRemovedAllFlows = CountDownLatch(1) calls = 0 val dataSourceProps = makeTestDataSourceProperties() - database = configureDatabase(dataSourceProps) + database = configureDatabase(dataSourceProps, makeTestDatabaseProperties()) val identityService = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate) val kms = MockKeyManagementService(identityService, ALICE_KEY) @@ -92,7 +91,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() { overrideClock = testClock, keyManagement = kms, network = mockMessagingService), TestReference { - override val vaultService: VaultService = NodeVaultService(this, dataSourceProps) + override val vaultService: VaultService = NodeVaultService(this, dataSourceProps, makeTestDatabaseProperties()) override val testReference = this@NodeSchedulerServiceTest } scheduler = NodeSchedulerService(services, schedulerGatedExecutor) diff --git a/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt b/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt index f9cd07f578..a57893a34d 100644 --- a/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt @@ -22,6 +22,7 @@ import net.corda.node.utilities.configureDatabase import net.corda.testing.* import net.corda.testing.node.MOCK_VERSION_INFO import net.corda.testing.node.makeTestDataSourceProperties +import net.corda.testing.node.makeTestDatabaseProperties import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.After @@ -30,6 +31,7 @@ import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder import java.net.ServerSocket +import java.util.* import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.TimeUnit.MILLISECONDS import kotlin.concurrent.thread @@ -68,7 +70,7 @@ class ArtemisMessagingTests : TestDependencyInjectionBase() { baseDirectory = baseDirectory, myLegalName = ALICE.name) LogHelper.setLevel(PersistentUniquenessProvider::class) - database = configureDatabase(makeTestDataSourceProperties()) + database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties()) networkMapRegistrationFuture = Futures.immediateFuture(Unit) } diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/DBCheckpointStorageTests.kt b/node/src/test/kotlin/net/corda/node/services/persistence/DBCheckpointStorageTests.kt index b303a8b425..11e99901d8 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/DBCheckpointStorageTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/DBCheckpointStorageTests.kt @@ -10,6 +10,7 @@ import net.corda.node.utilities.configureDatabase import net.corda.testing.LogHelper import net.corda.testing.TestDependencyInjectionBase import net.corda.testing.node.makeTestDataSourceProperties +import net.corda.testing.node.makeTestDatabaseProperties import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatExceptionOfType import org.junit.After @@ -32,7 +33,7 @@ class DBCheckpointStorageTests : TestDependencyInjectionBase() { @Before fun setUp() { LogHelper.setLevel(PersistentUniquenessProvider::class) - database = configureDatabase(makeTestDataSourceProperties()) + database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties()) newCheckpointStorage() } diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt b/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt index d62b9af52c..1f5203195e 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt @@ -14,6 +14,7 @@ import net.corda.testing.DUMMY_NOTARY import net.corda.testing.LogHelper import net.corda.testing.TestDependencyInjectionBase import net.corda.testing.node.makeTestDataSourceProperties +import net.corda.testing.node.makeTestDatabaseProperties import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Before @@ -28,7 +29,7 @@ class DBTransactionStorageTests : TestDependencyInjectionBase() { @Before fun setUp() { LogHelper.setLevel(PersistentUniquenessProvider::class) - database = configureDatabase(makeTestDataSourceProperties()) + database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties()) newTransactionStorage() } diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentStorageTest.kt b/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentStorageTest.kt index c24f89cd2d..f1d5a529e1 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentStorageTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentStorageTest.kt @@ -16,6 +16,7 @@ import net.corda.node.services.transactions.PersistentUniquenessProvider import net.corda.node.utilities.CordaPersistence import net.corda.node.utilities.configureDatabase import net.corda.testing.node.makeTestDataSourceProperties +import net.corda.testing.node.makeTestDatabaseProperties import org.junit.After import org.junit.Before import org.junit.Test @@ -42,9 +43,9 @@ class NodeAttachmentStorageTest { LogHelper.setLevel(PersistentUniquenessProvider::class) dataSourceProperties = makeTestDataSourceProperties() - database = configureDatabase(dataSourceProperties) + database = configureDatabase(dataSourceProperties, makeTestDatabaseProperties()) - configuration = RequeryConfiguration(dataSourceProperties) + configuration = RequeryConfiguration(dataSourceProperties, databaseProperties = makeTestDatabaseProperties()) fs = Jimfs.newFileSystem(Configuration.unix()) } @@ -59,7 +60,7 @@ class NodeAttachmentStorageTest { val expectedHash = testJar.readAll().sha256() database.transaction { - val storage = NodeAttachmentService(fs.getPath("/"), dataSourceProperties, MetricRegistry()) + val storage = NodeAttachmentService(fs.getPath("/"), dataSourceProperties, MetricRegistry(), makeTestDatabaseProperties()) val id = testJar.read { storage.importAttachment(it) } assertEquals(expectedHash, id) @@ -85,7 +86,7 @@ class NodeAttachmentStorageTest { fun `duplicates not allowed`() { val testJar = makeTestJar() database.transaction { - val storage = NodeAttachmentService(fs.getPath("/"), dataSourceProperties, MetricRegistry()) + val storage = NodeAttachmentService(fs.getPath("/"), dataSourceProperties, MetricRegistry(), makeTestDatabaseProperties()) testJar.read { storage.importAttachment(it) } @@ -101,7 +102,7 @@ class NodeAttachmentStorageTest { fun `corrupt entry throws exception`() { val testJar = makeTestJar() database.transaction { - val storage = NodeAttachmentService(fs.getPath("/"), dataSourceProperties, MetricRegistry()) + val storage = NodeAttachmentService(fs.getPath("/"), dataSourceProperties, MetricRegistry(), makeTestDatabaseProperties()) val id = testJar.read { storage.importAttachment(it) } // Corrupt the file in the store. @@ -129,7 +130,7 @@ class NodeAttachmentStorageTest { @Test fun `non jar rejected`() { database.transaction { - val storage = NodeAttachmentService(fs.getPath("/"), dataSourceProperties, MetricRegistry()) + val storage = NodeAttachmentService(fs.getPath("/"), dataSourceProperties, MetricRegistry(), makeTestDatabaseProperties()) val path = fs.getPath("notajar") path.writeLines(listOf("Hey", "there!")) path.read { diff --git a/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt b/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt index f8c380bd99..64a5b9d791 100644 --- a/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt @@ -14,6 +14,7 @@ import net.corda.node.utilities.CordaPersistence import net.corda.node.utilities.configureDatabase import net.corda.testing.MEGA_CORP import net.corda.testing.node.makeTestDataSourceProperties +import net.corda.testing.node.makeTestDatabaseProperties import org.hibernate.annotations.Cascade import org.hibernate.annotations.CascadeType import org.jetbrains.exposed.sql.transactions.TransactionManager @@ -31,7 +32,7 @@ class HibernateObserverTests { @Before fun setUp() { LogHelper.setLevel(HibernateObserver::class) - database = configureDatabase(makeTestDataSourceProperties()) + database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties()) } @After @@ -101,7 +102,7 @@ class HibernateObserverTests { } @Suppress("UNUSED_VARIABLE") - val observer = HibernateObserver(rawUpdatesPublisher, HibernateConfiguration(schemaService)) + val observer = HibernateObserver(rawUpdatesPublisher, HibernateConfiguration(schemaService, makeTestDatabaseProperties())) database.transaction { rawUpdatesPublisher.onNext(Vault.Update(emptySet(), setOf(StateAndRef(TransactionState(TestState(), MEGA_CORP), StateRef(SecureHash.sha256("dummy"), 0))))) val parentRowCountResult = TransactionManager.current().connection.prepareStatement("select count(*) from Parents").executeQuery() diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/DistributedImmutableMapTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/DistributedImmutableMapTests.kt index bb0742468a..0917f5342d 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/DistributedImmutableMapTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/DistributedImmutableMapTests.kt @@ -15,6 +15,7 @@ import net.corda.testing.LogHelper import net.corda.testing.TestDependencyInjectionBase import net.corda.testing.freeLocalHostAndPort import net.corda.testing.node.makeTestDataSourceProperties +import net.corda.testing.node.makeTestDatabaseProperties import org.jetbrains.exposed.sql.Transaction import org.junit.After import org.junit.Before @@ -34,7 +35,7 @@ class DistributedImmutableMapTests : TestDependencyInjectionBase() { fun setup() { LogHelper.setLevel("-org.apache.activemq") LogHelper.setLevel(NetworkMapService::class) - database = configureDatabase(makeTestDataSourceProperties()) + database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties()) cluster = setUpCluster() } diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/PersistentUniquenessProviderTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/PersistentUniquenessProviderTests.kt index 858bae0539..f8db1ecee0 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/PersistentUniquenessProviderTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/PersistentUniquenessProviderTests.kt @@ -9,6 +9,7 @@ import net.corda.testing.MEGA_CORP import net.corda.testing.TestDependencyInjectionBase import net.corda.testing.generateStateRef import net.corda.testing.node.makeTestDataSourceProperties +import net.corda.testing.node.makeTestDatabaseProperties import org.junit.After import org.junit.Before import org.junit.Test @@ -24,7 +25,7 @@ class PersistentUniquenessProviderTests : TestDependencyInjectionBase() { @Before fun setUp() { LogHelper.setLevel(PersistentUniquenessProvider::class) - database = configureDatabase(makeTestDataSourceProperties()) + database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties()) } @After diff --git a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt index b746cbef46..993a2fe5df 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt @@ -23,6 +23,7 @@ import net.corda.testing.* import net.corda.testing.contracts.fillWithSomeTestCash import net.corda.testing.node.MockServices import net.corda.testing.node.makeTestDataSourceProperties +import net.corda.testing.node.makeTestDatabaseProperties import net.corda.testing.schemas.DummyLinearStateSchemaV1 import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatExceptionOfType @@ -46,10 +47,10 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() { fun setUp() { LogHelper.setLevel(NodeVaultService::class) val dataSourceProps = makeTestDataSourceProperties() - database = configureDatabase(dataSourceProps) + database = configureDatabase(dataSourceProps, makeTestDatabaseProperties()) database.transaction { val customSchemas = setOf(CommercialPaperSchemaV1, DummyLinearStateSchemaV1) - val hibernateConfig = HibernateConfiguration(NodeSchemaService(customSchemas)) + val hibernateConfig = HibernateConfiguration(NodeSchemaService(customSchemas), makeTestDatabaseProperties()) services = object : MockServices() { override val vaultService: VaultService = makeVaultService(dataSourceProps, hibernateConfig) diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt index 2b2f6a6038..c5accd024d 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt @@ -30,6 +30,7 @@ import net.corda.testing.* import net.corda.testing.contracts.* import net.corda.testing.node.MockServices import net.corda.testing.node.makeTestDataSourceProperties +import net.corda.testing.node.makeTestDatabaseProperties import net.corda.testing.schemas.DummyLinearStateSchemaV1 import org.assertj.core.api.Assertions import org.assertj.core.api.Assertions.assertThat @@ -56,10 +57,10 @@ class VaultQueryTests : TestDependencyInjectionBase() { @Before fun setUp() { val dataSourceProps = makeTestDataSourceProperties() - database = configureDatabase(dataSourceProps) + database = configureDatabase(dataSourceProps, makeTestDatabaseProperties()) database.transaction { val customSchemas = setOf(CommercialPaperSchemaV1, DummyLinearStateSchemaV1) - val hibernateConfig = HibernateConfiguration(NodeSchemaService(customSchemas)) + val hibernateConfig = HibernateConfiguration(NodeSchemaService(customSchemas), makeTestDatabaseProperties()) services = object : MockServices(MEGA_CORP_KEY) { override val vaultService: VaultService = makeVaultService(dataSourceProps, hibernateConfig) @@ -86,7 +87,7 @@ class VaultQueryTests : TestDependencyInjectionBase() { @Ignore @Test fun createPersistentTestDb() { - val database = configureDatabase(makePersistentDataSourceProperties()) + val database = configureDatabase(makePersistentDataSourceProperties(), makeTestDatabaseProperties()) setUpDb(database, 5000) diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt index 20aa913bfb..6cfea9de2e 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt @@ -20,6 +20,7 @@ import net.corda.testing.* import net.corda.testing.contracts.* import net.corda.testing.node.MockServices import net.corda.testing.node.makeTestDataSourceProperties +import net.corda.testing.node.makeTestDatabaseProperties import net.corda.testing.schemas.DummyLinearStateSchemaV1 import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy @@ -43,10 +44,10 @@ class VaultWithCashTest : TestDependencyInjectionBase() { fun setUp() { LogHelper.setLevel(VaultWithCashTest::class) val dataSourceProps = makeTestDataSourceProperties() - database = configureDatabase(dataSourceProps) + database = configureDatabase(dataSourceProps, makeTestDatabaseProperties()) database.transaction { val customSchemas = setOf(CommercialPaperSchemaV1, DummyLinearStateSchemaV1) - val hibernateConfig = HibernateConfiguration(NodeSchemaService(customSchemas)) + val hibernateConfig = HibernateConfiguration(NodeSchemaService(customSchemas), makeTestDatabaseProperties()) services = object : MockServices() { override val vaultService: VaultService = makeVaultService(dataSourceProps, hibernateConfig) diff --git a/node/src/test/kotlin/net/corda/node/utilities/ObservablesTests.kt b/node/src/test/kotlin/net/corda/node/utilities/ObservablesTests.kt index b74b902b09..f330411d3f 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/ObservablesTests.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/ObservablesTests.kt @@ -4,6 +4,7 @@ import com.google.common.util.concurrent.SettableFuture import net.corda.core.internal.bufferUntilSubscribed import net.corda.core.internal.tee import net.corda.testing.node.makeTestDataSourceProperties +import net.corda.testing.node.makeTestDatabaseProperties import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Test @@ -19,7 +20,7 @@ class ObservablesTests { val toBeClosed = mutableListOf() fun createDatabase(): CordaPersistence { - val database = configureDatabase(makeTestDataSourceProperties()) + val database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties()) toBeClosed += database return database } diff --git a/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt b/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt index 5ac06c5302..b03b406d59 100644 --- a/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt +++ b/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt @@ -21,6 +21,7 @@ import net.corda.testing.* import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockServices import net.corda.testing.node.makeTestDataSourceProperties +import net.corda.testing.node.makeTestDatabaseProperties import org.bouncycastle.asn1.x500.X500Name import org.junit.After import org.junit.Assert @@ -59,7 +60,7 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() { @Before fun setUp() { - database = configureDatabase(makeTestDataSourceProperties()) + database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties()) database.transaction { oracle = NodeInterestRates.Oracle( MEGA_CORP, diff --git a/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt b/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt index b39a2a2e22..5b93da6c1c 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt @@ -21,6 +21,7 @@ import net.corda.node.services.identity.InMemoryIdentityService import net.corda.nodeapi.config.SSLConfiguration import net.corda.testing.node.MockServices import net.corda.testing.node.makeTestDataSourceProperties +import net.corda.testing.node.makeTestDatabaseProperties import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500NameBuilder import org.bouncycastle.asn1.x500.style.BCStyle @@ -163,6 +164,7 @@ fun testNodeConfiguration( whenever(nc.trustStorePassword).thenReturn("trustpass") whenever(nc.rpcUsers).thenReturn(emptyList()) whenever(nc.dataSourceProperties).thenReturn(makeTestDataSourceProperties(myLegalName.commonName)) + whenever(nc.database).thenReturn(makeTestDatabaseProperties()) whenever(nc.emailAddress).thenReturn("") whenever(nc.exportJMXto).thenReturn("") whenever(nc.devMode).thenReturn(true) diff --git a/test-utils/src/main/kotlin/net/corda/testing/node/MockServices.kt b/test-utils/src/main/kotlin/net/corda/testing/node/MockServices.kt index 91ccf67286..26275c99ae 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -80,8 +80,8 @@ open class MockServices(vararg val keys: KeyPair) : ServiceHub { } override val transactionVerifierService: TransactionVerifierService get() = InMemoryTransactionVerifierService(2) - fun makeVaultService(dataSourceProps: Properties, hibernateConfig: HibernateConfiguration = HibernateConfiguration(NodeSchemaService())): VaultService { - val vaultService = NodeVaultService(this, dataSourceProps) + fun makeVaultService(dataSourceProps: Properties, hibernateConfig: HibernateConfiguration = HibernateConfiguration(NodeSchemaService(), makeTestDatabaseProperties())): VaultService { + val vaultService = NodeVaultService(this, dataSourceProps, makeTestDatabaseProperties()) HibernateObserver(vaultService.rawUpdates, hibernateConfig) return vaultService } @@ -196,4 +196,10 @@ fun makeTestDataSourceProperties(nodeName: String = SecureHash.randomSHA256().to return props } +fun makeTestDatabaseProperties(): Properties { + val props = Properties() + props.setProperty("transactionIsolationLevel", "repeatableRead") //for other possible values see net.corda.node.utilities.CordaPeristence.parserTransactionIsolationLevel(String) + return props +} + val MOCK_VERSION_INFO = VersionInfo(1, "Mock release", "Mock revision", "Mock Vendor") diff --git a/test-utils/src/main/kotlin/net/corda/testing/node/SimpleNode.kt b/test-utils/src/main/kotlin/net/corda/testing/node/SimpleNode.kt index 580ff3072b..e67ed01edd 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/node/SimpleNode.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/node/SimpleNode.kt @@ -32,7 +32,7 @@ class SimpleNode(val config: NodeConfiguration, val address: NetworkHostAndPort rpcAddress: NetworkHostAndPort = freeLocalHostAndPort(), trustRoot: X509Certificate) : AutoCloseable { - val database: CordaPersistence = configureDatabase(config.dataSourceProperties) + val database: CordaPersistence = configureDatabase(config.dataSourceProperties, config.database) val userService = RPCUserServiceImpl(config.rpcUsers) val monitoringService = MonitoringService(MetricRegistry()) val identity: KeyPair = generateKeyPair()