mirror of
https://github.com/corda/corda.git
synced 2024-12-23 14:52:29 +00:00
rewrite NodeMessagingClient to use Hibernate
This commit is contained in:
parent
09adba8275
commit
709495957d
@ -11,6 +11,9 @@ import net.corda.core.messaging.RPCOps
|
|||||||
import net.corda.core.messaging.SingleMessageRecipient
|
import net.corda.core.messaging.SingleMessageRecipient
|
||||||
import net.corda.core.node.services.PartyInfo
|
import net.corda.core.node.services.PartyInfo
|
||||||
import net.corda.core.node.services.TransactionVerifierService
|
import net.corda.core.node.services.TransactionVerifierService
|
||||||
|
import net.corda.core.serialization.SerializationDefaults
|
||||||
|
import net.corda.core.serialization.deserialize
|
||||||
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.transactions.LedgerTransaction
|
import net.corda.core.transactions.LedgerTransaction
|
||||||
import net.corda.core.utilities.*
|
import net.corda.core.utilities.*
|
||||||
import net.corda.node.VersionInfo
|
import net.corda.node.VersionInfo
|
||||||
@ -36,13 +39,12 @@ import org.apache.activemq.artemis.api.core.client.*
|
|||||||
import org.apache.activemq.artemis.api.core.client.ActiveMQClient.DEFAULT_ACK_BATCH_SIZE
|
import org.apache.activemq.artemis.api.core.client.ActiveMQClient.DEFAULT_ACK_BATCH_SIZE
|
||||||
import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl
|
import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import org.jetbrains.exposed.sql.ResultRow
|
|
||||||
import org.jetbrains.exposed.sql.statements.InsertStatement
|
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.*
|
import java.util.concurrent.*
|
||||||
import javax.annotation.concurrent.ThreadSafe
|
import javax.annotation.concurrent.ThreadSafe
|
||||||
|
import javax.persistence.*
|
||||||
|
|
||||||
// TODO: Stop the wallet explorer and other clients from using this class and get rid of persistentInbox
|
// TODO: Stop the wallet explorer and other clients from using this class and get rid of persistentInbox
|
||||||
|
|
||||||
@ -68,12 +70,12 @@ import javax.annotation.concurrent.ThreadSafe
|
|||||||
*/
|
*/
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
class NodeMessagingClient(override val config: NodeConfiguration,
|
class NodeMessagingClient(override val config: NodeConfiguration,
|
||||||
val versionInfo: VersionInfo,
|
private val versionInfo: VersionInfo,
|
||||||
val serverAddress: NetworkHostAndPort,
|
private val serverAddress: NetworkHostAndPort,
|
||||||
val myIdentity: PublicKey?,
|
private val myIdentity: PublicKey?,
|
||||||
val nodeExecutor: AffinityExecutor.ServiceAffinityExecutor,
|
private val nodeExecutor: AffinityExecutor.ServiceAffinityExecutor,
|
||||||
val database: CordaPersistence,
|
val database: CordaPersistence,
|
||||||
val networkMapRegistrationFuture: CordaFuture<Unit>,
|
private val networkMapRegistrationFuture: CordaFuture<Unit>,
|
||||||
val monitoringService: MonitoringService,
|
val monitoringService: MonitoringService,
|
||||||
advertisedAddress: NetworkHostAndPort = serverAddress
|
advertisedAddress: NetworkHostAndPort = serverAddress
|
||||||
) : ArtemisMessagingComponent(), MessagingService {
|
) : ArtemisMessagingComponent(), MessagingService {
|
||||||
@ -93,6 +95,38 @@ class NodeMessagingClient(override val config: NodeConfiguration,
|
|||||||
private val verifierResponseAddress = "$VERIFICATION_RESPONSES_QUEUE_NAME_PREFIX.${random63BitValue()}"
|
private val verifierResponseAddress = "$VERIFICATION_RESPONSES_QUEUE_NAME_PREFIX.${random63BitValue()}"
|
||||||
|
|
||||||
private val messageMaxRetryCount: Int = 3
|
private val messageMaxRetryCount: Int = 3
|
||||||
|
|
||||||
|
fun createProcessedMessage(): AppendOnlyPersistentMap<UUID, Instant, ProcessedMessage, String> {
|
||||||
|
return AppendOnlyPersistentMap(
|
||||||
|
toPersistentEntityKey = { it.toString() },
|
||||||
|
fromPersistentEntity = { Pair(UUID.fromString(it.uuid), it.insertionTime) },
|
||||||
|
toPersistentEntity = { key: UUID, value: Instant ->
|
||||||
|
ProcessedMessage().apply {
|
||||||
|
uuid = key.toString()
|
||||||
|
insertionTime = value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
persistentEntityClass = ProcessedMessage::class.java
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createMessageToRedeliver(): PersistentMap<Long, Pair<Message, MessageRecipients>, RetryMessage, Long> {
|
||||||
|
return PersistentMap(
|
||||||
|
toPersistentEntityKey = { it },
|
||||||
|
fromPersistentEntity = { Pair(it.key,
|
||||||
|
Pair(it.message.deserialize( context = SerializationDefaults.STORAGE_CONTEXT),
|
||||||
|
it.recipients.deserialize( context = SerializationDefaults.STORAGE_CONTEXT))
|
||||||
|
) },
|
||||||
|
toPersistentEntity = { _key: Long, (_message: Message, _recipient: MessageRecipients): Pair<Message, MessageRecipients> ->
|
||||||
|
RetryMessage().apply {
|
||||||
|
key = _key
|
||||||
|
message = _message.serialize(context = SerializationDefaults.STORAGE_CONTEXT).bytes
|
||||||
|
recipients = _recipient.serialize(context = SerializationDefaults.STORAGE_CONTEXT).bytes
|
||||||
|
}
|
||||||
|
},
|
||||||
|
persistentEntityClass = RetryMessage::class.java
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class InnerState {
|
private class InnerState {
|
||||||
@ -107,11 +141,11 @@ class NodeMessagingClient(override val config: NodeConfiguration,
|
|||||||
var verificationResponseConsumer: ClientConsumer? = null
|
var verificationResponseConsumer: ClientConsumer? = null
|
||||||
}
|
}
|
||||||
|
|
||||||
val messagesToRedeliver = database.transaction {
|
private val messagesToRedeliver = database.transaction {
|
||||||
JDBCHashMap<Long, Pair<Message, MessageRecipients>>("${NODE_DATABASE_PREFIX}message_retry", true)
|
createMessageToRedeliver()
|
||||||
}
|
}
|
||||||
|
|
||||||
val scheduledMessageRedeliveries = ConcurrentHashMap<Long, ScheduledFuture<*>>()
|
private val scheduledMessageRedeliveries = ConcurrentHashMap<Long, ScheduledFuture<*>>()
|
||||||
|
|
||||||
val verifierService = when (config.verifierType) {
|
val verifierService = when (config.verifierType) {
|
||||||
VerifierType.InMemory -> InMemoryTransactionVerifierService(numberOfWorkers = 4)
|
VerifierType.InMemory -> InMemoryTransactionVerifierService(numberOfWorkers = 4)
|
||||||
@ -139,17 +173,33 @@ class NodeMessagingClient(override val config: NodeConfiguration,
|
|||||||
private val state = ThreadBox(InnerState())
|
private val state = ThreadBox(InnerState())
|
||||||
private val handlers = CopyOnWriteArrayList<Handler>()
|
private val handlers = CopyOnWriteArrayList<Handler>()
|
||||||
|
|
||||||
private object Table : JDBCHashedTable("${NODE_DATABASE_PREFIX}message_ids") {
|
private val processedMessages = createProcessedMessage()
|
||||||
val uuid = uuidString("message_id")
|
|
||||||
}
|
|
||||||
|
|
||||||
private val processedMessages: MutableSet<UUID> = Collections.synchronizedSet(
|
@Entity
|
||||||
object : AbstractJDBCHashSet<UUID, Table>(Table, loadOnInit = true) {
|
@javax.persistence.Table(name = "${NODE_DATABASE_PREFIX}message_ids")
|
||||||
override fun elementFromRow(row: ResultRow): UUID = row[table.uuid]
|
class ProcessedMessage(
|
||||||
override fun addElementToInsert(insert: InsertStatement, entry: UUID, finalizables: MutableList<() -> Unit>) {
|
@Id
|
||||||
insert[table.uuid] = entry
|
@Column(name = "message_id", length = 36)
|
||||||
}
|
var uuid: String = "",
|
||||||
}
|
|
||||||
|
@Column(name = "insertion_time")
|
||||||
|
var insertionTime: Instant = Instant.now()
|
||||||
|
)
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@javax.persistence.Table(name = "${NODE_DATABASE_PREFIX}message_retry")
|
||||||
|
class RetryMessage(
|
||||||
|
@Id
|
||||||
|
@Column(name = "message_id", length = 36)
|
||||||
|
var key: Long = 0,
|
||||||
|
|
||||||
|
@Lob
|
||||||
|
@Column
|
||||||
|
var message: ByteArray = ByteArray(0),
|
||||||
|
|
||||||
|
@Lob
|
||||||
|
@Column
|
||||||
|
var recipients: ByteArray = ByteArray(0)
|
||||||
)
|
)
|
||||||
|
|
||||||
fun start(rpcOps: RPCOps, userService: RPCUserService) {
|
fun start(rpcOps: RPCOps, userService: RPCUserService) {
|
||||||
@ -374,7 +424,7 @@ class NodeMessagingClient(override val config: NodeConfiguration,
|
|||||||
callHandlers(msg, deliverTo)
|
callHandlers(msg, deliverTo)
|
||||||
}
|
}
|
||||||
// TODO We will at some point need to decide a trimming policy for the id's
|
// TODO We will at some point need to decide a trimming policy for the id's
|
||||||
processedMessages += msg.uniqueMessageId
|
processedMessages[msg.uniqueMessageId] = Instant.now()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import net.corda.core.serialization.SingletonSerializeAsToken
|
|||||||
import net.corda.node.services.api.SchemaService
|
import net.corda.node.services.api.SchemaService
|
||||||
import net.corda.node.services.events.NodeSchedulerService
|
import net.corda.node.services.events.NodeSchedulerService
|
||||||
import net.corda.node.services.keys.PersistentKeyManagementService
|
import net.corda.node.services.keys.PersistentKeyManagementService
|
||||||
|
import net.corda.node.services.messaging.NodeMessagingClient
|
||||||
import net.corda.node.services.network.PersistentNetworkMapService
|
import net.corda.node.services.network.PersistentNetworkMapService
|
||||||
import net.corda.node.services.persistence.DBCheckpointStorage
|
import net.corda.node.services.persistence.DBCheckpointStorage
|
||||||
import net.corda.node.services.persistence.DBTransactionMappingStorage
|
import net.corda.node.services.persistence.DBTransactionMappingStorage
|
||||||
@ -41,7 +42,9 @@ class NodeSchemaService(customSchemas: Set<MappedSchema> = emptySet()) : SchemaS
|
|||||||
NodeSchedulerService.PersistentScheduledState::class.java,
|
NodeSchedulerService.PersistentScheduledState::class.java,
|
||||||
NodeAttachmentService.DBAttachment::class.java,
|
NodeAttachmentService.DBAttachment::class.java,
|
||||||
PersistentNetworkMapService.NetworkNode::class.java,
|
PersistentNetworkMapService.NetworkNode::class.java,
|
||||||
PersistentNetworkMapService.NetworkSubscriber::class.java
|
PersistentNetworkMapService.NetworkSubscriber::class.java,
|
||||||
|
NodeMessagingClient.ProcessedMessage::class.java,
|
||||||
|
NodeMessagingClient.RetryMessage::class.java
|
||||||
))
|
))
|
||||||
|
|
||||||
// Required schemas are those used by internal Corda services
|
// Required schemas are those used by internal Corda services
|
||||||
|
@ -5,11 +5,11 @@ import java.util.*
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements a caching layer on top of an *append-only* table accessed via Hibernate mapping. Note that if the same key is [put] twice the
|
* Implements a caching layer on top of an *append-only* table accessed via Hibernate mapping. Note that if the same key is [set] twice the
|
||||||
* behaviour is unpredictable! There is a best-effort check for double inserts, but this should *not* be relied on, so
|
* behaviour is unpredictable! There is a best-effort check for double inserts, but this should *not* be relied on, so
|
||||||
* ONLY USE THIS IF YOUR TABLE IS APPEND-ONLY
|
* ONLY USE THIS IF YOUR TABLE IS APPEND-ONLY
|
||||||
*/
|
*/
|
||||||
class AppendOnlyPersistentMap<K, V, E, EK> (
|
class AppendOnlyPersistentMap<K, V, E, out EK> (
|
||||||
val toPersistentEntityKey: (K) -> EK,
|
val toPersistentEntityKey: (K) -> EK,
|
||||||
val fromPersistentEntity: (E) -> Pair<K,V>,
|
val fromPersistentEntity: (E) -> Pair<K,V>,
|
||||||
val toPersistentEntity: (key: K, value: V) -> E,
|
val toPersistentEntity: (key: K, value: V) -> E,
|
||||||
@ -84,7 +84,7 @@ class AppendOnlyPersistentMap<K, V, E, EK> (
|
|||||||
*/
|
*/
|
||||||
operator fun set(key: K, value: V) =
|
operator fun set(key: K, value: V) =
|
||||||
set(key, value, logWarning = false) {
|
set(key, value, logWarning = false) {
|
||||||
key,value -> DatabaseTransactionManager.current().session.save(toPersistentEntity(key,value))
|
k, v -> DatabaseTransactionManager.current().session.save(toPersistentEntity(k, v))
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,10 +95,10 @@ class AppendOnlyPersistentMap<K, V, E, EK> (
|
|||||||
*/
|
*/
|
||||||
fun addWithDuplicatesAllowed(key: K, value: V): Boolean =
|
fun addWithDuplicatesAllowed(key: K, value: V): Boolean =
|
||||||
set(key, value) {
|
set(key, value) {
|
||||||
key, value ->
|
k, v ->
|
||||||
val existingEntry = DatabaseTransactionManager.current().session.find(persistentEntityClass, toPersistentEntityKey(key))
|
val existingEntry = DatabaseTransactionManager.current().session.find(persistentEntityClass, toPersistentEntityKey(k))
|
||||||
if (existingEntry == null) {
|
if (existingEntry == null) {
|
||||||
DatabaseTransactionManager.current().session.save(toPersistentEntity(key,value))
|
DatabaseTransactionManager.current().session.save(toPersistentEntity(k, v))
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
fromPersistentEntity(existingEntry).second
|
fromPersistentEntity(existingEntry).second
|
||||||
@ -110,4 +110,5 @@ class AppendOnlyPersistentMap<K, V, E, EK> (
|
|||||||
return result?.let(fromPersistentEntity)?.second
|
return result?.let(fromPersistentEntity)?.second
|
||||||
}
|
}
|
||||||
|
|
||||||
|
operator fun contains(key: K) = get(key) != null
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import java.util.*
|
|||||||
/**
|
/**
|
||||||
* Implements an unbound caching layer on top of a table accessed via Hibernate mapping.
|
* Implements an unbound caching layer on top of a table accessed via Hibernate mapping.
|
||||||
*/
|
*/
|
||||||
class PersistentMap<K, V, E, EK> (
|
class PersistentMap<K, V, E, out EK> (
|
||||||
val toPersistentEntityKey: (K) -> EK,
|
val toPersistentEntityKey: (K) -> EK,
|
||||||
val fromPersistentEntity: (E) -> Pair<K,V>,
|
val fromPersistentEntity: (E) -> Pair<K,V>,
|
||||||
val toPersistentEntity: (key: K, value: V) -> E,
|
val toPersistentEntity: (key: K, value: V) -> E,
|
||||||
@ -47,7 +47,7 @@ class PersistentMap<K, V, E, EK> (
|
|||||||
RemovalCause.EXPIRED, RemovalCause.SIZE, RemovalCause.COLLECTED -> {
|
RemovalCause.EXPIRED, RemovalCause.SIZE, RemovalCause.COLLECTED -> {
|
||||||
log.error("Entry was removed from cache!!!")
|
log.error("Entry was removed from cache!!!")
|
||||||
}
|
}
|
||||||
//else do nothing for RemovalCause.REPLACED
|
RemovalCause.REPLACED -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -57,7 +57,7 @@ class PersistentMap<K, V, E, EK> (
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun all(): Sequence<Pair<K, V>> {
|
fun all(): Sequence<Pair<K, V>> {
|
||||||
return cache.asMap().map { entry -> Pair(entry.key as K, entry.value.get()) }.asSequence()
|
return cache.asMap().asSequence().map { Pair(it.key, it.value.get()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override val size = cache.size().toInt()
|
override val size = cache.size().toInt()
|
||||||
@ -98,8 +98,8 @@ class PersistentMap<K, V, E, EK> (
|
|||||||
operator fun set(key: K, value: V) =
|
operator fun set(key: K, value: V) =
|
||||||
set(key, value,
|
set(key, value,
|
||||||
logWarning = false,
|
logWarning = false,
|
||||||
store = { key: K, value: V ->
|
store = { k: K, v: V ->
|
||||||
DatabaseTransactionManager.current().session.save(toPersistentEntity(key,value))
|
DatabaseTransactionManager.current().session.save(toPersistentEntity(k, v))
|
||||||
null
|
null
|
||||||
},
|
},
|
||||||
replace = { _: K, _: V -> Unit }
|
replace = { _: K, _: V -> Unit }
|
||||||
@ -112,10 +112,10 @@ class PersistentMap<K, V, E, EK> (
|
|||||||
*/
|
*/
|
||||||
fun addWithDuplicatesAllowed(key: K, value: V) =
|
fun addWithDuplicatesAllowed(key: K, value: V) =
|
||||||
set(key, value,
|
set(key, value,
|
||||||
store = { key, value ->
|
store = { k, v ->
|
||||||
val existingEntry = DatabaseTransactionManager.current().session.find(persistentEntityClass, toPersistentEntityKey(key))
|
val existingEntry = DatabaseTransactionManager.current().session.find(persistentEntityClass, toPersistentEntityKey(k))
|
||||||
if (existingEntry == null) {
|
if (existingEntry == null) {
|
||||||
DatabaseTransactionManager.current().session.save(toPersistentEntity(key, value))
|
DatabaseTransactionManager.current().session.save(toPersistentEntity(k, v))
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
fromPersistentEntity(existingEntry).second
|
fromPersistentEntity(existingEntry).second
|
||||||
@ -128,7 +128,7 @@ class PersistentMap<K, V, E, EK> (
|
|||||||
* Associates the specified value with the specified key in this map and persists it.
|
* Associates the specified value with the specified key in this map and persists it.
|
||||||
* @return true if added key was unique, otherwise false
|
* @return true if added key was unique, otherwise false
|
||||||
*/
|
*/
|
||||||
fun addWithDuplicatesReplaced(key: K, value: V) =
|
private fun addWithDuplicatesReplaced(key: K, value: V) =
|
||||||
set(key, value,
|
set(key, value,
|
||||||
logWarning = false,
|
logWarning = false,
|
||||||
store = { k: K, v: V -> merge(k, v) },
|
store = { k: K, v: V -> merge(k, v) },
|
||||||
@ -249,4 +249,11 @@ class PersistentMap<K, V, E, EK> (
|
|||||||
addWithDuplicatesReplaced(key, value)
|
addWithDuplicatesReplaced(key, value)
|
||||||
return old.orElse(null)
|
return old.orElse(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun load() {
|
||||||
|
val session = DatabaseTransactionManager.current().session
|
||||||
|
val criteriaQuery = session.criteriaBuilder.createQuery(persistentEntityClass)
|
||||||
|
criteriaQuery.select(criteriaQuery.from(persistentEntityClass))
|
||||||
|
cache.getAll(session.createQuery(criteriaQuery).resultList.map { e -> fromPersistentEntity(e as E).first }.asIterable())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user