From 36e8075e72203042aecc9d9a900352a31ed8fdf6 Mon Sep 17 00:00:00 2001 From: Maximiliano Biandratti Date: Wed, 2 Dec 2020 18:16:56 -0300 Subject: [PATCH 1/5] add rpc client timeout --- .../iohk/ethereum/faucet/FaucetConfig.scala | 2 ++ .../faucet/jsonrpc/FaucetBuilder.scala | 2 +- .../faucet/jsonrpc/FaucetRpcService.scala | 2 +- .../faucet/jsonrpc/WalletRpcClient.scala | 5 +++-- .../ethereum/jsonrpc/client/RpcClient.scala | 22 +++++++++++++++---- src/test/resources/application.conf | 3 ++- .../faucet/jsonrpc/FaucetRpcServiceSpec.scala | 1 + .../faucet/jsonrpc/WalletServiceSpec.scala | 1 + src/universal/conf/faucet.conf | 5 ++++- 9 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/main/scala/io/iohk/ethereum/faucet/FaucetConfig.scala b/src/main/scala/io/iohk/ethereum/faucet/FaucetConfig.scala index ad0030d639..b025923719 100644 --- a/src/main/scala/io/iohk/ethereum/faucet/FaucetConfig.scala +++ b/src/main/scala/io/iohk/ethereum/faucet/FaucetConfig.scala @@ -22,6 +22,7 @@ case class FaucetConfig( minRequestInterval: FiniteDuration, handlerTimeout: FiniteDuration, responseTimeout: FiniteDuration, + clientTimeout: FiniteDuration, supervisor: SupervisorConfig, shutdownTimeout: FiniteDuration ) @@ -41,6 +42,7 @@ object FaucetConfig { minRequestInterval = faucetConfig.getDuration("min-request-interval").toMillis.millis, handlerTimeout = faucetConfig.getDuration("handler-timeout").toMillis.millis, responseTimeout = faucetConfig.getDuration("response-timeout").toMillis.millis, + clientTimeout = faucetConfig.getDuration("client-timeout").toMillis.millis, supervisor = SupervisorConfig(faucetConfig), shutdownTimeout = faucetConfig.getDuration("shutdown-timeout").toMillis.millis ) diff --git a/src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetBuilder.scala b/src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetBuilder.scala index 10857f77d8..324d5a974a 100644 --- a/src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetBuilder.scala +++ b/src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetBuilder.scala @@ -37,7 +37,7 @@ trait FaucetRpcServiceBuilder { ) val walletRpcClient: WalletRpcClient = - new WalletRpcClient(faucetConfig.rpcAddress, () => sslContext("faucet.rpc-client")) + new WalletRpcClient(faucetConfig.rpcAddress, faucetConfig.clientTimeout, () => sslContext("faucet.rpc-client")) val walletService = new WalletService(walletRpcClient, keyStore, faucetConfig) val faucetSupervisor: FaucetSupervisor = new FaucetSupervisor(walletService, faucetConfig, shutdown)(system) val faucetRpcService = new FaucetRpcService(faucetConfig) diff --git a/src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetRpcService.scala b/src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetRpcService.scala index d46750c86e..fd7b1bd607 100644 --- a/src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetRpcService.scala +++ b/src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetRpcService.scala @@ -16,7 +16,7 @@ class FaucetRpcService(config: FaucetConfig)(implicit system: ActorSystem) with FaucetHandlerSelector with Logger { - implicit lazy val actorTimeout: Timeout = Timeout(config.responseTimeout) + implicit lazy val actorTimeout: Timeout = Timeout(config.responseTimeout + config.clientTimeout) def sendFunds(sendFundsRequest: SendFundsRequest): ServiceResponse[SendFundsResponse] = selectFaucetHandler() diff --git a/src/main/scala/io/iohk/ethereum/faucet/jsonrpc/WalletRpcClient.scala b/src/main/scala/io/iohk/ethereum/faucet/jsonrpc/WalletRpcClient.scala index 88bc075c2c..e90fd8ae78 100644 --- a/src/main/scala/io/iohk/ethereum/faucet/jsonrpc/WalletRpcClient.scala +++ b/src/main/scala/io/iohk/ethereum/faucet/jsonrpc/WalletRpcClient.scala @@ -13,11 +13,12 @@ import javax.net.ssl.SSLContext import monix.eval.Task import scala.concurrent.ExecutionContext +import scala.concurrent.duration.Duration -class WalletRpcClient(node: Uri, getSSLContext: () => Either[SSLError, SSLContext])(implicit +class WalletRpcClient(node: Uri, timeout: Duration, getSSLContext: () => Either[SSLError, SSLContext])(implicit system: ActorSystem, ec: ExecutionContext -) extends RpcClient(node, getSSLContext) +) extends RpcClient(node, timeout, getSSLContext) with Logger { import io.iohk.ethereum.jsonrpc.client.CommonJsonCodecs._ diff --git a/src/main/scala/io/iohk/ethereum/jsonrpc/client/RpcClient.scala b/src/main/scala/io/iohk/ethereum/jsonrpc/client/RpcClient.scala index d56c85cfd6..f29126d344 100644 --- a/src/main/scala/io/iohk/ethereum/jsonrpc/client/RpcClient.scala +++ b/src/main/scala/io/iohk/ethereum/jsonrpc/client/RpcClient.scala @@ -5,8 +5,10 @@ import java.util.UUID import akka.actor.ActorSystem import akka.http.scaladsl.model._ +import akka.http.scaladsl.settings.{ClientConnectionSettings, ConnectionPoolSettings} import akka.http.scaladsl.unmarshalling.Unmarshal import akka.http.scaladsl.{ConnectionContext, Http, HttpsConnectionContext} +import akka.stream.scaladsl.TcpIdleTimeoutException import io.circe.generic.auto._ import io.circe.parser.parse import io.circe.syntax._ @@ -18,8 +20,9 @@ import javax.net.ssl.SSLContext import monix.eval.Task import scala.concurrent.ExecutionContext +import scala.concurrent.duration._ -abstract class RpcClient(node: Uri, getSSLContext: () => Either[SSLError, SSLContext])(implicit +abstract class RpcClient(node: Uri, timeout: Duration, getSSLContext: () => Either[SSLError, SSLContext])(implicit system: ActorSystem, ec: ExecutionContext ) extends Logger { @@ -32,6 +35,12 @@ abstract class RpcClient(node: Uri, getSSLContext: () => Either[SSLError, SSLCon Http().defaultClientHttpsContext } + lazy val connectionPoolSettings: ConnectionPoolSettings = ConnectionPoolSettings(system) + .withConnectionSettings( + ClientConnectionSettings(system) + .withIdleTimeout(timeout) + ) + protected def doRequest[T: Decoder](method: String, args: Seq[Json]): RpcResponse[T] = { doJsonRequest(method, args).map(_.flatMap(getResult[T])) } @@ -60,11 +69,16 @@ abstract class RpcClient(node: Uri, getSSLContext: () => Either[SSLError, SSLCon Task .deferFuture(for { - response <- Http().singleRequest(request, connectionContext) + response <- Http().singleRequest(request, connectionContext, connectionPoolSettings) data <- Unmarshal(response.entity).to[String] } yield parse(data).left.map(e => RpcClientError(e.message))) - .onErrorHandle { ex: Throwable => - Left(RpcClientError(s"RPC request failed: ${exceptionToString(ex)}")) + .onErrorHandle { + case ex: TcpIdleTimeoutException => + log.error("RPC request timeout", ex) + Left(RpcClientError(s"RPC request timeout")) + case ex: Throwable => + log.error("RPC request failed", ex) + Left(RpcClientError(s"RPC request failed: ${exceptionToString(ex)}")) } } diff --git a/src/test/resources/application.conf b/src/test/resources/application.conf index 8f81ba93f0..157fb97e3a 100644 --- a/src/test/resources/application.conf +++ b/src/test/resources/application.conf @@ -160,7 +160,8 @@ faucet { handler-timeout = 2.seconds - response-timeout = 2.seconds + response-timeout = 1.seconds + client-timeout = 2.seconds supervisor { min-backoff = 3.seconds diff --git a/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetRpcServiceSpec.scala b/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetRpcServiceSpec.scala index 0754586e91..d0eaede250 100644 --- a/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetRpcServiceSpec.scala +++ b/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetRpcServiceSpec.scala @@ -138,6 +138,7 @@ class FaucetRpcServiceSpec minRequestInterval = 10.seconds, handlerTimeout = 10.seconds, responseTimeout = 10.seconds, + clientTimeout = 10.seconds, supervisor = mock[SupervisorConfig], shutdownTimeout = 15.seconds ) diff --git a/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/WalletServiceSpec.scala b/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/WalletServiceSpec.scala index 8d6fc57578..0e8d59a0a7 100644 --- a/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/WalletServiceSpec.scala +++ b/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/WalletServiceSpec.scala @@ -81,6 +81,7 @@ class WalletServiceSpec extends AnyFlatSpec with Matchers with MockFactory { minRequestInterval = 10.seconds, handlerTimeout = 10.seconds, responseTimeout = 10.seconds, + clientTimeout = 10.seconds, supervisor = mock[SupervisorConfig], shutdownTimeout = 15.seconds ) diff --git a/src/universal/conf/faucet.conf b/src/universal/conf/faucet.conf index cab8873ac4..8bc4e6ff33 100644 --- a/src/universal/conf/faucet.conf +++ b/src/universal/conf/faucet.conf @@ -49,7 +49,10 @@ faucet { handler-timeout = 1.seconds # Response time-out from actor resolve - response-timeout = 3.seconds + response-timeout = 1.seconds + + # Response time-out from rpc client resolve + client-timeout = 3.seconds # Supervisor with BackoffSupervisor pattern supervisor { From d6eaa1cf109f9cc90f87f61801afb41caf6604ea Mon Sep 17 00:00:00 2001 From: Maximiliano Biandratti Date: Thu, 3 Dec 2020 11:32:17 -0300 Subject: [PATCH 2/5] change property name from client rpc timeout --- .../iohk/ethereum/faucet/FaucetConfig.scala | 24 ++++++++++++++----- .../ethereum/faucet/SupervisorConfig.scala | 2 +- .../faucet/jsonrpc/FaucetBuilder.scala | 2 +- .../faucet/jsonrpc/FaucetRpcService.scala | 2 +- src/test/resources/application.conf | 4 ++-- .../faucet/jsonrpc/FaucetRpcServiceSpec.scala | 5 ++-- .../faucet/jsonrpc/WalletServiceSpec.scala | 6 ++--- src/universal/conf/faucet.conf | 8 +++---- 8 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/main/scala/io/iohk/ethereum/faucet/FaucetConfig.scala b/src/main/scala/io/iohk/ethereum/faucet/FaucetConfig.scala index b025923719..a8beaa2f55 100644 --- a/src/main/scala/io/iohk/ethereum/faucet/FaucetConfig.scala +++ b/src/main/scala/io/iohk/ethereum/faucet/FaucetConfig.scala @@ -1,6 +1,6 @@ package io.iohk.ethereum.faucet -import com.typesafe.config.{ConfigFactory, Config} +import com.typesafe.config.{Config, ConfigFactory} import io.iohk.ethereum.domain.Address import scala.concurrent.duration.{FiniteDuration, _} @@ -11,18 +11,32 @@ trait FaucetConfigBuilder { lazy val faucetConfig: FaucetConfig = FaucetConfig(rawConfig) } +case class RpcClientConfig( + address: String, + timeout: FiniteDuration +) + +object RpcClientConfig { + def apply(rpcClientConfig: Config): RpcClientConfig = { + + RpcClientConfig( + address = rpcClientConfig.getString("rpc-address"), + timeout = rpcClientConfig.getDuration("timeout").toMillis.millis + ) + } +} + case class FaucetConfig( walletAddress: Address, walletPassword: String, txGasPrice: BigInt, txGasLimit: BigInt, txValue: BigInt, - rpcAddress: String, + rpcClient: RpcClientConfig, keyStoreDir: String, minRequestInterval: FiniteDuration, handlerTimeout: FiniteDuration, responseTimeout: FiniteDuration, - clientTimeout: FiniteDuration, supervisor: SupervisorConfig, shutdownTimeout: FiniteDuration ) @@ -30,19 +44,17 @@ case class FaucetConfig( object FaucetConfig { def apply(typesafeConfig: Config): FaucetConfig = { val faucetConfig = typesafeConfig.getConfig("faucet") - FaucetConfig( walletAddress = Address(faucetConfig.getString("wallet-address")), walletPassword = faucetConfig.getString("wallet-password"), txGasPrice = faucetConfig.getLong("tx-gas-price"), txGasLimit = faucetConfig.getLong("tx-gas-limit"), txValue = faucetConfig.getLong("tx-value"), - rpcAddress = faucetConfig.getString("rpc-client.rpc-address"), + rpcClient = RpcClientConfig(faucetConfig.getConfig("rpc-client")), keyStoreDir = faucetConfig.getString("keystore-dir"), minRequestInterval = faucetConfig.getDuration("min-request-interval").toMillis.millis, handlerTimeout = faucetConfig.getDuration("handler-timeout").toMillis.millis, responseTimeout = faucetConfig.getDuration("response-timeout").toMillis.millis, - clientTimeout = faucetConfig.getDuration("client-timeout").toMillis.millis, supervisor = SupervisorConfig(faucetConfig), shutdownTimeout = faucetConfig.getDuration("shutdown-timeout").toMillis.millis ) diff --git a/src/main/scala/io/iohk/ethereum/faucet/SupervisorConfig.scala b/src/main/scala/io/iohk/ethereum/faucet/SupervisorConfig.scala index 0afc97dfde..e91ed46ac3 100644 --- a/src/main/scala/io/iohk/ethereum/faucet/SupervisorConfig.scala +++ b/src/main/scala/io/iohk/ethereum/faucet/SupervisorConfig.scala @@ -17,7 +17,7 @@ object SupervisorConfig { SupervisorConfig( supervisorConfig.getDuration("min-backoff").toMillis.millis, - supervisorConfig.getDuration("man-backoff").toMillis.millis, + supervisorConfig.getDuration("max-backoff").toMillis.millis, supervisorConfig.getDouble("random-factor"), supervisorConfig.getDuration("auto-reset").toMillis.millis, supervisorConfig.getInt("attempts"), diff --git a/src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetBuilder.scala b/src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetBuilder.scala index 324d5a974a..47888bfc78 100644 --- a/src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetBuilder.scala +++ b/src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetBuilder.scala @@ -37,7 +37,7 @@ trait FaucetRpcServiceBuilder { ) val walletRpcClient: WalletRpcClient = - new WalletRpcClient(faucetConfig.rpcAddress, faucetConfig.clientTimeout, () => sslContext("faucet.rpc-client")) + new WalletRpcClient(faucetConfig.rpcClient.address, faucetConfig.rpcClient.timeout, () => sslContext("faucet.rpc-client")) val walletService = new WalletService(walletRpcClient, keyStore, faucetConfig) val faucetSupervisor: FaucetSupervisor = new FaucetSupervisor(walletService, faucetConfig, shutdown)(system) val faucetRpcService = new FaucetRpcService(faucetConfig) diff --git a/src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetRpcService.scala b/src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetRpcService.scala index fd7b1bd607..61284271d2 100644 --- a/src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetRpcService.scala +++ b/src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetRpcService.scala @@ -16,7 +16,7 @@ class FaucetRpcService(config: FaucetConfig)(implicit system: ActorSystem) with FaucetHandlerSelector with Logger { - implicit lazy val actorTimeout: Timeout = Timeout(config.responseTimeout + config.clientTimeout) + implicit lazy val actorTimeout: Timeout = Timeout(config.responseTimeout + config.rpcClient.timeout) def sendFunds(sendFundsRequest: SendFundsRequest): ServiceResponse[SendFundsResponse] = selectFaucetHandler() diff --git a/src/test/resources/application.conf b/src/test/resources/application.conf index 9f20b9c25b..643b0fcbb9 100644 --- a/src/test/resources/application.conf +++ b/src/test/resources/application.conf @@ -156,6 +156,7 @@ faucet { rpc-client { rpc-address = "http://127.0.0.1:8546/" certificate = null + timeout = 2.seconds } min-request-interval = 1.minute @@ -163,11 +164,10 @@ faucet { handler-timeout = 2.seconds response-timeout = 1.seconds - client-timeout = 2.seconds supervisor { min-backoff = 3.seconds - man-backoff = 30.seconds + max-backoff = 30.seconds random-factor = 0.2 auto-reset = 10.seconds attempts = 4 diff --git a/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetRpcServiceSpec.scala b/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetRpcServiceSpec.scala index d0eaede250..056c4da45f 100644 --- a/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetRpcServiceSpec.scala +++ b/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetRpcServiceSpec.scala @@ -13,7 +13,7 @@ import io.iohk.ethereum.faucet.FaucetHandler.FaucetHandlerResponse.{ } import io.iohk.ethereum.faucet.FaucetStatus.WalletAvailable import io.iohk.ethereum.faucet.jsonrpc.FaucetDomain.{SendFundsRequest, StatusRequest} -import io.iohk.ethereum.faucet.{FaucetConfig, SupervisorConfig} +import io.iohk.ethereum.faucet.{FaucetConfig, RpcClientConfig, SupervisorConfig} import io.iohk.ethereum.jsonrpc.JsonRpcError import io.iohk.ethereum.testing.ActorsTesting.simpleAutoPilot import io.iohk.ethereum.{NormalPatience, WithActorSystemShutDown} @@ -133,12 +133,11 @@ class FaucetRpcServiceSpec txGasPrice = 10, txGasLimit = 20, txValue = 1, - rpcAddress = "", + rpcClient = RpcClientConfig(address = "", timeout = 10.seconds), keyStoreDir = "", minRequestInterval = 10.seconds, handlerTimeout = 10.seconds, responseTimeout = 10.seconds, - clientTimeout = 10.seconds, supervisor = mock[SupervisorConfig], shutdownTimeout = 15.seconds ) diff --git a/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/WalletServiceSpec.scala b/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/WalletServiceSpec.scala index 0e8d59a0a7..78bddde3e1 100644 --- a/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/WalletServiceSpec.scala +++ b/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/WalletServiceSpec.scala @@ -5,7 +5,7 @@ import java.security.SecureRandom import akka.util.ByteString import io.iohk.ethereum.crypto._ import io.iohk.ethereum.domain.{Address, Transaction} -import io.iohk.ethereum.faucet.{FaucetConfig, SupervisorConfig} +import io.iohk.ethereum.faucet.{FaucetConfig, RpcClientConfig, SupervisorConfig} import io.iohk.ethereum.keystore.KeyStore.DecryptionFailed import io.iohk.ethereum.keystore.{KeyStore, Wallet} import io.iohk.ethereum.network.p2p.messages.CommonMessages.SignedTransactions.SignedTransactionEnc @@ -76,12 +76,12 @@ class WalletServiceSpec extends AnyFlatSpec with Matchers with MockFactory { txGasPrice = 10, txGasLimit = 20, txValue = 1, - rpcAddress = "", + rpcClient = RpcClientConfig("", + timeout = 10.seconds), keyStoreDir = "", minRequestInterval = 10.seconds, handlerTimeout = 10.seconds, responseTimeout = 10.seconds, - clientTimeout = 10.seconds, supervisor = mock[SupervisorConfig], shutdownTimeout = 15.seconds ) diff --git a/src/universal/conf/faucet.conf b/src/universal/conf/faucet.conf index 83da9d4bcb..fafc63f17e 100644 --- a/src/universal/conf/faucet.conf +++ b/src/universal/conf/faucet.conf @@ -40,6 +40,9 @@ faucet { # null value indicates HTTPS is not being used # password-file = "tls/password" #} + + # Response time-out from rpc client resolve + timeout = 3.seconds } # How often can a single IP address send a request @@ -51,13 +54,10 @@ faucet { # Response time-out from actor resolve response-timeout = 1.seconds - # Response time-out from rpc client resolve - client-timeout = 3.seconds - # Supervisor with BackoffSupervisor pattern supervisor { min-backoff = 3.seconds - man-backoff = 30.seconds + max-backoff = 30.seconds random-factor = 0.2 auto-reset = 10.seconds attempts = 4 From 4cb222a93d5596d0f349836c2d9330ba97caa908 Mon Sep 17 00:00:00 2001 From: Maximiliano Biandratti Date: Thu, 3 Dec 2020 12:22:43 -0300 Subject: [PATCH 3/5] add test from timeout --- .../ethereum/faucet/jsonrpc/FaucetBuilder.scala | 6 +++++- .../ethereum/jsonrpc/client/RpcClient.scala | 6 ++++-- .../faucet/jsonrpc/WalletServiceSpec.scala | 17 ++++++++++++++--- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetBuilder.scala b/src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetBuilder.scala index 47888bfc78..7a89472683 100644 --- a/src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetBuilder.scala +++ b/src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetBuilder.scala @@ -37,7 +37,11 @@ trait FaucetRpcServiceBuilder { ) val walletRpcClient: WalletRpcClient = - new WalletRpcClient(faucetConfig.rpcClient.address, faucetConfig.rpcClient.timeout, () => sslContext("faucet.rpc-client")) + new WalletRpcClient( + faucetConfig.rpcClient.address, + faucetConfig.rpcClient.timeout, + () => sslContext("faucet.rpc-client") + ) val walletService = new WalletService(walletRpcClient, keyStore, faucetConfig) val faucetSupervisor: FaucetSupervisor = new FaucetSupervisor(walletService, faucetConfig, shutdown)(system) val faucetRpcService = new FaucetRpcService(faucetConfig) diff --git a/src/main/scala/io/iohk/ethereum/jsonrpc/client/RpcClient.scala b/src/main/scala/io/iohk/ethereum/jsonrpc/client/RpcClient.scala index f29126d344..187c08bfe1 100644 --- a/src/main/scala/io/iohk/ethereum/jsonrpc/client/RpcClient.scala +++ b/src/main/scala/io/iohk/ethereum/jsonrpc/client/RpcClient.scala @@ -71,11 +71,11 @@ abstract class RpcClient(node: Uri, timeout: Duration, getSSLContext: () => Eith .deferFuture(for { response <- Http().singleRequest(request, connectionContext, connectionPoolSettings) data <- Unmarshal(response.entity).to[String] - } yield parse(data).left.map(e => RpcClientError(e.message))) + } yield parse(data).left.map(e => ParserError(e.message))) .onErrorHandle { case ex: TcpIdleTimeoutException => log.error("RPC request timeout", ex) - Left(RpcClientError(s"RPC request timeout")) + Left(Timeout(s"RPC request timeout")) case ex: Throwable => log.error("RPC request failed", ex) Left(RpcClientError(s"RPC request failed: ${exceptionToString(ex)}")) @@ -111,5 +111,7 @@ object RpcClient { case class ParserError(msg: String) extends RpcError + case class Timeout(msg: String) extends RpcError + case class RpcClientError(msg: String) extends RpcError } diff --git a/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/WalletServiceSpec.scala b/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/WalletServiceSpec.scala index 78bddde3e1..d6ddd46dbc 100644 --- a/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/WalletServiceSpec.scala +++ b/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/WalletServiceSpec.scala @@ -6,6 +6,7 @@ import akka.util.ByteString import io.iohk.ethereum.crypto._ import io.iohk.ethereum.domain.{Address, Transaction} import io.iohk.ethereum.faucet.{FaucetConfig, RpcClientConfig, SupervisorConfig} +import io.iohk.ethereum.jsonrpc.client.RpcClient.Timeout import io.iohk.ethereum.keystore.KeyStore.DecryptionFailed import io.iohk.ethereum.keystore.{KeyStore, Wallet} import io.iohk.ethereum.network.p2p.messages.CommonMessages.SignedTransactions.SignedTransactionEnc @@ -21,7 +22,7 @@ import scala.concurrent.duration._ class WalletServiceSpec extends AnyFlatSpec with Matchers with MockFactory { - "Wallet Service" should "send a transaction" in new TestSetup { + "Wallet Service" should "send a transaction successfully when getNonce and sendTransaction successfully" in new TestSetup { val receivingAddress = Address("0x99") val currentNonce = 2 @@ -44,6 +45,17 @@ class WalletServiceSpec extends AnyFlatSpec with Matchers with MockFactory { } + it should "failure the transaction when get timeout of getNonce" in new TestSetup { + + val timeout = Timeout("timeout") + (walletRpcClient.getNonce _).expects(config.walletAddress).returning(Task.pure(Left(timeout))) + + val res = walletService.sendFunds(wallet, Address("0x99")).runSyncUnsafe() + + res shouldEqual Left(timeout) + + } + it should "get wallet successful" in new TestSetup { (mockKeyStore.unlockAccount _).expects(config.walletAddress, config.walletPassword).returning(Right(wallet)) @@ -76,8 +88,7 @@ class WalletServiceSpec extends AnyFlatSpec with Matchers with MockFactory { txGasPrice = 10, txGasLimit = 20, txValue = 1, - rpcClient = RpcClientConfig("", - timeout = 10.seconds), + rpcClient = RpcClientConfig("", timeout = 10.seconds), keyStoreDir = "", minRequestInterval = 10.seconds, handlerTimeout = 10.seconds, From 004116e37077feab7bf4137f4d7b1d611e536133 Mon Sep 17 00:00:00 2001 From: Maximiliano Biandratti Date: Thu, 3 Dec 2020 15:13:13 -0300 Subject: [PATCH 4/5] change hanlder exception from rpc client --- .../ethereum/jsonrpc/client/RpcClient.scala | 22 ++++++++++++------- .../faucet/jsonrpc/WalletServiceSpec.scala | 4 ++-- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/main/scala/io/iohk/ethereum/jsonrpc/client/RpcClient.scala b/src/main/scala/io/iohk/ethereum/jsonrpc/client/RpcClient.scala index 187c08bfe1..8a212afce8 100644 --- a/src/main/scala/io/iohk/ethereum/jsonrpc/client/RpcClient.scala +++ b/src/main/scala/io/iohk/ethereum/jsonrpc/client/RpcClient.scala @@ -8,6 +8,7 @@ import akka.http.scaladsl.model._ import akka.http.scaladsl.settings.{ClientConnectionSettings, ConnectionPoolSettings} import akka.http.scaladsl.unmarshalling.Unmarshal import akka.http.scaladsl.{ConnectionContext, Http, HttpsConnectionContext} +import akka.stream.StreamTcpException import akka.stream.scaladsl.TcpIdleTimeoutException import io.circe.generic.auto._ import io.circe.parser.parse @@ -72,13 +73,18 @@ abstract class RpcClient(node: Uri, timeout: Duration, getSSLContext: () => Eith response <- Http().singleRequest(request, connectionContext, connectionPoolSettings) data <- Unmarshal(response.entity).to[String] } yield parse(data).left.map(e => ParserError(e.message))) - .onErrorHandle { - case ex: TcpIdleTimeoutException => - log.error("RPC request timeout", ex) - Left(Timeout(s"RPC request timeout")) - case ex: Throwable => - log.error("RPC request failed", ex) - Left(RpcClientError(s"RPC request failed: ${exceptionToString(ex)}")) + .onErrorHandle { ex: Throwable => + ex match { + case _: TcpIdleTimeoutException => + log.error("RPC request", ex) + Left(ConnectionError(s"RPC request timeout")) + case _: StreamTcpException => + log.error("Connection not established", ex) + Left(ConnectionError(s"Connection not established")) + case _ => + log.error("RPC request failed", ex) + Left(RpcClientError("RPC request failed")) + } } } @@ -111,7 +117,7 @@ object RpcClient { case class ParserError(msg: String) extends RpcError - case class Timeout(msg: String) extends RpcError + case class ConnectionError(msg: String) extends RpcError case class RpcClientError(msg: String) extends RpcError } diff --git a/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/WalletServiceSpec.scala b/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/WalletServiceSpec.scala index d6ddd46dbc..a568797500 100644 --- a/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/WalletServiceSpec.scala +++ b/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/WalletServiceSpec.scala @@ -6,7 +6,7 @@ import akka.util.ByteString import io.iohk.ethereum.crypto._ import io.iohk.ethereum.domain.{Address, Transaction} import io.iohk.ethereum.faucet.{FaucetConfig, RpcClientConfig, SupervisorConfig} -import io.iohk.ethereum.jsonrpc.client.RpcClient.Timeout +import io.iohk.ethereum.jsonrpc.client.RpcClient.ConnectionError import io.iohk.ethereum.keystore.KeyStore.DecryptionFailed import io.iohk.ethereum.keystore.{KeyStore, Wallet} import io.iohk.ethereum.network.p2p.messages.CommonMessages.SignedTransactions.SignedTransactionEnc @@ -47,7 +47,7 @@ class WalletServiceSpec extends AnyFlatSpec with Matchers with MockFactory { it should "failure the transaction when get timeout of getNonce" in new TestSetup { - val timeout = Timeout("timeout") + val timeout = ConnectionError("timeout") (walletRpcClient.getNonce _).expects(config.walletAddress).returning(Task.pure(Left(timeout))) val res = walletService.sendFunds(wallet, Address("0x99")).runSyncUnsafe() From 51dabf71cb3664524a34aca2ab9c357601c55e06 Mon Sep 17 00:00:00 2001 From: Maximiliano Biandratti Date: Fri, 4 Dec 2020 12:32:27 -0300 Subject: [PATCH 5/5] change property name from actor communication margin --- src/main/scala/io/iohk/ethereum/faucet/FaucetConfig.scala | 4 ++-- .../io/iohk/ethereum/faucet/jsonrpc/FaucetRpcService.scala | 2 +- src/test/resources/application.conf | 2 +- .../iohk/ethereum/faucet/jsonrpc/FaucetRpcServiceSpec.scala | 2 +- .../io/iohk/ethereum/faucet/jsonrpc/WalletServiceSpec.scala | 2 +- src/universal/conf/faucet.conf | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/scala/io/iohk/ethereum/faucet/FaucetConfig.scala b/src/main/scala/io/iohk/ethereum/faucet/FaucetConfig.scala index a8beaa2f55..fc796c0281 100644 --- a/src/main/scala/io/iohk/ethereum/faucet/FaucetConfig.scala +++ b/src/main/scala/io/iohk/ethereum/faucet/FaucetConfig.scala @@ -36,7 +36,7 @@ case class FaucetConfig( keyStoreDir: String, minRequestInterval: FiniteDuration, handlerTimeout: FiniteDuration, - responseTimeout: FiniteDuration, + actorCommunicationMargin: FiniteDuration, supervisor: SupervisorConfig, shutdownTimeout: FiniteDuration ) @@ -54,7 +54,7 @@ object FaucetConfig { keyStoreDir = faucetConfig.getString("keystore-dir"), minRequestInterval = faucetConfig.getDuration("min-request-interval").toMillis.millis, handlerTimeout = faucetConfig.getDuration("handler-timeout").toMillis.millis, - responseTimeout = faucetConfig.getDuration("response-timeout").toMillis.millis, + actorCommunicationMargin = faucetConfig.getDuration("actor-communication-margin").toMillis.millis, supervisor = SupervisorConfig(faucetConfig), shutdownTimeout = faucetConfig.getDuration("shutdown-timeout").toMillis.millis ) diff --git a/src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetRpcService.scala b/src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetRpcService.scala index 61284271d2..34cdea0b64 100644 --- a/src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetRpcService.scala +++ b/src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetRpcService.scala @@ -16,7 +16,7 @@ class FaucetRpcService(config: FaucetConfig)(implicit system: ActorSystem) with FaucetHandlerSelector with Logger { - implicit lazy val actorTimeout: Timeout = Timeout(config.responseTimeout + config.rpcClient.timeout) + implicit lazy val actorTimeout: Timeout = Timeout(config.actorCommunicationMargin + config.rpcClient.timeout) def sendFunds(sendFundsRequest: SendFundsRequest): ServiceResponse[SendFundsResponse] = selectFaucetHandler() diff --git a/src/test/resources/application.conf b/src/test/resources/application.conf index 643b0fcbb9..ad196b8a26 100644 --- a/src/test/resources/application.conf +++ b/src/test/resources/application.conf @@ -163,7 +163,7 @@ faucet { handler-timeout = 2.seconds - response-timeout = 1.seconds + actor-communication-margin = 1.seconds supervisor { min-backoff = 3.seconds diff --git a/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetRpcServiceSpec.scala b/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetRpcServiceSpec.scala index 056c4da45f..deba174214 100644 --- a/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetRpcServiceSpec.scala +++ b/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetRpcServiceSpec.scala @@ -137,7 +137,7 @@ class FaucetRpcServiceSpec keyStoreDir = "", minRequestInterval = 10.seconds, handlerTimeout = 10.seconds, - responseTimeout = 10.seconds, + actorCommunicationMargin = 10.seconds, supervisor = mock[SupervisorConfig], shutdownTimeout = 15.seconds ) diff --git a/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/WalletServiceSpec.scala b/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/WalletServiceSpec.scala index a568797500..b1dcfee222 100644 --- a/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/WalletServiceSpec.scala +++ b/src/test/scala/io/iohk/ethereum/faucet/jsonrpc/WalletServiceSpec.scala @@ -92,7 +92,7 @@ class WalletServiceSpec extends AnyFlatSpec with Matchers with MockFactory { keyStoreDir = "", minRequestInterval = 10.seconds, handlerTimeout = 10.seconds, - responseTimeout = 10.seconds, + actorCommunicationMargin = 10.seconds, supervisor = mock[SupervisorConfig], shutdownTimeout = 15.seconds ) diff --git a/src/universal/conf/faucet.conf b/src/universal/conf/faucet.conf index fafc63f17e..4c1d590035 100644 --- a/src/universal/conf/faucet.conf +++ b/src/universal/conf/faucet.conf @@ -52,7 +52,7 @@ faucet { handler-timeout = 1.seconds # Response time-out from actor resolve - response-timeout = 1.seconds + actor-communication-margin = 1.seconds # Supervisor with BackoffSupervisor pattern supervisor {