CORDA-3752: rethrow non-rpc specific errors rather than try to reconnect to the server (#5922)

This commit is contained in:
Ryan Fowler 2020-02-13 14:08:56 +00:00 committed by GitHub
parent 1c80ad8be2
commit 84be738374
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 54 additions and 7 deletions

View File

@ -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 {

View File

@ -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)

View File

@ -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