Szymon db confing (#1141)

database transaction isolation level and database initialisation are configurable
This commit is contained in:
szymonsztuka
2017-07-31 16:36:34 +01:00
committed by GitHub
parent 9eb1a0c9d5
commit 7620874e5f
34 changed files with 153 additions and 85 deletions

View File

@ -15,6 +15,7 @@ import net.corda.node.utilities.configureDatabase
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
import net.corda.testing.node.makeTestDataSourceProperties import net.corda.testing.node.makeTestDataSourceProperties
import net.corda.testing.node.makeTestDatabaseProperties
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.junit.runners.Parameterized import org.junit.runners.Parameterized
@ -212,7 +213,7 @@ class CommercialPaperTestsGeneric {
fun `issue move and then redeem`() { fun `issue move and then redeem`() {
initialiseTestSerialization() initialiseTestSerialization()
val dataSourcePropsAlice = makeTestDataSourceProperties() val dataSourcePropsAlice = makeTestDataSourceProperties()
val databaseAlice = configureDatabase(dataSourcePropsAlice) val databaseAlice = configureDatabase(dataSourcePropsAlice, makeTestDatabaseProperties())
databaseAlice.transaction { databaseAlice.transaction {
aliceServices = object : MockServices(ALICE_KEY) { aliceServices = object : MockServices(ALICE_KEY) {
@ -231,7 +232,7 @@ class CommercialPaperTestsGeneric {
} }
val dataSourcePropsBigCorp = makeTestDataSourceProperties() val dataSourcePropsBigCorp = makeTestDataSourceProperties()
val databaseBigCorp = configureDatabase(dataSourcePropsBigCorp) val databaseBigCorp = configureDatabase(dataSourcePropsBigCorp, makeTestDatabaseProperties())
databaseBigCorp.transaction { databaseBigCorp.transaction {
bigCorpServices = object : MockServices(BIG_CORP_KEY) { bigCorpServices = object : MockServices(BIG_CORP_KEY) {

View File

@ -21,6 +21,7 @@ import net.corda.testing.contracts.fillWithSomeTestCash
import net.corda.testing.node.MockKeyManagementService import net.corda.testing.node.MockKeyManagementService
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
import net.corda.testing.node.makeTestDataSourceProperties import net.corda.testing.node.makeTestDataSourceProperties
import net.corda.testing.node.makeTestDatabaseProperties
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import java.security.KeyPair import java.security.KeyPair
@ -51,7 +52,7 @@ class CashTests : TestDependencyInjectionBase() {
fun setUp() { fun setUp() {
LogHelper.setLevel(NodeVaultService::class) LogHelper.setLevel(NodeVaultService::class)
val dataSourceProps = makeTestDataSourceProperties() val dataSourceProps = makeTestDataSourceProperties()
database = configureDatabase(dataSourceProps) database = configureDatabase(dataSourceProps, makeTestDatabaseProperties())
database.transaction { database.transaction {
miniCorpServices = object : MockServices(MINI_CORP_KEY) { miniCorpServices = object : MockServices(MINI_CORP_KEY) {
override val keyManagementService: MockKeyManagementService = MockKeyManagementService(identityService, MINI_CORP_KEY, MEGA_CORP_KEY, OUR_KEY) override val keyManagementService: MockKeyManagementService = MockKeyManagementService(identityService, MINI_CORP_KEY, MEGA_CORP_KEY, OUR_KEY)

View File

@ -13,6 +13,7 @@ import junit.framework.TestSuite
import net.corda.testing.TestDependencyInjectionBase import net.corda.testing.TestDependencyInjectionBase
import net.corda.testing.initialiseTestSerialization import net.corda.testing.initialiseTestSerialization
import net.corda.testing.node.makeTestDataSourceProperties import net.corda.testing.node.makeTestDataSourceProperties
import net.corda.testing.node.makeTestDatabaseProperties
import net.corda.testing.resetTestSerialization import net.corda.testing.resetTestSerialization
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.jetbrains.exposed.sql.Transaction import org.jetbrains.exposed.sql.Transaction
@ -33,7 +34,7 @@ import java.util.*
JDBCHashMapTestSuite.SetConstrained::class) JDBCHashMapTestSuite.SetConstrained::class)
class JDBCHashMapTestSuite { class JDBCHashMapTestSuite {
companion object { companion object {
lateinit var transaction: Transaction lateinit var transaction: DatabaseTransaction
lateinit var database: CordaPersistence lateinit var database: CordaPersistence
lateinit var loadOnInitFalseMap: JDBCHashMap<String, String> lateinit var loadOnInitFalseMap: JDBCHashMap<String, String>
lateinit var memoryConstrainedMap: JDBCHashMap<String, String> lateinit var memoryConstrainedMap: JDBCHashMap<String, String>
@ -46,7 +47,7 @@ class JDBCHashMapTestSuite {
@BeforeClass @BeforeClass
fun before() { fun before() {
initialiseTestSerialization() initialiseTestSerialization()
database = configureDatabase(makeTestDataSourceProperties()) database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties())
setUpDatabaseTx() setUpDatabaseTx()
loadOnInitFalseMap = JDBCHashMap<String, String>("test_map_false", loadOnInit = false) loadOnInitFalseMap = JDBCHashMap<String, String>("test_map_false", loadOnInit = false)
memoryConstrainedMap = JDBCHashMap<String, String>("test_map_constrained", loadOnInit = false, maxBuckets = 1) memoryConstrainedMap = JDBCHashMap<String, String>("test_map_constrained", loadOnInit = false, maxBuckets = 1)
@ -105,7 +106,7 @@ class JDBCHashMapTestSuite {
.createTestSuite() .createTestSuite()
private fun setUpDatabaseTx() { private fun setUpDatabaseTx() {
transaction = TransactionManager.currentOrNew(Connection.TRANSACTION_REPEATABLE_READ) transaction = DatabaseTransactionManager.currentOrNew()
} }
private fun closeDatabaseTx() { private fun closeDatabaseTx() {
@ -232,7 +233,7 @@ class JDBCHashMapTestSuite {
@Before @Before
fun before() { fun before() {
database = configureDatabase(makeTestDataSourceProperties()) database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties())
} }
@After @After

View File

@ -485,7 +485,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
private fun makeVaultObservers() { private fun makeVaultObservers() {
VaultSoftLockManager(services.vaultService, smm) VaultSoftLockManager(services.vaultService, smm)
ScheduledActivityObserver(services) ScheduledActivityObserver(services)
HibernateObserver(services.vaultService.rawUpdates, HibernateConfiguration(services.schemaService)) HibernateObserver(services.vaultService.rawUpdates, HibernateConfiguration(services.schemaService, configuration.database ?: Properties()))
} }
private fun makeInfo(): NodeInfo { private fun makeInfo(): NodeInfo {
@ -544,7 +544,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
protected open fun initialiseDatabasePersistence(insideTransaction: () -> Unit) { protected open fun initialiseDatabasePersistence(insideTransaction: () -> Unit) {
val props = configuration.dataSourceProperties val props = configuration.dataSourceProperties
if (props.isNotEmpty()) { 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. // Now log the vendor string as this will also cause a connection to be tested eagerly.
database.transaction { database.transaction {
log.info("Connected to ${database.database.vendor} database.") log.info("Connected to ${database.database.vendor} database.")
@ -764,7 +764,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
private fun createAttachmentStorage(): NodeAttachmentService { private fun createAttachmentStorage(): NodeAttachmentService {
val attachmentsDir = (configuration.baseDirectory / "attachments").createDirectories() 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() { private inner class ServiceHubInternalImpl : ServiceHubInternal, SingletonSerializeAsToken() {
@ -776,9 +776,9 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
override val validatedTransactions = makeTransactionStorage() override val validatedTransactions = makeTransactionStorage()
override val transactionVerifierService by lazy { makeTransactionVerifierService() } override val transactionVerifierService by lazy { makeTransactionVerifierService() }
override val networkMapCache by lazy { InMemoryNetworkMapCache(this) } 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 { 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 // 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 // the KMS is meant for derived temporary keys used in transactions, and we're not supposed to sign things with

View File

@ -25,6 +25,7 @@ interface NodeConfiguration : NodeSSLConfiguration {
val emailAddress: String val emailAddress: String
val exportJMXto: String val exportJMXto: String
val dataSourceProperties: Properties val dataSourceProperties: Properties
val database: Properties?
val rpcUsers: List<User> val rpcUsers: List<User>
val devMode: Boolean val devMode: Boolean
val certificateSigningService: URL val certificateSigningService: URL
@ -47,6 +48,7 @@ data class FullNodeConfiguration(
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: Properties?,
override val certificateSigningService: URL, override val certificateSigningService: URL,
override val networkMapService: NetworkMapInfo?, override val networkMapService: NetworkMapInfo?,
override val minimumPlatformVersion: Int = 1, override val minimumPlatformVersion: Int = 1,

View File

@ -15,10 +15,11 @@ import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment
import org.hibernate.service.UnknownUnwrapTypeException import org.hibernate.service.UnknownUnwrapTypeException
import java.sql.Connection import java.sql.Connection
import java.util.*
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
class HibernateConfiguration(val schemaService: SchemaService, val useDefaultLogging: Boolean = false) { class HibernateConfiguration(val schemaService: SchemaService, val useDefaultLogging: Boolean = false, val databaseProperties: Properties) {
constructor(schemaService: SchemaService) : this(schemaService, false) constructor(schemaService: SchemaService, databaseProperties: Properties) : this(schemaService, false, databaseProperties)
companion object { companion object {
val logger = loggerFor<HibernateConfiguration>() val logger = loggerFor<HibernateConfiguration>()
@ -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. // 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", HibernateConfiguration.NodeDatabaseConnectionProvider::class.java.name) 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.show_sql", "$useDefaultLogging")
.setProperty("hibernate.format_sql", "$useDefaultLogging") .setProperty("hibernate.format_sql", "$useDefaultLogging")
schemas.forEach { schema -> schemas.forEach { schema ->
@ -102,7 +103,7 @@ class HibernateConfiguration(val schemaService: SchemaService, val useDefaultLog
override fun supportsAggressiveRelease(): Boolean = true override fun supportsAggressiveRelease(): Boolean = true
override fun getConnection(): Connection { override fun getConnection(): Connection {
return DatabaseTransactionManager.newTransaction(Connection.TRANSACTION_REPEATABLE_READ).connection return DatabaseTransactionManager.newTransaction().connection
} }
override fun <T : Any?> unwrap(unwrapType: Class<T>): T { override fun <T : Any?> unwrap(unwrapType: Class<T>): T {

View File

@ -3,6 +3,7 @@ package net.corda.node.services.database
import com.zaxxer.hikari.HikariConfig import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource import com.zaxxer.hikari.HikariDataSource
import io.requery.Persistable import io.requery.Persistable
import io.requery.TransactionIsolation
import io.requery.meta.EntityModel import io.requery.meta.EntityModel
import io.requery.sql.KotlinEntityDataStore import io.requery.sql.KotlinEntityDataStore
import io.requery.sql.SchemaModifier import io.requery.sql.SchemaModifier
@ -13,7 +14,7 @@ import java.sql.Connection
import java.util.* import java.util.*
import java.util.concurrent.ConcurrentHashMap 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 { companion object {
val logger = loggerFor<RequeryConfiguration>() val logger = loggerFor<RequeryConfiguration>()
@ -40,11 +41,25 @@ class RequeryConfiguration(val properties: Properties, val useDefaultLogging: Bo
fun makeSessionFactoryForModel(model: EntityModel): KotlinEntityDataStore<Persistable> { fun makeSessionFactoryForModel(model: EntityModel): KotlinEntityDataStore<Persistable> {
val configuration = KotlinConfigurationTransactionWrapper(model, dataSource, useDefaultLogging = this.useDefaultLogging) val configuration = KotlinConfigurationTransactionWrapper(model, dataSource, useDefaultLogging = this.useDefaultLogging)
val tables = SchemaModifier(configuration) val tables = SchemaModifier(configuration)
val mode = TableCreationMode.CREATE_NOT_EXISTS if (databaseProperties.getProperty("initDatabase","true") == "true" ) {
tables.createTables(mode) val mode = TableCreationMode.CREATE_NOT_EXISTS
tables.createTables(mode)
}
return KotlinEntityDataStore(configuration) return KotlinEntityDataStore(configuration)
} }
// TODO: remove once Requery supports QUERY WITH COMPOSITE_KEY IN // TODO: remove once Requery supports QUERY WITH COMPOSITE_KEY IN
fun jdbcSession(): Connection = DatabaseTransactionManager.current().connection 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
}
}

View File

@ -32,13 +32,13 @@ import javax.annotation.concurrent.ThreadSafe
* Stores attachments in H2 database. * Stores attachments in H2 database.
*/ */
@ThreadSafe @ThreadSafe
class NodeAttachmentService(val storePath: Path, dataSourceProperties: Properties, metrics: MetricRegistry) class NodeAttachmentService(val storePath: Path, dataSourceProperties: Properties, metrics: MetricRegistry, databaseProperties: Properties?)
: AttachmentStorage, AcceptsFileUpload, SingletonSerializeAsToken() { : AttachmentStorage, AcceptsFileUpload, SingletonSerializeAsToken() {
companion object { companion object {
private val log = loggerFor<NodeAttachmentService>() private val log = loggerFor<NodeAttachmentService>()
} }
val configuration = RequeryConfiguration(dataSourceProperties) val configuration = RequeryConfiguration(dataSourceProperties, databaseProperties = databaseProperties ?: Properties())
val session = configuration.sessionForModel(Models.PERSISTENCE) val session = configuration.sessionForModel(Models.PERSISTENCE)
@VisibleForTesting @VisibleForTesting

View File

@ -57,7 +57,7 @@ class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
DatabaseTransactionManager.current().close() DatabaseTransactionManager.current().close()
Strand.sleep(millis) Strand.sleep(millis)
DatabaseTransactionManager.dataSource = db DatabaseTransactionManager.dataSource = db
DatabaseTransactionManager.newTransaction(Connection.TRANSACTION_REPEATABLE_READ) DatabaseTransactionManager.newTransaction()
} else Strand.sleep(millis) } else Strand.sleep(millis)
} }
} }

View File

@ -34,6 +34,7 @@ import net.corda.core.serialization.serialize
import net.corda.core.transactions.* import net.corda.core.transactions.*
import net.corda.core.utilities.* import net.corda.core.utilities.*
import net.corda.node.services.database.RequeryConfiguration 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.statemachine.FlowStateMachineImpl
import net.corda.node.services.vault.schemas.requery.Models import net.corda.node.services.vault.schemas.requery.Models
import net.corda.node.services.vault.schemas.requery.VaultSchema 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: 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. * 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 { private companion object {
val log = loggerFor<NodeVaultService>() val log = loggerFor<NodeVaultService>()
@ -69,8 +70,9 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
val stateRefCompositeColumn: RowExpression = RowExpression.of(listOf(VaultStatesEntity.TX_ID, VaultStatesEntity.INDEX)) 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) val session = configuration.sessionForModel(Models.VAULT)
private val transactionIsolationLevel = parserTransactionIsolationLevel(databaseProperties?.getProperty("transactionIsolationLevel") ?:"")
private class InnerState { private class InnerState {
val _updatesPublisher = PublishSubject.create<Vault.Update<ContractState>>()!! val _updatesPublisher = PublishSubject.create<Vault.Update<ContractState>>()!!
@ -89,7 +91,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
val consumedStateRefs = update.consumed.map { it.ref } val consumedStateRefs = update.consumed.map { it.ref }
log.trace { "Removing $consumedStateRefs consumed contract states and adding $producedStateRefs produced contract states to the database." } 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 -> producedStateRefsMap.forEach { it ->
val state = VaultStatesEntity().apply { val state = VaultStatesEntity().apply {
txId = it.key.txhash.toString() txId = it.key.txhash.toString()
@ -142,7 +144,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
override fun <T : ContractState> states(clazzes: Set<Class<T>>, statuses: EnumSet<Vault.StateStatus>, includeSoftLockedStates: Boolean): Iterable<StateAndRef<T>> { override fun <T : ContractState> states(clazzes: Set<Class<T>>, statuses: EnumSet<Vault.StateStatus>, includeSoftLockedStates: Boolean): Iterable<StateAndRef<T>> {
val stateAndRefs = val stateAndRefs =
session.withTransaction(TransactionIsolation.REPEATABLE_READ) { session.withTransaction(transactionIsolationLevel) {
val query = select(VaultSchema.VaultStates::class) val query = select(VaultSchema.VaultStates::class)
.where(VaultSchema.VaultStates::stateStatus `in` statuses) .where(VaultSchema.VaultStates::stateStatus `in` statuses)
// TODO: temporary fix to continue supporting track() function (until becomes Typed) // 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<StateRef>): Map<StateRef, TransactionState<*>?> { override fun statesForRefs(refs: List<StateRef>): Map<StateRef, TransactionState<*>?> {
val stateAndRefs = val stateAndRefs =
session.withTransaction(TransactionIsolation.REPEATABLE_READ) { session.withTransaction(transactionIsolationLevel) {
var results: List<StateAndRef<*>> = emptyList() var results: List<StateAndRef<*>> = emptyList()
refs.forEach { refs.forEach {
val result = select(VaultSchema.VaultStates::class) val result = select(VaultSchema.VaultStates::class)
@ -273,7 +275,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
private fun loadStates(refs: Collection<StateRef>): HashSet<StateAndRef<ContractState>> { private fun loadStates(refs: Collection<StateRef>): HashSet<StateAndRef<ContractState>> {
val states = HashSet<StateAndRef<ContractState>>() val states = HashSet<StateAndRef<ContractState>>()
if (refs.isNotEmpty()) { if (refs.isNotEmpty()) {
session.withTransaction(TransactionIsolation.REPEATABLE_READ) { session.withTransaction(transactionIsolationLevel) {
val result = select(VaultStatesEntity::class). val result = select(VaultStatesEntity::class).
where(stateRefCompositeColumn.`in`(stateRefArgs(refs))). where(stateRefCompositeColumn.`in`(stateRefArgs(refs))).
and(VaultSchema.VaultStates::stateStatus eq Vault.StateStatus.UNCONSUMED) 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) { override fun addNoteToTransaction(txnId: SecureHash, noteText: String) {
session.withTransaction(TransactionIsolation.REPEATABLE_READ) { session.withTransaction(transactionIsolationLevel) {
val txnNoteEntity = VaultTxnNoteEntity() val txnNoteEntity = VaultTxnNoteEntity()
txnNoteEntity.txId = txnId.toString() txnNoteEntity.txId = txnId.toString()
txnNoteEntity.note = noteText txnNoteEntity.note = noteText
@ -310,7 +312,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
} }
override fun getTransactionNotes(txnId: SecureHash): Iterable<String> { override fun getTransactionNotes(txnId: SecureHash): Iterable<String> {
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 } (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 softLockTimestamp = services.clock.instant()
val stateRefArgs = stateRefArgs(stateRefs) val stateRefArgs = stateRefArgs(stateRefs)
try { try {
session.withTransaction(TransactionIsolation.REPEATABLE_READ) { session.withTransaction(transactionIsolationLevel) {
val updatedRows = update(VaultStatesEntity::class) val updatedRows = update(VaultStatesEntity::class)
.set(VaultStatesEntity.LOCK_ID, lockId.toString()) .set(VaultStatesEntity.LOCK_ID, lockId.toString())
.set(VaultStatesEntity.LOCK_UPDATE_TIME, softLockTimestamp) .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<StateRef>?) { override fun softLockRelease(lockId: UUID, stateRefs: NonEmptySet<StateRef>?) {
if (stateRefs == null) { if (stateRefs == null) {
session.withTransaction(TransactionIsolation.REPEATABLE_READ) { session.withTransaction(transactionIsolationLevel) {
val update = update(VaultStatesEntity::class) val update = update(VaultStatesEntity::class)
.set(VaultStatesEntity.LOCK_ID, null) .set(VaultStatesEntity.LOCK_ID, null)
.set(VaultStatesEntity.LOCK_UPDATE_TIME, services.clock.instant()) .set(VaultStatesEntity.LOCK_UPDATE_TIME, services.clock.instant())
@ -365,7 +367,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
} }
} else { } else {
try { try {
session.withTransaction(TransactionIsolation.REPEATABLE_READ) { session.withTransaction(transactionIsolationLevel) {
val updatedRows = update(VaultStatesEntity::class) val updatedRows = update(VaultStatesEntity::class)
.set(VaultStatesEntity.LOCK_ID, null) .set(VaultStatesEntity.LOCK_ID, null)
.set(VaultStatesEntity.LOCK_UPDATE_TIME, services.clock.instant()) .set(VaultStatesEntity.LOCK_UPDATE_TIME, services.clock.instant())
@ -482,7 +484,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
override fun <T : ContractState> softLockedStates(lockId: UUID?): List<StateAndRef<T>> { override fun <T : ContractState> softLockedStates(lockId: UUID?): List<StateAndRef<T>> {
val stateAndRefs = val stateAndRefs =
session.withTransaction(TransactionIsolation.REPEATABLE_READ) { session.withTransaction(transactionIsolationLevel) {
val query = select(VaultSchema.VaultStates::class) val query = select(VaultSchema.VaultStates::class)
.where(VaultSchema.VaultStates::stateStatus eq Vault.StateStatus.UNCONSUMED) .where(VaultSchema.VaultStates::stateStatus eq Vault.StateStatus.UNCONSUMED)
.and(VaultSchema.VaultStates::contractStateClassName eq Cash.State::class.java.name) .and(VaultSchema.VaultStates::contractStateClassName eq Cash.State::class.java.name)

View File

@ -14,15 +14,16 @@ import java.util.*
import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.CopyOnWriteArrayList
//HikariDataSource implements also Closeable which allows CordaPersistence to be Closeable //HikariDataSource implements Closeable which allows CordaPersistence to be Closeable
class CordaPersistence(var dataSource: HikariDataSource): Closeable { class CordaPersistence(var dataSource: HikariDataSource, databaseProperties: Properties): Closeable {
/** Holds Exposed database, the field will be removed once Exposed library is removed */ /** Holds Exposed database, the field will be removed once Exposed library is removed */
lateinit var database: Database lateinit var database: Database
var transactionIsolationLevel = parserTransactionIsolationLevel(databaseProperties.getProperty("transactionIsolationLevel"))
companion object { companion object {
fun connect(dataSource: HikariDataSource): CordaPersistence { fun connect(dataSource: HikariDataSource, databaseProperties: Properties): CordaPersistence {
return CordaPersistence(dataSource).apply { return CordaPersistence(dataSource, databaseProperties).apply {
DatabaseTransactionManager(this) DatabaseTransactionManager(this)
} }
} }
@ -31,7 +32,7 @@ class CordaPersistence(var dataSource: HikariDataSource): Closeable {
fun createTransaction(): DatabaseTransaction { fun createTransaction(): DatabaseTransaction {
// We need to set the database for the current [Thread] or [Fiber] here as some tests share threads across databases. // We need to set the database for the current [Thread] or [Fiber] here as some tests share threads across databases.
DatabaseTransactionManager.dataSource = this DatabaseTransactionManager.dataSource = this
return DatabaseTransactionManager.currentOrNew(Connection.TRANSACTION_REPEATABLE_READ) return DatabaseTransactionManager.currentOrNew(transactionIsolationLevel)
} }
fun <T> isolatedTransaction(block: DatabaseTransaction.() -> T): T { fun <T> isolatedTransaction(block: DatabaseTransaction.() -> T): T {
@ -45,7 +46,7 @@ class CordaPersistence(var dataSource: HikariDataSource): Closeable {
fun <T> transaction(statement: DatabaseTransaction.() -> T): T { fun <T> transaction(statement: DatabaseTransaction.() -> T): T {
DatabaseTransactionManager.dataSource = this DatabaseTransactionManager.dataSource = this
return transaction(Connection.TRANSACTION_REPEATABLE_READ, 3, statement) return transaction(transactionIsolationLevel, 3, statement)
} }
private fun <T> transaction(transactionIsolation: Int, repetitionAttempts: Int, statement: DatabaseTransaction.() -> T): T { private fun <T> transaction(transactionIsolation: Int, repetitionAttempts: Int, statement: DatabaseTransaction.() -> T): T {
@ -90,10 +91,10 @@ class CordaPersistence(var dataSource: HikariDataSource): Closeable {
} }
} }
fun configureDatabase(props: Properties): CordaPersistence { fun configureDatabase(dataSourceProperties: Properties, databaseProperties: Properties?): CordaPersistence {
val config = HikariConfig(props) val config = HikariConfig(dataSourceProperties)
val dataSource = HikariDataSource(config) 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 //org.jetbrains.exposed.sql.Database will be removed once Exposed library is removed
val database = Database.connect(dataSource) { _ -> ExposedTransactionManager() } val database = Database.connect(dataSource) { _ -> ExposedTransactionManager() }
@ -191,3 +192,16 @@ fun <T : Any> rx.Observable<T>.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
}
}

View File

@ -74,11 +74,11 @@ class DatabaseTransactionManager(initDataSource: CordaPersistence) {
fun currentOrNull(): DatabaseTransaction? = manager.currentOrNull() 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 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) data class Boundary(val txId: UUID)

View File

@ -9,6 +9,10 @@ dataSourceProperties = {
"dataSource.user" = sa "dataSource.user" = sa
"dataSource.password" = "" "dataSource.password" = ""
} }
database = {
transactionIsolationLevel = "repeatableRead"
initDatabase = true
}
devMode = true devMode = true
certificateSigningService = "https://cordaci-netperm.corda.r3cev.com" certificateSigningService = "https://cordaci-netperm.corda.r3cev.com"
useHTTPS = false useHTTPS = false

View File

@ -52,6 +52,7 @@ import static net.corda.core.utilities.ByteArrays.toHexString;
import static net.corda.node.utilities.CordaPersistenceKt.configureDatabase; import static net.corda.node.utilities.CordaPersistenceKt.configureDatabase;
import static net.corda.testing.CoreTestUtils.*; import static net.corda.testing.CoreTestUtils.*;
import static net.corda.testing.node.MockServicesKt.makeTestDataSourceProperties; import static net.corda.testing.node.MockServicesKt.makeTestDataSourceProperties;
import static net.corda.testing.node.MockServicesKt.makeTestDatabaseProperties;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
public class VaultQueryJavaTests extends TestDependencyInjectionBase { public class VaultQueryJavaTests extends TestDependencyInjectionBase {
@ -64,10 +65,10 @@ public class VaultQueryJavaTests extends TestDependencyInjectionBase {
@Before @Before
public void setUp() { public void setUp() {
Properties dataSourceProps = makeTestDataSourceProperties(SecureHash.randomSHA256().toString()); Properties dataSourceProps = makeTestDataSourceProperties(SecureHash.randomSHA256().toString());
database = configureDatabase(dataSourceProps); database = configureDatabase(dataSourceProps, makeTestDatabaseProperties());
Set<MappedSchema> customSchemas = new HashSet<>(Collections.singletonList(DummyLinearStateSchemaV1.INSTANCE)); Set<MappedSchema> customSchemas = new HashSet<>(Collections.singletonList(DummyLinearStateSchemaV1.INSTANCE));
HibernateConfiguration hibernateConfig = new HibernateConfiguration(new NodeSchemaService(customSchemas)); HibernateConfiguration hibernateConfig = new HibernateConfiguration(new NodeSchemaService(customSchemas), makeTestDatabaseProperties());
database.transaction( database.transaction(
statement -> { services = new MockServices(getMEGA_CORP_KEY()) { statement -> { services = new MockServices(getMEGA_CORP_KEY()) {
@NotNull @NotNull

View File

@ -15,6 +15,7 @@ import net.corda.node.services.persistence.schemas.requery.AttachmentEntity
import net.corda.node.services.transactions.SimpleNotaryService import net.corda.node.services.transactions.SimpleNotaryService
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import net.corda.testing.node.makeTestDataSourceProperties import net.corda.testing.node.makeTestDataSourceProperties
import net.corda.testing.node.makeTestDatabaseProperties
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
@ -35,7 +36,7 @@ class AttachmentTests {
fun setUp() { fun setUp() {
mockNet = MockNetwork() mockNet = MockNetwork()
val dataSourceProperties = makeTestDataSourceProperties() val dataSourceProperties = makeTestDataSourceProperties()
configuration = RequeryConfiguration(dataSourceProperties) configuration = RequeryConfiguration(dataSourceProperties, databaseProperties = makeTestDatabaseProperties())
} }
@After @After

View File

@ -5,6 +5,7 @@ import net.corda.core.utilities.NetworkHostAndPort
import net.corda.testing.ALICE import net.corda.testing.ALICE
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.testing.node.makeTestDataSourceProperties import net.corda.testing.node.makeTestDataSourceProperties
import net.corda.testing.node.makeTestDatabaseProperties
import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Test import org.junit.Test
import java.net.URL import java.net.URL
@ -21,6 +22,7 @@ class FullNodeConfigurationTest {
keyStorePassword = "cordacadevpass", keyStorePassword = "cordacadevpass",
trustStorePassword = "trustpass", trustStorePassword = "trustpass",
dataSourceProperties = makeTestDataSourceProperties(ALICE.name.commonName), dataSourceProperties = makeTestDataSourceProperties(ALICE.name.commonName),
database = makeTestDatabaseProperties(),
certificateSigningService = URL("http://localhost"), certificateSigningService = URL("http://localhost"),
rpcUsers = emptyList(), rpcUsers = emptyList(),
verifierType = VerifierType.InMemory, verifierType = VerifierType.InMemory,

View File

@ -27,6 +27,7 @@ import net.corda.testing.contracts.fillWithSomeTestDeals
import net.corda.testing.contracts.fillWithSomeTestLinearStates import net.corda.testing.contracts.fillWithSomeTestLinearStates
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
import net.corda.testing.node.makeTestDataSourceProperties import net.corda.testing.node.makeTestDataSourceProperties
import net.corda.testing.node.makeTestDatabaseProperties
import net.corda.testing.schemas.DummyLinearStateSchemaV1 import net.corda.testing.schemas.DummyLinearStateSchemaV1
import net.corda.testing.schemas.DummyLinearStateSchemaV2 import net.corda.testing.schemas.DummyLinearStateSchemaV2
import org.assertj.core.api.Assertions import org.assertj.core.api.Assertions
@ -60,16 +61,17 @@ class HibernateConfigurationTest : TestDependencyInjectionBase() {
@Before @Before
fun setUp() { fun setUp() {
val dataSourceProps = makeTestDataSourceProperties() val dataSourceProps = makeTestDataSourceProperties()
database = configureDatabase(dataSourceProps) val defaultDatabaseProperties = makeTestDatabaseProperties()
database = configureDatabase(dataSourceProps, defaultDatabaseProperties)
val customSchemas = setOf(VaultSchemaV1, CashSchemaV1, SampleCashSchemaV2, SampleCashSchemaV3) val customSchemas = setOf(VaultSchemaV1, CashSchemaV1, SampleCashSchemaV2, SampleCashSchemaV3)
database.transaction { database.transaction {
hibernateConfig = HibernateConfiguration(NodeSchemaService(customSchemas)) hibernateConfig = HibernateConfiguration(NodeSchemaService(customSchemas), makeTestDatabaseProperties())
services = object : MockServices(BOB_KEY) { services = object : MockServices(BOB_KEY) {
override val vaultService: VaultService get() { override val vaultService: VaultService get() {
val vaultService = NodeVaultService(this, dataSourceProps) val vaultService = NodeVaultService(this, dataSourceProps, makeTestDatabaseProperties())
hibernatePersister = HibernateObserver(vaultService.rawUpdates, hibernateConfig) hibernatePersister = HibernateObserver(vaultService.rawUpdates, hibernateConfig)
return vaultService return vaultService
} }

View File

@ -25,6 +25,7 @@ import net.corda.testing.DUMMY_PUBKEY_1
import net.corda.testing.TestDependencyInjectionBase import net.corda.testing.TestDependencyInjectionBase
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import net.corda.testing.node.makeTestDataSourceProperties import net.corda.testing.node.makeTestDataSourceProperties
import net.corda.testing.node.makeTestDatabaseProperties
import org.assertj.core.api.Assertions import org.assertj.core.api.Assertions
import org.junit.After import org.junit.After
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
@ -43,7 +44,7 @@ class RequeryConfigurationTest : TestDependencyInjectionBase() {
@Before @Before
fun setUp() { fun setUp() {
val dataSourceProperties = makeTestDataSourceProperties() val dataSourceProperties = makeTestDataSourceProperties()
database = configureDatabase(dataSourceProperties) database = configureDatabase(dataSourceProperties, makeTestDatabaseProperties())
newTransactionStorage() newTransactionStorage()
newRequeryStorage(dataSourceProperties) 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" val nativeQuery = "SELECT v.transaction_id, v.output_index FROM vault_states v WHERE v.state_status = 0"
database.transaction { database.transaction {
val configuration = RequeryConfiguration(dataSourceProperties, true) val configuration = RequeryConfiguration(dataSourceProperties, true, makeTestDatabaseProperties())
val jdbcSession = configuration.jdbcSession() val jdbcSession = configuration.jdbcSession()
val prepStatement = jdbcSession.prepareStatement(nativeQuery) val prepStatement = jdbcSession.prepareStatement(nativeQuery)
val rs = prepStatement.executeQuery() val rs = prepStatement.executeQuery()
@ -197,7 +198,7 @@ class RequeryConfigurationTest : TestDependencyInjectionBase() {
private fun newRequeryStorage(dataSourceProperties: Properties) { private fun newRequeryStorage(dataSourceProperties: Properties) {
database.transaction { database.transaction {
val configuration = RequeryConfiguration(dataSourceProperties, true) val configuration = RequeryConfiguration(dataSourceProperties, true, makeTestDatabaseProperties())
requerySession = configuration.sessionForModel(Models.VAULT) requerySession = configuration.sessionForModel(Models.VAULT)
} }
} }

View File

@ -21,12 +21,10 @@ import net.corda.node.services.statemachine.StateMachineManager
import net.corda.node.services.vault.NodeVaultService import net.corda.node.services.vault.NodeVaultService
import net.corda.node.utilities.* import net.corda.node.utilities.*
import net.corda.testing.getTestX509Name 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.testNodeConfiguration
import net.corda.testing.initialiseTestSerialization import net.corda.testing.initialiseTestSerialization
import net.corda.testing.node.*
import net.corda.testing.node.TestClock
import net.corda.testing.resetTestSerialization import net.corda.testing.resetTestSerialization
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
@ -37,6 +35,7 @@ import java.nio.file.Paths
import java.security.PublicKey import java.security.PublicKey
import java.time.Clock import java.time.Clock
import java.time.Instant import java.time.Instant
import java.util.*
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -75,7 +74,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
smmHasRemovedAllFlows = CountDownLatch(1) smmHasRemovedAllFlows = CountDownLatch(1)
calls = 0 calls = 0
val dataSourceProps = makeTestDataSourceProperties() val dataSourceProps = makeTestDataSourceProperties()
database = configureDatabase(dataSourceProps) database = configureDatabase(dataSourceProps, makeTestDatabaseProperties())
val identityService = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate) val identityService = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate)
val kms = MockKeyManagementService(identityService, ALICE_KEY) val kms = MockKeyManagementService(identityService, ALICE_KEY)
@ -92,7 +91,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
overrideClock = testClock, overrideClock = testClock,
keyManagement = kms, keyManagement = kms,
network = mockMessagingService), TestReference { network = mockMessagingService), TestReference {
override val vaultService: VaultService = NodeVaultService(this, dataSourceProps) override val vaultService: VaultService = NodeVaultService(this, dataSourceProps, makeTestDatabaseProperties())
override val testReference = this@NodeSchedulerServiceTest override val testReference = this@NodeSchedulerServiceTest
} }
scheduler = NodeSchedulerService(services, schedulerGatedExecutor) scheduler = NodeSchedulerService(services, schedulerGatedExecutor)

View File

@ -22,6 +22,7 @@ import net.corda.node.utilities.configureDatabase
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.node.MOCK_VERSION_INFO import net.corda.testing.node.MOCK_VERSION_INFO
import net.corda.testing.node.makeTestDataSourceProperties 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.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.After import org.junit.After
@ -30,6 +31,7 @@ import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
import java.net.ServerSocket import java.net.ServerSocket
import java.util.*
import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.TimeUnit.MILLISECONDS import java.util.concurrent.TimeUnit.MILLISECONDS
import kotlin.concurrent.thread import kotlin.concurrent.thread
@ -68,7 +70,7 @@ class ArtemisMessagingTests : TestDependencyInjectionBase() {
baseDirectory = baseDirectory, baseDirectory = baseDirectory,
myLegalName = ALICE.name) myLegalName = ALICE.name)
LogHelper.setLevel(PersistentUniquenessProvider::class) LogHelper.setLevel(PersistentUniquenessProvider::class)
database = configureDatabase(makeTestDataSourceProperties()) database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties())
networkMapRegistrationFuture = Futures.immediateFuture(Unit) networkMapRegistrationFuture = Futures.immediateFuture(Unit)
} }

View File

@ -10,6 +10,7 @@ import net.corda.node.utilities.configureDatabase
import net.corda.testing.LogHelper import net.corda.testing.LogHelper
import net.corda.testing.TestDependencyInjectionBase import net.corda.testing.TestDependencyInjectionBase
import net.corda.testing.node.makeTestDataSourceProperties 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.assertThat
import org.assertj.core.api.Assertions.assertThatExceptionOfType import org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.junit.After import org.junit.After
@ -32,7 +33,7 @@ class DBCheckpointStorageTests : TestDependencyInjectionBase() {
@Before @Before
fun setUp() { fun setUp() {
LogHelper.setLevel(PersistentUniquenessProvider::class) LogHelper.setLevel(PersistentUniquenessProvider::class)
database = configureDatabase(makeTestDataSourceProperties()) database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties())
newCheckpointStorage() newCheckpointStorage()
} }

View File

@ -14,6 +14,7 @@ import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.LogHelper import net.corda.testing.LogHelper
import net.corda.testing.TestDependencyInjectionBase import net.corda.testing.TestDependencyInjectionBase
import net.corda.testing.node.makeTestDataSourceProperties 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.assertThat
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -28,7 +29,7 @@ class DBTransactionStorageTests : TestDependencyInjectionBase() {
@Before @Before
fun setUp() { fun setUp() {
LogHelper.setLevel(PersistentUniquenessProvider::class) LogHelper.setLevel(PersistentUniquenessProvider::class)
database = configureDatabase(makeTestDataSourceProperties()) database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties())
newTransactionStorage() newTransactionStorage()
} }

View File

@ -16,6 +16,7 @@ import net.corda.node.services.transactions.PersistentUniquenessProvider
import net.corda.node.utilities.CordaPersistence import net.corda.node.utilities.CordaPersistence
import net.corda.node.utilities.configureDatabase import net.corda.node.utilities.configureDatabase
import net.corda.testing.node.makeTestDataSourceProperties import net.corda.testing.node.makeTestDataSourceProperties
import net.corda.testing.node.makeTestDatabaseProperties
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
@ -42,9 +43,9 @@ class NodeAttachmentStorageTest {
LogHelper.setLevel(PersistentUniquenessProvider::class) LogHelper.setLevel(PersistentUniquenessProvider::class)
dataSourceProperties = makeTestDataSourceProperties() dataSourceProperties = makeTestDataSourceProperties()
database = configureDatabase(dataSourceProperties) database = configureDatabase(dataSourceProperties, makeTestDatabaseProperties())
configuration = RequeryConfiguration(dataSourceProperties) configuration = RequeryConfiguration(dataSourceProperties, databaseProperties = makeTestDatabaseProperties())
fs = Jimfs.newFileSystem(Configuration.unix()) fs = Jimfs.newFileSystem(Configuration.unix())
} }
@ -59,7 +60,7 @@ class NodeAttachmentStorageTest {
val expectedHash = testJar.readAll().sha256() val expectedHash = testJar.readAll().sha256()
database.transaction { database.transaction {
val storage = NodeAttachmentService(fs.getPath("/"), dataSourceProperties, MetricRegistry()) val storage = NodeAttachmentService(fs.getPath("/"), dataSourceProperties, MetricRegistry(), makeTestDatabaseProperties())
val id = testJar.read { storage.importAttachment(it) } val id = testJar.read { storage.importAttachment(it) }
assertEquals(expectedHash, id) assertEquals(expectedHash, id)
@ -85,7 +86,7 @@ class NodeAttachmentStorageTest {
fun `duplicates not allowed`() { fun `duplicates not allowed`() {
val testJar = makeTestJar() val testJar = makeTestJar()
database.transaction { database.transaction {
val storage = NodeAttachmentService(fs.getPath("/"), dataSourceProperties, MetricRegistry()) val storage = NodeAttachmentService(fs.getPath("/"), dataSourceProperties, MetricRegistry(), makeTestDatabaseProperties())
testJar.read { testJar.read {
storage.importAttachment(it) storage.importAttachment(it)
} }
@ -101,7 +102,7 @@ class NodeAttachmentStorageTest {
fun `corrupt entry throws exception`() { fun `corrupt entry throws exception`() {
val testJar = makeTestJar() val testJar = makeTestJar()
database.transaction { database.transaction {
val storage = NodeAttachmentService(fs.getPath("/"), dataSourceProperties, MetricRegistry()) val storage = NodeAttachmentService(fs.getPath("/"), dataSourceProperties, MetricRegistry(), makeTestDatabaseProperties())
val id = testJar.read { storage.importAttachment(it) } val id = testJar.read { storage.importAttachment(it) }
// Corrupt the file in the store. // Corrupt the file in the store.
@ -129,7 +130,7 @@ class NodeAttachmentStorageTest {
@Test @Test
fun `non jar rejected`() { fun `non jar rejected`() {
database.transaction { database.transaction {
val storage = NodeAttachmentService(fs.getPath("/"), dataSourceProperties, MetricRegistry()) val storage = NodeAttachmentService(fs.getPath("/"), dataSourceProperties, MetricRegistry(), makeTestDatabaseProperties())
val path = fs.getPath("notajar") val path = fs.getPath("notajar")
path.writeLines(listOf("Hey", "there!")) path.writeLines(listOf("Hey", "there!"))
path.read { path.read {

View File

@ -14,6 +14,7 @@ import net.corda.node.utilities.CordaPersistence
import net.corda.node.utilities.configureDatabase import net.corda.node.utilities.configureDatabase
import net.corda.testing.MEGA_CORP import net.corda.testing.MEGA_CORP
import net.corda.testing.node.makeTestDataSourceProperties import net.corda.testing.node.makeTestDataSourceProperties
import net.corda.testing.node.makeTestDatabaseProperties
import org.hibernate.annotations.Cascade import org.hibernate.annotations.Cascade
import org.hibernate.annotations.CascadeType import org.hibernate.annotations.CascadeType
import org.jetbrains.exposed.sql.transactions.TransactionManager import org.jetbrains.exposed.sql.transactions.TransactionManager
@ -31,7 +32,7 @@ class HibernateObserverTests {
@Before @Before
fun setUp() { fun setUp() {
LogHelper.setLevel(HibernateObserver::class) LogHelper.setLevel(HibernateObserver::class)
database = configureDatabase(makeTestDataSourceProperties()) database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties())
} }
@After @After
@ -101,7 +102,7 @@ class HibernateObserverTests {
} }
@Suppress("UNUSED_VARIABLE") @Suppress("UNUSED_VARIABLE")
val observer = HibernateObserver(rawUpdatesPublisher, HibernateConfiguration(schemaService)) val observer = HibernateObserver(rawUpdatesPublisher, HibernateConfiguration(schemaService, makeTestDatabaseProperties()))
database.transaction { database.transaction {
rawUpdatesPublisher.onNext(Vault.Update(emptySet(), setOf(StateAndRef(TransactionState(TestState(), MEGA_CORP), StateRef(SecureHash.sha256("dummy"), 0))))) 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() val parentRowCountResult = TransactionManager.current().connection.prepareStatement("select count(*) from Parents").executeQuery()

View File

@ -15,6 +15,7 @@ import net.corda.testing.LogHelper
import net.corda.testing.TestDependencyInjectionBase import net.corda.testing.TestDependencyInjectionBase
import net.corda.testing.freeLocalHostAndPort import net.corda.testing.freeLocalHostAndPort
import net.corda.testing.node.makeTestDataSourceProperties import net.corda.testing.node.makeTestDataSourceProperties
import net.corda.testing.node.makeTestDatabaseProperties
import org.jetbrains.exposed.sql.Transaction import org.jetbrains.exposed.sql.Transaction
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -34,7 +35,7 @@ class DistributedImmutableMapTests : TestDependencyInjectionBase() {
fun setup() { fun setup() {
LogHelper.setLevel("-org.apache.activemq") LogHelper.setLevel("-org.apache.activemq")
LogHelper.setLevel(NetworkMapService::class) LogHelper.setLevel(NetworkMapService::class)
database = configureDatabase(makeTestDataSourceProperties()) database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties())
cluster = setUpCluster() cluster = setUpCluster()
} }

View File

@ -9,6 +9,7 @@ import net.corda.testing.MEGA_CORP
import net.corda.testing.TestDependencyInjectionBase import net.corda.testing.TestDependencyInjectionBase
import net.corda.testing.generateStateRef import net.corda.testing.generateStateRef
import net.corda.testing.node.makeTestDataSourceProperties import net.corda.testing.node.makeTestDataSourceProperties
import net.corda.testing.node.makeTestDatabaseProperties
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
@ -24,7 +25,7 @@ class PersistentUniquenessProviderTests : TestDependencyInjectionBase() {
@Before @Before
fun setUp() { fun setUp() {
LogHelper.setLevel(PersistentUniquenessProvider::class) LogHelper.setLevel(PersistentUniquenessProvider::class)
database = configureDatabase(makeTestDataSourceProperties()) database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties())
} }
@After @After

View File

@ -23,6 +23,7 @@ import net.corda.testing.*
import net.corda.testing.contracts.fillWithSomeTestCash import net.corda.testing.contracts.fillWithSomeTestCash
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
import net.corda.testing.node.makeTestDataSourceProperties import net.corda.testing.node.makeTestDataSourceProperties
import net.corda.testing.node.makeTestDatabaseProperties
import net.corda.testing.schemas.DummyLinearStateSchemaV1 import net.corda.testing.schemas.DummyLinearStateSchemaV1
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatExceptionOfType import org.assertj.core.api.Assertions.assertThatExceptionOfType
@ -46,10 +47,10 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() {
fun setUp() { fun setUp() {
LogHelper.setLevel(NodeVaultService::class) LogHelper.setLevel(NodeVaultService::class)
val dataSourceProps = makeTestDataSourceProperties() val dataSourceProps = makeTestDataSourceProperties()
database = configureDatabase(dataSourceProps) database = configureDatabase(dataSourceProps, makeTestDatabaseProperties())
database.transaction { database.transaction {
val customSchemas = setOf(CommercialPaperSchemaV1, DummyLinearStateSchemaV1) val customSchemas = setOf(CommercialPaperSchemaV1, DummyLinearStateSchemaV1)
val hibernateConfig = HibernateConfiguration(NodeSchemaService(customSchemas)) val hibernateConfig = HibernateConfiguration(NodeSchemaService(customSchemas), makeTestDatabaseProperties())
services = object : MockServices() { services = object : MockServices() {
override val vaultService: VaultService = makeVaultService(dataSourceProps, hibernateConfig) override val vaultService: VaultService = makeVaultService(dataSourceProps, hibernateConfig)

View File

@ -30,6 +30,7 @@ import net.corda.testing.*
import net.corda.testing.contracts.* import net.corda.testing.contracts.*
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
import net.corda.testing.node.makeTestDataSourceProperties import net.corda.testing.node.makeTestDataSourceProperties
import net.corda.testing.node.makeTestDatabaseProperties
import net.corda.testing.schemas.DummyLinearStateSchemaV1 import net.corda.testing.schemas.DummyLinearStateSchemaV1
import org.assertj.core.api.Assertions import org.assertj.core.api.Assertions
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
@ -56,10 +57,10 @@ class VaultQueryTests : TestDependencyInjectionBase() {
@Before @Before
fun setUp() { fun setUp() {
val dataSourceProps = makeTestDataSourceProperties() val dataSourceProps = makeTestDataSourceProperties()
database = configureDatabase(dataSourceProps) database = configureDatabase(dataSourceProps, makeTestDatabaseProperties())
database.transaction { database.transaction {
val customSchemas = setOf(CommercialPaperSchemaV1, DummyLinearStateSchemaV1) val customSchemas = setOf(CommercialPaperSchemaV1, DummyLinearStateSchemaV1)
val hibernateConfig = HibernateConfiguration(NodeSchemaService(customSchemas)) val hibernateConfig = HibernateConfiguration(NodeSchemaService(customSchemas), makeTestDatabaseProperties())
services = object : MockServices(MEGA_CORP_KEY) { services = object : MockServices(MEGA_CORP_KEY) {
override val vaultService: VaultService = makeVaultService(dataSourceProps, hibernateConfig) override val vaultService: VaultService = makeVaultService(dataSourceProps, hibernateConfig)
@ -86,7 +87,7 @@ class VaultQueryTests : TestDependencyInjectionBase() {
@Ignore @Ignore
@Test @Test
fun createPersistentTestDb() { fun createPersistentTestDb() {
val database = configureDatabase(makePersistentDataSourceProperties()) val database = configureDatabase(makePersistentDataSourceProperties(), makeTestDatabaseProperties())
setUpDb(database, 5000) setUpDb(database, 5000)

View File

@ -20,6 +20,7 @@ import net.corda.testing.*
import net.corda.testing.contracts.* import net.corda.testing.contracts.*
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
import net.corda.testing.node.makeTestDataSourceProperties import net.corda.testing.node.makeTestDataSourceProperties
import net.corda.testing.node.makeTestDatabaseProperties
import net.corda.testing.schemas.DummyLinearStateSchemaV1 import net.corda.testing.schemas.DummyLinearStateSchemaV1
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.Assertions.assertThatThrownBy
@ -43,10 +44,10 @@ class VaultWithCashTest : TestDependencyInjectionBase() {
fun setUp() { fun setUp() {
LogHelper.setLevel(VaultWithCashTest::class) LogHelper.setLevel(VaultWithCashTest::class)
val dataSourceProps = makeTestDataSourceProperties() val dataSourceProps = makeTestDataSourceProperties()
database = configureDatabase(dataSourceProps) database = configureDatabase(dataSourceProps, makeTestDatabaseProperties())
database.transaction { database.transaction {
val customSchemas = setOf(CommercialPaperSchemaV1, DummyLinearStateSchemaV1) val customSchemas = setOf(CommercialPaperSchemaV1, DummyLinearStateSchemaV1)
val hibernateConfig = HibernateConfiguration(NodeSchemaService(customSchemas)) val hibernateConfig = HibernateConfiguration(NodeSchemaService(customSchemas), makeTestDatabaseProperties())
services = object : MockServices() { services = object : MockServices() {
override val vaultService: VaultService = makeVaultService(dataSourceProps, hibernateConfig) override val vaultService: VaultService = makeVaultService(dataSourceProps, hibernateConfig)

View File

@ -4,6 +4,7 @@ import com.google.common.util.concurrent.SettableFuture
import net.corda.core.internal.bufferUntilSubscribed import net.corda.core.internal.bufferUntilSubscribed
import net.corda.core.internal.tee import net.corda.core.internal.tee
import net.corda.testing.node.makeTestDataSourceProperties 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.assertThat
import org.junit.After import org.junit.After
import org.junit.Test import org.junit.Test
@ -19,7 +20,7 @@ class ObservablesTests {
val toBeClosed = mutableListOf<Closeable>() val toBeClosed = mutableListOf<Closeable>()
fun createDatabase(): CordaPersistence { fun createDatabase(): CordaPersistence {
val database = configureDatabase(makeTestDataSourceProperties()) val database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties())
toBeClosed += database toBeClosed += database
return database return database
} }

View File

@ -21,6 +21,7 @@ import net.corda.testing.*
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
import net.corda.testing.node.makeTestDataSourceProperties import net.corda.testing.node.makeTestDataSourceProperties
import net.corda.testing.node.makeTestDatabaseProperties
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import org.junit.After import org.junit.After
import org.junit.Assert import org.junit.Assert
@ -59,7 +60,7 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() {
@Before @Before
fun setUp() { fun setUp() {
database = configureDatabase(makeTestDataSourceProperties()) database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties())
database.transaction { database.transaction {
oracle = NodeInterestRates.Oracle( oracle = NodeInterestRates.Oracle(
MEGA_CORP, MEGA_CORP,

View File

@ -21,6 +21,7 @@ import net.corda.node.services.identity.InMemoryIdentityService
import net.corda.nodeapi.config.SSLConfiguration import net.corda.nodeapi.config.SSLConfiguration
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
import net.corda.testing.node.makeTestDataSourceProperties import net.corda.testing.node.makeTestDataSourceProperties
import net.corda.testing.node.makeTestDatabaseProperties
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x500.X500NameBuilder import org.bouncycastle.asn1.x500.X500NameBuilder
import org.bouncycastle.asn1.x500.style.BCStyle import org.bouncycastle.asn1.x500.style.BCStyle
@ -163,6 +164,7 @@ fun testNodeConfiguration(
whenever(nc.trustStorePassword).thenReturn("trustpass") whenever(nc.trustStorePassword).thenReturn("trustpass")
whenever(nc.rpcUsers).thenReturn(emptyList()) whenever(nc.rpcUsers).thenReturn(emptyList())
whenever(nc.dataSourceProperties).thenReturn(makeTestDataSourceProperties(myLegalName.commonName)) whenever(nc.dataSourceProperties).thenReturn(makeTestDataSourceProperties(myLegalName.commonName))
whenever(nc.database).thenReturn(makeTestDatabaseProperties())
whenever(nc.emailAddress).thenReturn("") whenever(nc.emailAddress).thenReturn("")
whenever(nc.exportJMXto).thenReturn("") whenever(nc.exportJMXto).thenReturn("")
whenever(nc.devMode).thenReturn(true) whenever(nc.devMode).thenReturn(true)

View File

@ -80,8 +80,8 @@ open class MockServices(vararg val keys: KeyPair) : ServiceHub {
} }
override val transactionVerifierService: TransactionVerifierService get() = InMemoryTransactionVerifierService(2) override val transactionVerifierService: TransactionVerifierService get() = InMemoryTransactionVerifierService(2)
fun makeVaultService(dataSourceProps: Properties, hibernateConfig: HibernateConfiguration = HibernateConfiguration(NodeSchemaService())): VaultService { fun makeVaultService(dataSourceProps: Properties, hibernateConfig: HibernateConfiguration = HibernateConfiguration(NodeSchemaService(), makeTestDatabaseProperties())): VaultService {
val vaultService = NodeVaultService(this, dataSourceProps) val vaultService = NodeVaultService(this, dataSourceProps, makeTestDatabaseProperties())
HibernateObserver(vaultService.rawUpdates, hibernateConfig) HibernateObserver(vaultService.rawUpdates, hibernateConfig)
return vaultService return vaultService
} }
@ -196,4 +196,10 @@ fun makeTestDataSourceProperties(nodeName: String = SecureHash.randomSHA256().to
return props 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") val MOCK_VERSION_INFO = VersionInfo(1, "Mock release", "Mock revision", "Mock Vendor")

View File

@ -32,7 +32,7 @@ class SimpleNode(val config: NodeConfiguration, val address: NetworkHostAndPort
rpcAddress: NetworkHostAndPort = freeLocalHostAndPort(), rpcAddress: NetworkHostAndPort = freeLocalHostAndPort(),
trustRoot: X509Certificate) : AutoCloseable { trustRoot: X509Certificate) : AutoCloseable {
val database: CordaPersistence = configureDatabase(config.dataSourceProperties) val database: CordaPersistence = configureDatabase(config.dataSourceProperties, config.database)
val userService = RPCUserServiceImpl(config.rpcUsers) val userService = RPCUserServiceImpl(config.rpcUsers)
val monitoringService = MonitoringService(MetricRegistry()) val monitoringService = MonitoringService(MetricRegistry())
val identity: KeyPair = generateKeyPair() val identity: KeyPair = generateKeyPair()