mirror of
https://github.com/corda/corda.git
synced 2024-12-20 05:28:21 +00:00
CORDA-3752: rethrow non-rpc specific errors rather than try to reconnect to the server (#5922)
This commit is contained in:
parent
1c80ad8be2
commit
84be738374
@ -35,6 +35,9 @@ sourceSets {
|
|||||||
runtimeClasspath += main.output + test.output
|
runtimeClasspath += main.output + test.output
|
||||||
srcDir file('src/integration-test/java')
|
srcDir file('src/integration-test/java')
|
||||||
}
|
}
|
||||||
|
resources {
|
||||||
|
srcDirs "src/integration-test/resources"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
smokeTest {
|
smokeTest {
|
||||||
kotlin {
|
kotlin {
|
||||||
|
@ -1,13 +1,22 @@
|
|||||||
package net.corda.client.rpc
|
package net.corda.client.rpc
|
||||||
|
|
||||||
import net.corda.core.context.*
|
import net.corda.core.CordaRuntimeException
|
||||||
|
import net.corda.core.context.Actor
|
||||||
|
import net.corda.core.context.AuthServiceId
|
||||||
|
import net.corda.core.context.InvocationContext
|
||||||
|
import net.corda.core.context.InvocationOrigin
|
||||||
|
import net.corda.core.context.Trace
|
||||||
import net.corda.core.contracts.FungibleAsset
|
import net.corda.core.contracts.FungibleAsset
|
||||||
import net.corda.core.crypto.random63BitValue
|
import net.corda.core.crypto.random63BitValue
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.concurrent.flatMap
|
import net.corda.core.internal.concurrent.flatMap
|
||||||
import net.corda.core.internal.location
|
import net.corda.core.internal.location
|
||||||
import net.corda.core.internal.toPath
|
import net.corda.core.internal.toPath
|
||||||
import net.corda.core.messaging.*
|
import net.corda.core.messaging.FlowProgressHandle
|
||||||
|
import net.corda.core.messaging.StateMachineInfo
|
||||||
|
import net.corda.core.messaging.StateMachineUpdate
|
||||||
|
import net.corda.core.messaging.startFlow
|
||||||
|
import net.corda.core.messaging.startTrackedFlow
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.OpaqueBytes
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
@ -22,8 +31,14 @@ import net.corda.finance.workflows.getCashBalance
|
|||||||
import net.corda.finance.workflows.getCashBalances
|
import net.corda.finance.workflows.getCashBalances
|
||||||
import net.corda.node.internal.NodeWithInfo
|
import net.corda.node.internal.NodeWithInfo
|
||||||
import net.corda.node.services.Permissions.Companion.all
|
import net.corda.node.services.Permissions.Companion.all
|
||||||
|
import net.corda.nodeapi.exceptions.DuplicateAttachmentException
|
||||||
import net.corda.testing.common.internal.checkNotOnClasspath
|
import net.corda.testing.common.internal.checkNotOnClasspath
|
||||||
import net.corda.testing.core.*
|
import net.corda.testing.core.ALICE_NAME
|
||||||
|
import net.corda.testing.core.BOB_NAME
|
||||||
|
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||||
|
import net.corda.testing.core.expect
|
||||||
|
import net.corda.testing.core.expectEvents
|
||||||
|
import net.corda.testing.core.sequence
|
||||||
import net.corda.testing.node.User
|
import net.corda.testing.node.User
|
||||||
import net.corda.testing.node.internal.NodeBasedTest
|
import net.corda.testing.node.internal.NodeBasedTest
|
||||||
import net.corda.testing.node.internal.ProcessUtilities
|
import net.corda.testing.node.internal.ProcessUtilities
|
||||||
@ -31,6 +46,7 @@ import net.corda.testing.node.internal.poll
|
|||||||
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
|
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
|
||||||
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.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@ -38,7 +54,10 @@ import rx.subjects.PublishSubject
|
|||||||
import java.net.URLClassLoader
|
import java.net.URLClassLoader
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.*
|
import java.util.concurrent.CountDownLatch
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
import java.util.concurrent.ScheduledExecutorService
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFalse
|
import kotlin.test.assertFalse
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
@ -47,6 +66,7 @@ class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance"), notaries =
|
|||||||
companion object {
|
companion object {
|
||||||
val rpcUser = User("user1", "test", permissions = setOf(all()))
|
val rpcUser = User("user1", "test", permissions = setOf(all()))
|
||||||
val log = contextLogger()
|
val log = contextLogger()
|
||||||
|
const val testJar = "net/corda/client/rpc/test.jar"
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var node: NodeWithInfo
|
private lateinit var node: NodeWithInfo
|
||||||
@ -244,6 +264,29 @@ class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance"), notaries =
|
|||||||
assertThat(outOfProcessRpc.waitFor()).isZero() // i.e. no exceptions were thrown
|
assertThat(outOfProcessRpc.waitFor()).isZero() // i.e. no exceptions were thrown
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `nonspecific reconnect errors dont trigger graceful reconnect`() {
|
||||||
|
val inputJar1 = Thread.currentThread().contextClassLoader.getResourceAsStream(testJar)!!
|
||||||
|
val inputJar2 = Thread.currentThread().contextClassLoader.getResourceAsStream(testJar)!!
|
||||||
|
var disconnects = 0
|
||||||
|
var reconnects = 0
|
||||||
|
val gracefulReconnect = GracefulReconnect(onDisconnect = {++disconnects}, onReconnect = {++reconnects})
|
||||||
|
|
||||||
|
// This just recreates the original issue which allowed us to fix this. Any non-rpc exception would do
|
||||||
|
// https://r3-cev.atlassian.net/browse/CORDA-3572
|
||||||
|
assertThatThrownBy {
|
||||||
|
client.start(rpcUser.username, rpcUser.password, gracefulReconnect = gracefulReconnect).use {
|
||||||
|
val rpc = it.proxy
|
||||||
|
rpc.uploadAttachment(inputJar1)
|
||||||
|
rpc.uploadAttachment(inputJar2)
|
||||||
|
}
|
||||||
|
}.isInstanceOf(CordaRuntimeException::class.java)
|
||||||
|
.hasMessageContaining(DuplicateAttachmentException::class.java.name)
|
||||||
|
|
||||||
|
assertThat(disconnects).isEqualTo(0)
|
||||||
|
assertThat(reconnects).isEqualTo(0)
|
||||||
|
}
|
||||||
|
|
||||||
private fun checkShellNotification(info: StateMachineInfo) {
|
private fun checkShellNotification(info: StateMachineInfo) {
|
||||||
val context = info.invocationContext
|
val context = info.invocationContext
|
||||||
assertThat(context.origin).isInstanceOf(InvocationOrigin.Shell::class.java)
|
assertThat(context.origin).isInstanceOf(InvocationOrigin.Shell::class.java)
|
||||||
|
Binary file not shown.
@ -271,6 +271,7 @@ class ReconnectingCordaRPCOps private constructor(
|
|||||||
log.info("Could not establish connection. Next retry interval $nextInterval")
|
log.info("Could not establish connection. Next retry interval $nextInterval")
|
||||||
return establishConnectionWithRetry(nextInterval, nextRoundRobinIndex, remainingRetries)
|
return establishConnectionWithRetry(nextInterval, nextRoundRobinIndex, remainingRetries)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val proxy: CordaRPCOps
|
override val proxy: CordaRPCOps
|
||||||
get() = current.proxy
|
get() = current.proxy
|
||||||
override val serverProtocolVersion
|
override val serverProtocolVersion
|
||||||
@ -305,6 +306,7 @@ class ReconnectingCordaRPCOps private constructor(
|
|||||||
*
|
*
|
||||||
* A negative number for [maxNumberOfAttempts] means an unlimited number of retries will be performed.
|
* A negative number for [maxNumberOfAttempts] means an unlimited number of retries will be performed.
|
||||||
*/
|
*/
|
||||||
|
@Suppress("ThrowsCount", "ComplexMethod")
|
||||||
private fun doInvoke(method: Method, args: Array<out Any>?, maxNumberOfAttempts: Int): Any? {
|
private fun doInvoke(method: Method, args: Array<out Any>?, maxNumberOfAttempts: Int): Any? {
|
||||||
checkIfClosed()
|
checkIfClosed()
|
||||||
var remainingAttempts = maxNumberOfAttempts
|
var remainingAttempts = maxNumberOfAttempts
|
||||||
@ -338,9 +340,8 @@ class ReconnectingCordaRPCOps private constructor(
|
|||||||
throw RPCException("User does not have permission to perform operation ${method.name}.", e)
|
throw RPCException("User does not have permission to perform operation ${method.name}.", e)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
log.warn("Failed to perform operation ${method.name}. Unknown error. Retrying....", e)
|
log.warn("Failed to perform operation ${method.name}.", e)
|
||||||
reconnectingRPCConnection.reconnectOnError(e)
|
throw e.targetException
|
||||||
checkIfIsStartFlow(method, e)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastException = e.targetException
|
lastException = e.targetException
|
||||||
|
Loading…
Reference in New Issue
Block a user