RpcRetryException (#274)

* Created RpcRetryException for better error messages on RpcUtil timeouts

* De-duplicated RpcUtil

* Ran scalafmt

* Fixed an import and a version
This commit is contained in:
Nadav Kohen 2019-01-07 16:49:17 -06:00 committed by Chris Stewart
parent 1301336231
commit cc04732e41
7 changed files with 81 additions and 29 deletions

View file

@ -1,13 +1,14 @@
package org.bitcoins.rpc
package org.bitcoins.rpc.util
import java.io.PrintStream
import akka.actor.ActorSystem
import org.bitcoins.core.util.BitcoinSLogger
import org.bitcoins.rpc.client.BitcoindRpcClient
import scala.concurrent.duration.{DurationInt, FiniteDuration}
import scala.concurrent.{Await, Future, Promise}
trait RpcUtil extends BitcoinSLogger {
trait AsyncUtil extends BitcoinSLogger {
private def retryRunnable(
condition: => Boolean,
@ -38,10 +39,37 @@ trait RpcUtil extends BitcoinSLogger {
conditionF: () => Future[Boolean],
duration: FiniteDuration = 100.millis,
maxTries: Int = 50)(implicit system: ActorSystem): Future[Unit] = {
val stackTrace: Array[StackTraceElement] =
Thread.currentThread().getStackTrace
retryUntilSatisfiedWithCounter(conditionF = conditionF,
duration = duration,
maxTries = maxTries)
maxTries = maxTries,
stackTrace = stackTrace)
}
case class RpcRetryException(
message: String,
caller: Array[StackTraceElement])
extends Exception(message) {
override def printStackTrace(s: PrintStream): Unit = {
super.printStackTrace(s)
val indexOfLastBitcoinSOpt = caller.reverse.zipWithIndex
.find {
case (element, _) =>
element.getClassName.contains("bitcoins")
}
val indexOfLastRelevantElement =
indexOfLastBitcoinSOpt.map(_._2).getOrElse(0)
val relevantStackTrace =
caller.dropRight(indexOfLastRelevantElement).drop(1)
s.println("Called from:")
relevantStackTrace.foreach(element => s.println(s"\t$element"))
}
}
// Has a different name so that default values are permitted
@ -49,7 +77,9 @@ trait RpcUtil extends BitcoinSLogger {
conditionF: () => Future[Boolean],
duration: FiniteDuration,
counter: Int = 0,
maxTries: Int)(implicit system: ActorSystem): Future[Unit] = {
maxTries: Int,
stackTrace: Array[StackTraceElement])(
implicit system: ActorSystem): Future[Unit] = {
implicit val ec = system.dispatcher
@ -57,7 +87,7 @@ trait RpcUtil extends BitcoinSLogger {
if (condition) {
Future.successful(())
} else if (counter == maxTries) {
Future.failed(new RuntimeException("Condition timed out"))
Future.failed(RpcRetryException("Condition timed out", stackTrace))
} else {
val p = Promise[Boolean]()
val runnable = retryRunnable(condition, p)
@ -70,7 +100,8 @@ trait RpcUtil extends BitcoinSLogger {
retryUntilSatisfiedWithCounter(conditionF,
duration,
counter + 1,
maxTries)
maxTries,
stackTrace)
}
}
}
@ -116,22 +147,6 @@ trait RpcUtil extends BitcoinSLogger {
Await.result(f, overallTimeout)
}
def awaitServer(
server: BitcoindRpcClient,
duration: FiniteDuration = 1.seconds,
maxTries: Int = 50)(implicit system: ActorSystem): Unit = {
val f = () => server.isStarted
awaitCondition(f, duration, maxTries)
}
def awaitServerShutdown(
server: BitcoindRpcClient,
duration: FiniteDuration = 300.milliseconds,
maxTries: Int = 50)(implicit system: ActorSystem): Unit = {
val f = () => !server.isStarted
awaitCondition(f, duration, maxTries)
}
}
object RpcUtil extends RpcUtil
object AsyncUtil extends AsyncUtil

View file

@ -0,0 +1,27 @@
package org.bitcoins.rpc.util
import akka.actor.ActorSystem
import org.bitcoins.rpc.client.BitcoindRpcClient
import scala.concurrent.duration.{DurationInt, FiniteDuration}
trait RpcUtil extends AsyncUtil {
def awaitServer(
server: BitcoindRpcClient,
duration: FiniteDuration = 1.seconds,
maxTries: Int = 50)(implicit system: ActorSystem): Unit = {
val f = () => server.isStarted
awaitCondition(f, duration, maxTries)
}
def awaitServerShutdown(
server: BitcoindRpcClient,
duration: FiniteDuration = 300.milliseconds,
maxTries: Int = 50)(implicit system: ActorSystem): Unit = {
val f = () => !server.isStarted
awaitCondition(f, duration, maxTries)
}
}
object RpcUtil extends RpcUtil

View file

@ -7,6 +7,7 @@ import akka.testkit.TestKit
import org.bitcoins.core.currency.Bitcoins
import org.bitcoins.rpc.client.BitcoindRpcClient
import org.bitcoins.rpc.config.BitcoindInstance
import org.bitcoins.rpc.util.RpcUtil
import org.scalatest.{AsyncFlatSpec, BeforeAndAfterAll}
import scala.concurrent.Future

View file

@ -4,7 +4,9 @@ import java.io.File
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import org.bitcoins.rpc.util.RpcUtil.RpcRetryException
import org.bitcoins.rpc.client.BitcoindRpcClient
import org.bitcoins.rpc.util.RpcUtil
import org.scalatest.{AsyncFlatSpec, BeforeAndAfterAll}
import scala.concurrent.duration.DurationInt
@ -44,7 +46,7 @@ class RpcUtilTest extends AsyncFlatSpec with BeforeAndAfterAll {
}
it should "fail if condition is false" in {
recoverToSucceededIf[RuntimeException] {
recoverToSucceededIf[RpcRetryException] {
RpcUtil.retryUntilSatisfiedF(conditionF = () => Future.successful(false),
duration = 0.millis)
}
@ -59,7 +61,7 @@ class RpcUtilTest extends AsyncFlatSpec with BeforeAndAfterAll {
it should "fail if there is a delay and duration is zero" in {
val boolLater = trueLater(delay = 250)
recoverToSucceededIf[RuntimeException] {
recoverToSucceededIf[RpcRetryException] {
RpcUtil.retryUntilSatisfiedF(boolLaterDoneAndTrue(boolLater),
duration = 0.millis)
}
@ -71,7 +73,7 @@ class RpcUtilTest extends AsyncFlatSpec with BeforeAndAfterAll {
}
it should "timeout if condition is false" in {
assertThrows[RuntimeException] {
assertThrows[RpcRetryException] {
RpcUtil.awaitCondition(condition = () => false, duration = 0.millis)
}
}
@ -86,7 +88,7 @@ class RpcUtilTest extends AsyncFlatSpec with BeforeAndAfterAll {
it should "timeout if there is a delay and duration is zero" in {
val boolLater = trueLater(delay = 250)
assertThrows[RuntimeException] {
assertThrows[RpcRetryException] {
RpcUtil.awaitConditionF(boolLaterDoneAndTrue(boolLater),
duration = 0.millis)
}

View file

@ -18,7 +18,8 @@ import org.bitcoins.eclair.rpc.client.EclairRpcClient
import org.bitcoins.eclair.rpc.config.EclairInstance
import org.bitcoins.rpc.client.BitcoindRpcClient
import org.bitcoins.rpc.config.{BitcoindInstance, ZmqConfig}
import org.bitcoins.rpc.{BitcoindRpcTestUtil, RpcUtil}
import org.bitcoins.rpc.BitcoindRpcTestUtil
import org.bitcoins.rpc.util.RpcUtil
import scala.concurrent.Future
import scala.concurrent.duration.DurationInt

View file

@ -15,6 +15,7 @@ import org.bitcoins.rpc.config.{
BitcoindInstance,
ZmqConfig
}
import org.bitcoins.rpc.util.RpcUtil
import scala.collection.immutable.Map
import scala.collection.JavaConverters.{asScalaSet, mapAsJavaMap}

View file

@ -0,0 +1,5 @@
package org.bitcoins.util
trait AsyncUtil extends org.bitcoins.rpc.util.AsyncUtil
object AsyncUtil extends AsyncUtil