mirror of
https://github.com/corda/corda.git
synced 2025-05-09 20:12:56 +00:00
CORDA-535: Move MySQL & JPA notary configurations out of node
This commit is contained in:
parent
75c9c50abe
commit
29443bf7fe
@ -0,0 +1,9 @@
|
|||||||
|
package net.corda.notary.jpa
|
||||||
|
|
||||||
|
data class JPANotaryConfiguration(
|
||||||
|
val batchSize: Int = 128,
|
||||||
|
val batchTimeoutMs: Long = 1L,
|
||||||
|
val maxInputStates: Int = 2000,
|
||||||
|
val maxDBTransactionRetryCount: Int = 10,
|
||||||
|
val backOffBaseMs: Long = 20L
|
||||||
|
)
|
@ -6,6 +6,7 @@ import net.corda.core.internal.notary.NotaryServiceFlow
|
|||||||
import net.corda.node.services.api.ServiceHubInternal
|
import net.corda.node.services.api.ServiceHubInternal
|
||||||
import net.corda.node.services.transactions.NonValidatingNotaryFlow
|
import net.corda.node.services.transactions.NonValidatingNotaryFlow
|
||||||
import net.corda.node.services.transactions.ValidatingNotaryFlow
|
import net.corda.node.services.transactions.ValidatingNotaryFlow
|
||||||
|
import net.corda.nodeapi.internal.config.parseAs
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
|
||||||
/** Notary service backed by a replicated MySQL database. */
|
/** Notary service backed by a replicated MySQL database. */
|
||||||
@ -17,7 +18,12 @@ class JPANotaryService(
|
|||||||
?: throw IllegalArgumentException("Failed to register ${this::class.java}: notary configuration not present")
|
?: throw IllegalArgumentException("Failed to register ${this::class.java}: notary configuration not present")
|
||||||
|
|
||||||
override val asyncUniquenessProvider = with(services) {
|
override val asyncUniquenessProvider = with(services) {
|
||||||
JPAUniquenessProvider(services.clock, services.database, notaryConfig)
|
val jpaNotaryConfig = try {
|
||||||
|
notaryConfig.extraConfig!!.parseAs<JPANotaryConfiguration>()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
throw IllegalArgumentException("Failed to register ${JPANotaryService::class.java}: JPA notary configuration not present")
|
||||||
|
}
|
||||||
|
JPAUniquenessProvider(services.clock, services.database, jpaNotaryConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createServiceFlow(otherPartySession: FlowSession): NotaryServiceFlow {
|
override fun createServiceFlow(otherPartySession: FlowSession): NotaryServiceFlow {
|
||||||
|
@ -12,14 +12,16 @@ import net.corda.core.flows.StateConsumptionDetails
|
|||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.concurrent.OpenFuture
|
import net.corda.core.internal.concurrent.OpenFuture
|
||||||
import net.corda.core.internal.concurrent.openFuture
|
import net.corda.core.internal.concurrent.openFuture
|
||||||
import net.corda.core.internal.notary.*
|
import net.corda.core.internal.notary.AsyncUniquenessProvider
|
||||||
|
import net.corda.core.internal.notary.NotaryInternalException
|
||||||
|
import net.corda.core.internal.notary.isConsumedByTheSameTx
|
||||||
|
import net.corda.core.internal.notary.validateTimeWindow
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.serialization.SerializationDefaults
|
import net.corda.core.serialization.SerializationDefaults
|
||||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
import net.corda.core.utilities.debug
|
import net.corda.core.utilities.debug
|
||||||
import net.corda.node.services.config.NotaryConfig
|
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
|
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
|
||||||
import net.corda.serialization.internal.CordaSerializationEncoding
|
import net.corda.serialization.internal.CordaSerializationEncoding
|
||||||
@ -36,7 +38,7 @@ import kotlin.concurrent.thread
|
|||||||
|
|
||||||
/** A JPA backed Uniqueness provider */
|
/** A JPA backed Uniqueness provider */
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
class JPAUniquenessProvider(val clock: Clock, val database: CordaPersistence, val notaryConfig: NotaryConfig) : AsyncUniquenessProvider, SingletonSerializeAsToken() {
|
class JPAUniquenessProvider(val clock: Clock, val database: CordaPersistence, val config: JPANotaryConfiguration) : AsyncUniquenessProvider, SingletonSerializeAsToken() {
|
||||||
|
|
||||||
// TODO: test vs. MySQLUniquenessProvider
|
// TODO: test vs. MySQLUniquenessProvider
|
||||||
|
|
||||||
@ -96,7 +98,7 @@ class JPAUniquenessProvider(val clock: Clock, val database: CordaPersistence, va
|
|||||||
try {
|
try {
|
||||||
val buffer = LinkedList<CommitRequest>()
|
val buffer = LinkedList<CommitRequest>()
|
||||||
while (!Thread.interrupted()) {
|
while (!Thread.interrupted()) {
|
||||||
val drainedSize = Queues.drain(requestQueue, buffer, notaryConfig.batchSize, notaryConfig.batchTimeoutMs, TimeUnit.MILLISECONDS)
|
val drainedSize = Queues.drain(requestQueue, buffer, config.batchSize, config.batchTimeoutMs, TimeUnit.MILLISECONDS)
|
||||||
if (drainedSize == 0) continue
|
if (drainedSize == 0) continue
|
||||||
processRequests(buffer)
|
processRequests(buffer)
|
||||||
buffer.clear()
|
buffer.clear()
|
||||||
@ -173,7 +175,7 @@ class JPAUniquenessProvider(val clock: Clock, val database: CordaPersistence, va
|
|||||||
val ids = (states + references).map { encodeStateRef(it) }.toSet()
|
val ids = (states + references).map { encodeStateRef(it) }.toSet()
|
||||||
val committedStates = mutableListOf<CommittedState>()
|
val committedStates = mutableListOf<CommittedState>()
|
||||||
|
|
||||||
for (idsBatch in ids.chunked(notaryConfig.maxInputStates)) {
|
for (idsBatch in ids.chunked(config.maxInputStates)) {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
val existing = session.createNamedQuery("CommittedState.select").setParameter("ids", idsBatch).resultList as List<CommittedState>
|
val existing = session.createNamedQuery("CommittedState.select").setParameter("ids", idsBatch).resultList as List<CommittedState>
|
||||||
committedStates.addAll(existing)
|
committedStates.addAll(existing)
|
||||||
@ -192,8 +194,8 @@ class JPAUniquenessProvider(val clock: Clock, val database: CordaPersistence, va
|
|||||||
|
|
||||||
private fun withRetry(block: () -> Unit) {
|
private fun withRetry(block: () -> Unit) {
|
||||||
var retryCount = 0
|
var retryCount = 0
|
||||||
var backOff = notaryConfig.backOffBaseMs
|
var backOff = config.backOffBaseMs
|
||||||
while (retryCount < notaryConfig.maxDBTransactionRetryCount) {
|
while (retryCount < config.maxDBTransactionRetryCount) {
|
||||||
try {
|
try {
|
||||||
block()
|
block()
|
||||||
break
|
break
|
||||||
|
@ -10,10 +10,11 @@ import net.corda.core.identity.CordaX500Name
|
|||||||
import net.corda.core.internal.notary.NotaryInternalException
|
import net.corda.core.internal.notary.NotaryInternalException
|
||||||
import net.corda.node.services.config.NotaryConfig
|
import net.corda.node.services.config.NotaryConfig
|
||||||
import net.corda.node.services.schema.NodeSchemaService
|
import net.corda.node.services.schema.NodeSchemaService
|
||||||
import net.corda.notary.jpa.JPAUniquenessProvider.Companion.decodeStateRef
|
import net.corda.nodeapi.internal.config.toConfig
|
||||||
import net.corda.notary.jpa.JPAUniquenessProvider.Companion.encodeStateRef
|
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||||
|
import net.corda.notary.jpa.JPAUniquenessProvider.Companion.decodeStateRef
|
||||||
|
import net.corda.notary.jpa.JPAUniquenessProvider.Companion.encodeStateRef
|
||||||
import net.corda.testing.core.SerializationEnvironmentRule
|
import net.corda.testing.core.SerializationEnvironmentRule
|
||||||
import net.corda.testing.core.TestIdentity
|
import net.corda.testing.core.TestIdentity
|
||||||
import net.corda.testing.core.generateStateRef
|
import net.corda.testing.core.generateStateRef
|
||||||
@ -35,7 +36,8 @@ class JPAUniquenessProviderTests {
|
|||||||
private val identity = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party
|
private val identity = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party
|
||||||
private val txID = SecureHash.randomSHA256()
|
private val txID = SecureHash.randomSHA256()
|
||||||
private val requestSignature = NotarisationRequestSignature(DigitalSignature.WithKey(NullKeys.NullPublicKey, ByteArray(32)), 0)
|
private val requestSignature = NotarisationRequestSignature(DigitalSignature.WithKey(NullKeys.NullPublicKey, ByteArray(32)), 0)
|
||||||
private val notaryConfig = NotaryConfig(validating=false, maxInputStates = 10)
|
private val notaryConfig = JPANotaryConfiguration(maxInputStates = 10)
|
||||||
|
|
||||||
|
|
||||||
private lateinit var database: CordaPersistence
|
private lateinit var database: CordaPersistence
|
||||||
|
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
package net.corda.notary.mysql
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
data class MySQLNotaryConfiguration(
|
||||||
|
val dataSource: Properties,
|
||||||
|
/**
|
||||||
|
* Number of times to attempt to reconnect to the database.
|
||||||
|
*/
|
||||||
|
val connectionRetries: Int = 2, // Default value for a 3 server cluster.
|
||||||
|
/**
|
||||||
|
* Time increment between re-connection attempts.
|
||||||
|
*
|
||||||
|
* The total back-off duration is calculated as: backOffIncrement * backOffBase ^ currentRetryCount
|
||||||
|
*/
|
||||||
|
val backOffIncrement: Int = 500,
|
||||||
|
/** Exponential back-off multiplier base. */
|
||||||
|
val backOffBase: Double = 1.5,
|
||||||
|
/** The maximum number of transactions processed in a single batch. */
|
||||||
|
val maxBatchSize: Int = 500,
|
||||||
|
/** The maximum combined number of input states processed in a single batch. */
|
||||||
|
val maxBatchInputStates: Int = 10_000,
|
||||||
|
/** A batch will be processed after a specified timeout even if it has not yet reached full capacity. */
|
||||||
|
val batchTimeoutMs: Long = 200,
|
||||||
|
/**
|
||||||
|
* The maximum number of commit requests in flight. Once the capacity is reached the service will block on
|
||||||
|
* further commit requests.
|
||||||
|
*/
|
||||||
|
val maxQueueSize: Int = 100_000
|
||||||
|
) {
|
||||||
|
init {
|
||||||
|
require(connectionRetries >= 0) { "connectionRetries cannot be negative" }
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ import net.corda.core.internal.notary.NotaryServiceFlow
|
|||||||
import net.corda.node.services.api.ServiceHubInternal
|
import net.corda.node.services.api.ServiceHubInternal
|
||||||
import net.corda.node.services.transactions.NonValidatingNotaryFlow
|
import net.corda.node.services.transactions.NonValidatingNotaryFlow
|
||||||
import net.corda.node.services.transactions.ValidatingNotaryFlow
|
import net.corda.node.services.transactions.ValidatingNotaryFlow
|
||||||
|
import net.corda.nodeapi.internal.config.parseAs
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
|
||||||
/** Notary service backed by a replicated MySQL database. */
|
/** Notary service backed by a replicated MySQL database. */
|
||||||
@ -20,8 +21,11 @@ class MySQLNotaryService(
|
|||||||
?: throw IllegalArgumentException("Failed to register ${this::class.java}: notary configuration not present")
|
?: throw IllegalArgumentException("Failed to register ${this::class.java}: notary configuration not present")
|
||||||
|
|
||||||
override val asyncUniquenessProvider = with(services) {
|
override val asyncUniquenessProvider = with(services) {
|
||||||
val mysqlConfig = notaryConfig.mysql
|
val mysqlConfig = try {
|
||||||
?: throw IllegalArgumentException("Failed to register ${this::class.java}: raft configuration not present")
|
notaryConfig.extraConfig!!.parseAs<MySQLNotaryConfiguration>()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
throw IllegalArgumentException("Failed to register ${MySQLNotaryService::class.java}: mysql configuration not present")
|
||||||
|
}
|
||||||
MySQLUniquenessProvider(
|
MySQLUniquenessProvider(
|
||||||
services.monitoringService.metrics,
|
services.monitoringService.metrics,
|
||||||
services.clock,
|
services.clock,
|
||||||
|
@ -30,7 +30,6 @@ import net.corda.core.serialization.serialize
|
|||||||
import net.corda.core.utilities.debug
|
import net.corda.core.utilities.debug
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
import net.corda.core.utilities.trace
|
import net.corda.core.utilities.trace
|
||||||
import net.corda.node.services.config.MySQLConfiguration
|
|
||||||
import net.corda.serialization.internal.CordaSerializationEncoding.SNAPPY
|
import net.corda.serialization.internal.CordaSerializationEncoding.SNAPPY
|
||||||
import java.sql.*
|
import java.sql.*
|
||||||
import java.time.Clock
|
import java.time.Clock
|
||||||
@ -49,7 +48,7 @@ import kotlin.concurrent.thread
|
|||||||
class MySQLUniquenessProvider(
|
class MySQLUniquenessProvider(
|
||||||
metrics: MetricRegistry,
|
metrics: MetricRegistry,
|
||||||
val clock: Clock,
|
val clock: Clock,
|
||||||
val config: MySQLConfiguration
|
val config: MySQLNotaryConfiguration
|
||||||
) : AsyncUniquenessProvider, SingletonSerializeAsToken() {
|
) : AsyncUniquenessProvider, SingletonSerializeAsToken() {
|
||||||
companion object {
|
companion object {
|
||||||
private val log = loggerFor<MySQLUniquenessProvider>()
|
private val log = loggerFor<MySQLUniquenessProvider>()
|
||||||
|
@ -22,9 +22,9 @@ import net.corda.core.node.NotaryInfo
|
|||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.core.utilities.seconds
|
import net.corda.core.utilities.seconds
|
||||||
import net.corda.node.services.config.MySQLConfiguration
|
|
||||||
import net.corda.node.services.config.NotaryConfig
|
import net.corda.node.services.config.NotaryConfig
|
||||||
import net.corda.nodeapi.internal.DevIdentityGenerator
|
import net.corda.nodeapi.internal.DevIdentityGenerator
|
||||||
|
import net.corda.nodeapi.internal.config.toConfig
|
||||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.contracts.DummyContract
|
import net.corda.testing.contracts.DummyContract
|
||||||
@ -256,7 +256,8 @@ class MySQLNotaryServiceTests : IntegrationTest() {
|
|||||||
configOverrides = {
|
configOverrides = {
|
||||||
val notaryConfig = NotaryConfig(
|
val notaryConfig = NotaryConfig(
|
||||||
validating = true,
|
validating = true,
|
||||||
mysql = MySQLConfiguration(dataStoreProperties, maxBatchSize = 10, maxBatchInputStates = 100),
|
extraConfig = MySQLNotaryConfiguration(dataStoreProperties, maxBatchSize = 10, maxBatchInputStates = 100).toConfig(),
|
||||||
|
serviceLegalName = notaryName,
|
||||||
className = MySQLNotaryService::class.java.name
|
className = MySQLNotaryService::class.java.name
|
||||||
)
|
)
|
||||||
doReturn(notaryConfig).whenever(it).notary
|
doReturn(notaryConfig).whenever(it).notary
|
||||||
|
@ -12,9 +12,9 @@ import net.corda.core.node.AppServiceHub
|
|||||||
import net.corda.core.node.services.CordaService
|
import net.corda.core.node.services.CordaService
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.node.services.config.ConfigHelper
|
import net.corda.node.services.config.ConfigHelper
|
||||||
import net.corda.node.services.config.MySQLConfiguration
|
|
||||||
import net.corda.node.services.transactions.NonValidatingNotaryFlow
|
import net.corda.node.services.transactions.NonValidatingNotaryFlow
|
||||||
import net.corda.nodeapi.internal.config.parseAs
|
import net.corda.nodeapi.internal.config.parseAs
|
||||||
|
import net.corda.notary.mysql.MySQLNotaryConfiguration
|
||||||
import net.corda.notary.mysql.MySQLUniquenessProvider
|
import net.corda.notary.mysql.MySQLUniquenessProvider
|
||||||
import net.corda.notarytest.flows.AsyncLoadTestFlow
|
import net.corda.notarytest.flows.AsyncLoadTestFlow
|
||||||
import java.net.InetAddress
|
import java.net.InetAddress
|
||||||
@ -61,7 +61,7 @@ class JDBCNotaryService(override val services: AppServiceHub, override val notar
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun createUniquenessProvider(): MySQLUniquenessProvider {
|
private fun createUniquenessProvider(): MySQLUniquenessProvider {
|
||||||
val mysqlConfig = appConfig.getConfig("mysql").parseAs<MySQLConfiguration>()
|
val mysqlConfig = appConfig.getConfig("mysql").parseAs<MySQLNotaryConfiguration>()
|
||||||
return MySQLUniquenessProvider(createMetricsRegistry(), services.clock, mysqlConfig)
|
return MySQLUniquenessProvider(createMetricsRegistry(), services.clock, mysqlConfig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user