mirror of
https://github.com/corda/corda.git
synced 2025-02-22 10:10:59 +00:00
CORDA-3600 Add flowIO request to checkpoint (#6017)
* Update Checkpoint DB to update flow io request * Modify flow monitor to update Checkpoint DB with waiting flows This happens periodically. * Refactored code to avoid looping twice and updated tests * Fix tests after rebasing * Fix MR comments (non-functional refactor of tests + FlowMonitor). * Made visible for testing method private in DBCheckpointStorage This is not needed anymore. * Explicity check if ioRequestType has changed in update method * Fix shadowing warning * Import non deprecated Assert into test * Use AssertEquals not assert in test * Address more comments (minor refactor) of DBCheckpointStorage * Minor fix use it instead of referencing object explicitly * Add null check to DBCheckpointStorage * Revert changes to Flow Monitor. We will instead store the information in the main thread of the state machine. * Remove now uneeded API and make statemachine update ioRequest * Add Integration Test to check statemachine updates DB on Recieve * Use simpleName in checkpoint storage instead of class. Hibernate was previously resetting the class field this is now set to null (when getting checkpoint form DB) and a new method for getting back the simple name as a string. * Update StateMachineState to store simple name. * Fix after rebase broke stuff + renamed test * Fix Detekt issue * Remove uneeded null assertion
This commit is contained in:
parent
d5e84a4f93
commit
51db2c2d7f
@ -74,7 +74,7 @@ class CordaPersistenceServiceTests {
|
|||||||
status = FlowStatus.RUNNABLE,
|
status = FlowStatus.RUNNABLE,
|
||||||
compatible = false,
|
compatible = false,
|
||||||
progressStep = "",
|
progressStep = "",
|
||||||
ioRequestType = FlowIORequest.ForceCheckpoint.javaClass,
|
ioRequestType = FlowIORequest.ForceCheckpoint::class.java.simpleName,
|
||||||
checkpointInstant = Instant.now(),
|
checkpointInstant = Instant.now(),
|
||||||
flowMetadata = createMetadataRecord(UUID.randomUUID(), now)
|
flowMetadata = createMetadataRecord(UUID.randomUUID(), now)
|
||||||
)
|
)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.corda.node.services.api
|
package net.corda.node.services.api
|
||||||
|
|
||||||
import net.corda.core.flows.StateMachineRunId
|
import net.corda.core.flows.StateMachineRunId
|
||||||
|
import net.corda.core.internal.FlowIORequest
|
||||||
import net.corda.core.serialization.SerializedBytes
|
import net.corda.core.serialization.SerializedBytes
|
||||||
import net.corda.node.services.statemachine.Checkpoint
|
import net.corda.node.services.statemachine.Checkpoint
|
||||||
import net.corda.node.services.statemachine.FlowState
|
import net.corda.node.services.statemachine.FlowState
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package net.corda.node.services.persistence
|
package net.corda.node.services.persistence
|
||||||
|
|
||||||
import net.corda.core.flows.StateMachineRunId
|
import net.corda.core.flows.StateMachineRunId
|
||||||
import net.corda.core.internal.FlowIORequest
|
|
||||||
import net.corda.core.internal.PLATFORM_VERSION
|
import net.corda.core.internal.PLATFORM_VERSION
|
||||||
import net.corda.core.serialization.SerializationDefaults
|
import net.corda.core.serialization.SerializationDefaults
|
||||||
import net.corda.core.serialization.SerializedBytes
|
import net.corda.core.serialization.SerializedBytes
|
||||||
@ -17,12 +16,10 @@ import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
|
|||||||
import net.corda.nodeapi.internal.persistence.currentDBSession
|
import net.corda.nodeapi.internal.persistence.currentDBSession
|
||||||
import org.apache.commons.lang3.ArrayUtils.EMPTY_BYTE_ARRAY
|
import org.apache.commons.lang3.ArrayUtils.EMPTY_BYTE_ARRAY
|
||||||
import org.hibernate.annotations.Type
|
import org.hibernate.annotations.Type
|
||||||
import org.slf4j.Logger
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import java.sql.Connection
|
import java.sql.Connection
|
||||||
import java.sql.SQLException
|
import java.sql.SQLException
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.UUID
|
import java.util.*
|
||||||
import java.util.stream.Stream
|
import java.util.stream.Stream
|
||||||
import javax.persistence.CascadeType
|
import javax.persistence.CascadeType
|
||||||
import javax.persistence.Column
|
import javax.persistence.Column
|
||||||
@ -106,7 +103,7 @@ class DBCheckpointStorage(private val checkpointPerformanceRecorder: CheckpointP
|
|||||||
var progressStep: String?,
|
var progressStep: String?,
|
||||||
|
|
||||||
@Column(name = "flow_io_request")
|
@Column(name = "flow_io_request")
|
||||||
var ioRequestType: Class<out FlowIORequest<*>>?,
|
var ioRequestType: String?,
|
||||||
|
|
||||||
@Column(name = "timestamp", nullable = false)
|
@Column(name = "timestamp", nullable = false)
|
||||||
var checkpointInstant: Instant
|
var checkpointInstant: Instant
|
||||||
@ -238,7 +235,7 @@ class DBCheckpointStorage(private val checkpointPerformanceRecorder: CheckpointP
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getCheckpoint(id: StateMachineRunId): Checkpoint.Serialized? {
|
override fun getCheckpoint(id: StateMachineRunId): Checkpoint.Serialized? {
|
||||||
return currentDBSession().get(DBFlowCheckpoint::class.java, id.uuid.toString())?.toSerializedCheckpoint()
|
return getDBCheckpoint(id)?.toSerializedCheckpoint()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getAllCheckpoints(): Stream<Pair<StateMachineRunId, Checkpoint.Serialized>> {
|
override fun getAllCheckpoints(): Stream<Pair<StateMachineRunId, Checkpoint.Serialized>> {
|
||||||
@ -251,6 +248,10 @@ class DBCheckpointStorage(private val checkpointPerformanceRecorder: CheckpointP
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getDBCheckpoint(id: StateMachineRunId): DBFlowCheckpoint? {
|
||||||
|
return currentDBSession().find(DBFlowCheckpoint::class.java, id.uuid.toString())
|
||||||
|
}
|
||||||
|
|
||||||
private fun createDBCheckpoint(
|
private fun createDBCheckpoint(
|
||||||
id: StateMachineRunId,
|
id: StateMachineRunId,
|
||||||
checkpoint: Checkpoint,
|
checkpoint: Checkpoint,
|
||||||
|
@ -62,7 +62,7 @@ data class Checkpoint(
|
|||||||
val result: Any? = null,
|
val result: Any? = null,
|
||||||
val status: FlowStatus = FlowStatus.RUNNABLE,
|
val status: FlowStatus = FlowStatus.RUNNABLE,
|
||||||
val progressStep: String? = null,
|
val progressStep: String? = null,
|
||||||
val flowIoRequest: Class<out FlowIORequest<*>>? = null,
|
val flowIoRequest: String? = null,
|
||||||
val compatible: Boolean = true
|
val compatible: Boolean = true
|
||||||
) {
|
) {
|
||||||
@CordaSerializable
|
@CordaSerializable
|
||||||
@ -149,7 +149,7 @@ data class Checkpoint(
|
|||||||
val result: SerializedBytes<Any>?,
|
val result: SerializedBytes<Any>?,
|
||||||
val status: FlowStatus,
|
val status: FlowStatus,
|
||||||
val progressStep: String?,
|
val progressStep: String?,
|
||||||
val flowIoRequest: Class<out FlowIORequest<*>>?,
|
val flowIoRequest: String?,
|
||||||
val compatible: Boolean
|
val compatible: Boolean
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
|
@ -158,7 +158,8 @@ class TopLevelTransition(
|
|||||||
flowState = FlowState.Started(event.ioRequest, event.fiber),
|
flowState = FlowState.Started(event.ioRequest, event.fiber),
|
||||||
checkpointState = currentState.checkpoint.checkpointState.copy(
|
checkpointState = currentState.checkpoint.checkpointState.copy(
|
||||||
numberOfSuspends = currentState.checkpoint.checkpointState.numberOfSuspends + 1
|
numberOfSuspends = currentState.checkpoint.checkpointState.numberOfSuspends + 1
|
||||||
)
|
),
|
||||||
|
flowIoRequest = event.ioRequest::class.java.simpleName
|
||||||
)
|
)
|
||||||
if (event.maySkipCheckpoint) {
|
if (event.maySkipCheckpoint) {
|
||||||
actions.addAll(arrayOf(
|
actions.addAll(arrayOf(
|
||||||
|
@ -32,14 +32,14 @@ import net.corda.testing.node.MockServices.Companion.makeTestDataSourcePropertie
|
|||||||
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
|
||||||
|
import org.junit.Assert.assertNotNull
|
||||||
|
import org.junit.Assert.assertNull
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import kotlin.streams.toList
|
import kotlin.streams.toList
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertNotNull
|
|
||||||
import kotlin.test.assertNull
|
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
internal fun CheckpointStorage.checkpoints(): List<Checkpoint.Serialized> {
|
internal fun CheckpointStorage.checkpoints(): List<Checkpoint.Serialized> {
|
||||||
@ -121,7 +121,7 @@ class DBCheckpointStorageTests {
|
|||||||
logic.checkpointSerialize(context = CheckpointSerializationDefaults.CHECKPOINT_CONTEXT)
|
logic.checkpointSerialize(context = CheckpointSerializationDefaults.CHECKPOINT_CONTEXT)
|
||||||
),
|
),
|
||||||
progressStep = "I have made progress",
|
progressStep = "I have made progress",
|
||||||
flowIoRequest = FlowIORequest.SendAndReceive::class.java
|
flowIoRequest = FlowIORequest.SendAndReceive::class.java.simpleName
|
||||||
)
|
)
|
||||||
val updatedSerializedFlowState = updatedCheckpoint.serializeFlowState()
|
val updatedSerializedFlowState = updatedCheckpoint.serializeFlowState()
|
||||||
database.transaction {
|
database.transaction {
|
||||||
@ -439,6 +439,30 @@ class DBCheckpointStorageTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(timeout = 300_000)
|
||||||
|
fun `Checkpoint can be updated with flow io request information`() {
|
||||||
|
val (id, checkpoint) = newCheckpoint(1)
|
||||||
|
database.transaction {
|
||||||
|
val serializedFlowState = checkpoint.flowState.checkpointSerialize(context = CheckpointSerializationDefaults.CHECKPOINT_CONTEXT)
|
||||||
|
checkpointStorage.addCheckpoint(id, checkpoint, serializedFlowState)
|
||||||
|
val checkpointFromStorage = checkpointStorage.getCheckpoint(id)
|
||||||
|
assertNull(checkpointFromStorage!!.flowIoRequest)
|
||||||
|
}
|
||||||
|
database.transaction {
|
||||||
|
val newCheckpoint = checkpoint.copy(flowIoRequest = FlowIORequest.Sleep::class.java.simpleName)
|
||||||
|
val serializedFlowState = newCheckpoint.flowState.checkpointSerialize(
|
||||||
|
context = CheckpointSerializationDefaults.CHECKPOINT_CONTEXT
|
||||||
|
)
|
||||||
|
checkpointStorage.updateCheckpoint(id, newCheckpoint, serializedFlowState)
|
||||||
|
}
|
||||||
|
database.transaction {
|
||||||
|
val checkpointFromStorage = checkpointStorage.getCheckpoint(id)
|
||||||
|
assertNotNull(checkpointFromStorage!!.flowIoRequest)
|
||||||
|
val flowIORequest = checkpointFromStorage.flowIoRequest
|
||||||
|
assertEquals(FlowIORequest.Sleep::class.java.simpleName, flowIORequest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun newCheckpointStorage() {
|
private fun newCheckpointStorage() {
|
||||||
database.transaction {
|
database.transaction {
|
||||||
checkpointStorage = DBCheckpointStorage(object : CheckpointPerformanceRecorder {
|
checkpointStorage = DBCheckpointStorage(object : CheckpointPerformanceRecorder {
|
||||||
|
@ -12,10 +12,12 @@ import net.corda.core.crypto.random63BitValue
|
|||||||
import net.corda.core.flows.*
|
import net.corda.core.flows.*
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.DeclaredField
|
import net.corda.core.internal.DeclaredField
|
||||||
|
import net.corda.core.internal.FlowIORequest
|
||||||
import net.corda.core.internal.concurrent.flatMap
|
import net.corda.core.internal.concurrent.flatMap
|
||||||
import net.corda.core.messaging.MessageRecipients
|
import net.corda.core.messaging.MessageRecipients
|
||||||
import net.corda.core.node.services.PartyInfo
|
import net.corda.core.node.services.PartyInfo
|
||||||
import net.corda.core.node.services.queryBy
|
import net.corda.core.node.services.queryBy
|
||||||
|
import net.corda.core.serialization.SerializedBytes
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.toFuture
|
import net.corda.core.toFuture
|
||||||
@ -25,6 +27,8 @@ import net.corda.core.utilities.ProgressTracker
|
|||||||
import net.corda.core.utilities.ProgressTracker.Change
|
import net.corda.core.utilities.ProgressTracker.Change
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.core.utilities.unwrap
|
import net.corda.core.utilities.unwrap
|
||||||
|
import net.corda.node.services.persistence.CheckpointPerformanceRecorder
|
||||||
|
import net.corda.node.services.persistence.DBCheckpointStorage
|
||||||
import net.corda.node.services.persistence.checkpoints
|
import net.corda.node.services.persistence.checkpoints
|
||||||
import net.corda.testing.contracts.DummyContract
|
import net.corda.testing.contracts.DummyContract
|
||||||
import net.corda.testing.contracts.DummyState
|
import net.corda.testing.contracts.DummyState
|
||||||
@ -69,6 +73,16 @@ class FlowFrameworkTests {
|
|||||||
private lateinit var notaryIdentity: Party
|
private lateinit var notaryIdentity: Party
|
||||||
private val receivedSessionMessages = ArrayList<SessionTransfer>()
|
private val receivedSessionMessages = ArrayList<SessionTransfer>()
|
||||||
|
|
||||||
|
private val dbCheckpointStorage = DBCheckpointStorage(object : CheckpointPerformanceRecorder {
|
||||||
|
override fun record(
|
||||||
|
serializedCheckpointState: SerializedBytes<CheckpointState>,
|
||||||
|
serializedFlowState: SerializedBytes<FlowState>
|
||||||
|
) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUpMockNet() {
|
fun setUpMockNet() {
|
||||||
mockNet = InternalMockNetwork(
|
mockNet = InternalMockNetwork(
|
||||||
@ -208,6 +222,19 @@ class FlowFrameworkTests {
|
|||||||
script(FlowMonitor(aliceNode.smm, Duration.ZERO, Duration.ZERO), FlowMonitor(bobNode.smm, Duration.ZERO, Duration.ZERO))
|
script(FlowMonitor(aliceNode.smm, Duration.ZERO, Duration.ZERO), FlowMonitor(bobNode.smm, Duration.ZERO, Duration.ZERO))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(timeout = 300_000)
|
||||||
|
fun `flow status is updated in database when flow suspends on ioRequest`() {
|
||||||
|
val terminationSignal = Semaphore(0)
|
||||||
|
bobNode.registerCordappFlowFactory(ReceiveFlow::class) { NoOpFlow( terminateUponSignal = terminationSignal) }
|
||||||
|
val flowId = aliceNode.services.startFlow(ReceiveFlow(bob)).id
|
||||||
|
mockNet.runNetwork()
|
||||||
|
aliceNode.database.transaction {
|
||||||
|
val checkpoint = dbCheckpointStorage.getCheckpoint(flowId)
|
||||||
|
assertEquals(FlowIORequest.Receive::class.java.simpleName, checkpoint?.flowIoRequest)
|
||||||
|
}
|
||||||
|
terminationSignal.release()
|
||||||
|
}
|
||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `receiving unexpected session end before entering sendAndReceive`() {
|
fun `receiving unexpected session end before entering sendAndReceive`() {
|
||||||
bobNode.registerCordappFlowFactory(WaitForOtherSideEndBeforeSendAndReceive::class) { NoOpFlow() }
|
bobNode.registerCordappFlowFactory(WaitForOtherSideEndBeforeSendAndReceive::class) { NoOpFlow() }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user