mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-03 18:47:38 +01:00
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:
parent
1301336231
commit
cc04732e41
7 changed files with 81 additions and 29 deletions
|
@ -1,13 +1,14 @@
|
||||||
package org.bitcoins.rpc
|
package org.bitcoins.rpc.util
|
||||||
|
|
||||||
|
import java.io.PrintStream
|
||||||
|
|
||||||
import akka.actor.ActorSystem
|
import akka.actor.ActorSystem
|
||||||
import org.bitcoins.core.util.BitcoinSLogger
|
import org.bitcoins.core.util.BitcoinSLogger
|
||||||
import org.bitcoins.rpc.client.BitcoindRpcClient
|
|
||||||
|
|
||||||
import scala.concurrent.duration.{DurationInt, FiniteDuration}
|
import scala.concurrent.duration.{DurationInt, FiniteDuration}
|
||||||
import scala.concurrent.{Await, Future, Promise}
|
import scala.concurrent.{Await, Future, Promise}
|
||||||
|
|
||||||
trait RpcUtil extends BitcoinSLogger {
|
trait AsyncUtil extends BitcoinSLogger {
|
||||||
|
|
||||||
private def retryRunnable(
|
private def retryRunnable(
|
||||||
condition: => Boolean,
|
condition: => Boolean,
|
||||||
|
@ -38,10 +39,37 @@ trait RpcUtil extends BitcoinSLogger {
|
||||||
conditionF: () => Future[Boolean],
|
conditionF: () => Future[Boolean],
|
||||||
duration: FiniteDuration = 100.millis,
|
duration: FiniteDuration = 100.millis,
|
||||||
maxTries: Int = 50)(implicit system: ActorSystem): Future[Unit] = {
|
maxTries: Int = 50)(implicit system: ActorSystem): Future[Unit] = {
|
||||||
|
val stackTrace: Array[StackTraceElement] =
|
||||||
|
Thread.currentThread().getStackTrace
|
||||||
|
|
||||||
retryUntilSatisfiedWithCounter(conditionF = conditionF,
|
retryUntilSatisfiedWithCounter(conditionF = conditionF,
|
||||||
duration = duration,
|
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
|
// Has a different name so that default values are permitted
|
||||||
|
@ -49,7 +77,9 @@ trait RpcUtil extends BitcoinSLogger {
|
||||||
conditionF: () => Future[Boolean],
|
conditionF: () => Future[Boolean],
|
||||||
duration: FiniteDuration,
|
duration: FiniteDuration,
|
||||||
counter: Int = 0,
|
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
|
implicit val ec = system.dispatcher
|
||||||
|
|
||||||
|
@ -57,7 +87,7 @@ trait RpcUtil extends BitcoinSLogger {
|
||||||
if (condition) {
|
if (condition) {
|
||||||
Future.successful(())
|
Future.successful(())
|
||||||
} else if (counter == maxTries) {
|
} else if (counter == maxTries) {
|
||||||
Future.failed(new RuntimeException("Condition timed out"))
|
Future.failed(RpcRetryException("Condition timed out", stackTrace))
|
||||||
} else {
|
} else {
|
||||||
val p = Promise[Boolean]()
|
val p = Promise[Boolean]()
|
||||||
val runnable = retryRunnable(condition, p)
|
val runnable = retryRunnable(condition, p)
|
||||||
|
@ -70,7 +100,8 @@ trait RpcUtil extends BitcoinSLogger {
|
||||||
retryUntilSatisfiedWithCounter(conditionF,
|
retryUntilSatisfiedWithCounter(conditionF,
|
||||||
duration,
|
duration,
|
||||||
counter + 1,
|
counter + 1,
|
||||||
maxTries)
|
maxTries,
|
||||||
|
stackTrace)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,22 +147,6 @@ trait RpcUtil extends BitcoinSLogger {
|
||||||
|
|
||||||
Await.result(f, overallTimeout)
|
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
|
27
rpc/src/main/scala/org/bitcoins/rpc/util/RpcUtil.scala
Normal file
27
rpc/src/main/scala/org/bitcoins/rpc/util/RpcUtil.scala
Normal 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
|
|
@ -7,6 +7,7 @@ import akka.testkit.TestKit
|
||||||
import org.bitcoins.core.currency.Bitcoins
|
import org.bitcoins.core.currency.Bitcoins
|
||||||
import org.bitcoins.rpc.client.BitcoindRpcClient
|
import org.bitcoins.rpc.client.BitcoindRpcClient
|
||||||
import org.bitcoins.rpc.config.BitcoindInstance
|
import org.bitcoins.rpc.config.BitcoindInstance
|
||||||
|
import org.bitcoins.rpc.util.RpcUtil
|
||||||
import org.scalatest.{AsyncFlatSpec, BeforeAndAfterAll}
|
import org.scalatest.{AsyncFlatSpec, BeforeAndAfterAll}
|
||||||
|
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
|
|
|
@ -4,7 +4,9 @@ import java.io.File
|
||||||
|
|
||||||
import akka.actor.ActorSystem
|
import akka.actor.ActorSystem
|
||||||
import akka.stream.ActorMaterializer
|
import akka.stream.ActorMaterializer
|
||||||
|
import org.bitcoins.rpc.util.RpcUtil.RpcRetryException
|
||||||
import org.bitcoins.rpc.client.BitcoindRpcClient
|
import org.bitcoins.rpc.client.BitcoindRpcClient
|
||||||
|
import org.bitcoins.rpc.util.RpcUtil
|
||||||
import org.scalatest.{AsyncFlatSpec, BeforeAndAfterAll}
|
import org.scalatest.{AsyncFlatSpec, BeforeAndAfterAll}
|
||||||
|
|
||||||
import scala.concurrent.duration.DurationInt
|
import scala.concurrent.duration.DurationInt
|
||||||
|
@ -44,7 +46,7 @@ class RpcUtilTest extends AsyncFlatSpec with BeforeAndAfterAll {
|
||||||
}
|
}
|
||||||
|
|
||||||
it should "fail if condition is false" in {
|
it should "fail if condition is false" in {
|
||||||
recoverToSucceededIf[RuntimeException] {
|
recoverToSucceededIf[RpcRetryException] {
|
||||||
RpcUtil.retryUntilSatisfiedF(conditionF = () => Future.successful(false),
|
RpcUtil.retryUntilSatisfiedF(conditionF = () => Future.successful(false),
|
||||||
duration = 0.millis)
|
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 {
|
it should "fail if there is a delay and duration is zero" in {
|
||||||
val boolLater = trueLater(delay = 250)
|
val boolLater = trueLater(delay = 250)
|
||||||
recoverToSucceededIf[RuntimeException] {
|
recoverToSucceededIf[RpcRetryException] {
|
||||||
RpcUtil.retryUntilSatisfiedF(boolLaterDoneAndTrue(boolLater),
|
RpcUtil.retryUntilSatisfiedF(boolLaterDoneAndTrue(boolLater),
|
||||||
duration = 0.millis)
|
duration = 0.millis)
|
||||||
}
|
}
|
||||||
|
@ -71,7 +73,7 @@ class RpcUtilTest extends AsyncFlatSpec with BeforeAndAfterAll {
|
||||||
}
|
}
|
||||||
|
|
||||||
it should "timeout if condition is false" in {
|
it should "timeout if condition is false" in {
|
||||||
assertThrows[RuntimeException] {
|
assertThrows[RpcRetryException] {
|
||||||
RpcUtil.awaitCondition(condition = () => false, duration = 0.millis)
|
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 {
|
it should "timeout if there is a delay and duration is zero" in {
|
||||||
val boolLater = trueLater(delay = 250)
|
val boolLater = trueLater(delay = 250)
|
||||||
assertThrows[RuntimeException] {
|
assertThrows[RpcRetryException] {
|
||||||
RpcUtil.awaitConditionF(boolLaterDoneAndTrue(boolLater),
|
RpcUtil.awaitConditionF(boolLaterDoneAndTrue(boolLater),
|
||||||
duration = 0.millis)
|
duration = 0.millis)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,8 @@ import org.bitcoins.eclair.rpc.client.EclairRpcClient
|
||||||
import org.bitcoins.eclair.rpc.config.EclairInstance
|
import org.bitcoins.eclair.rpc.config.EclairInstance
|
||||||
import org.bitcoins.rpc.client.BitcoindRpcClient
|
import org.bitcoins.rpc.client.BitcoindRpcClient
|
||||||
import org.bitcoins.rpc.config.{BitcoindInstance, ZmqConfig}
|
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.Future
|
||||||
import scala.concurrent.duration.DurationInt
|
import scala.concurrent.duration.DurationInt
|
||||||
|
|
|
@ -15,6 +15,7 @@ import org.bitcoins.rpc.config.{
|
||||||
BitcoindInstance,
|
BitcoindInstance,
|
||||||
ZmqConfig
|
ZmqConfig
|
||||||
}
|
}
|
||||||
|
import org.bitcoins.rpc.util.RpcUtil
|
||||||
|
|
||||||
import scala.collection.immutable.Map
|
import scala.collection.immutable.Map
|
||||||
import scala.collection.JavaConverters.{asScalaSet, mapAsJavaMap}
|
import scala.collection.JavaConverters.{asScalaSet, mapAsJavaMap}
|
||||||
|
|
5
testkit/src/main/scala/org/bitcoins/util/AsyncUtil.scala
Normal file
5
testkit/src/main/scala/org/bitcoins/util/AsyncUtil.scala
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package org.bitcoins.util
|
||||||
|
|
||||||
|
trait AsyncUtil extends org.bitcoins.rpc.util.AsyncUtil
|
||||||
|
|
||||||
|
object AsyncUtil extends AsyncUtil
|
Loading…
Add table
Reference in a new issue