mirror of https://github.com/sbt/sbt.git
retry with backoff while publishing
This commit is contained in:
parent
1e870ce40b
commit
fa0f859552
|
|
@ -60,4 +60,6 @@ object LMSysProp {
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy val useGigahorse: Boolean = getOrFalse("sbt.gigahorse")
|
lazy val useGigahorse: Boolean = getOrFalse("sbt.gigahorse")
|
||||||
|
lazy val maxPublishAttempts: Int = java.lang.Integer.getInteger("sbt.repository.publish.attempts", 3)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import sbt.librarymanagement.{ ModuleDescriptorConfiguration => InlineConfigurat
|
||||||
import syntax._
|
import syntax._
|
||||||
import InternalDefaults._
|
import InternalDefaults._
|
||||||
import UpdateClassifiersUtil._
|
import UpdateClassifiersUtil._
|
||||||
|
import sbt.internal.librarymanagement.IvyUtil.TransientNetworkException
|
||||||
|
|
||||||
object IvyActions {
|
object IvyActions {
|
||||||
|
|
||||||
|
|
@ -497,8 +498,14 @@ object IvyActions {
|
||||||
checkFilesPresent(artifacts)
|
checkFilesPresent(artifacts)
|
||||||
try {
|
try {
|
||||||
resolver.beginPublishTransaction(module.getModuleRevisionId(), overwrite);
|
resolver.beginPublishTransaction(module.getModuleRevisionId(), overwrite);
|
||||||
for ((artifact, file) <- artifacts)
|
artifacts.foreach {
|
||||||
resolver.publish(artifact, file, overwrite)
|
case (artifact, file) =>
|
||||||
|
IvyUtil.retryWithBackoff(
|
||||||
|
resolver.publish(artifact, file, overwrite),
|
||||||
|
TransientNetworkException.apply,
|
||||||
|
maxAttempts = LMSysProp.maxPublishAttempts
|
||||||
|
)
|
||||||
|
}
|
||||||
resolver.commitPublishTransaction()
|
resolver.commitPublishTransaction()
|
||||||
} catch {
|
} catch {
|
||||||
case e: Throwable =>
|
case e: Throwable =>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,50 @@
|
||||||
package sbt.internal.librarymanagement
|
package sbt.internal.librarymanagement
|
||||||
|
|
||||||
|
import java.io.IOException
|
||||||
|
import java.net.{ SocketException, SocketTimeoutException }
|
||||||
|
|
||||||
|
import scala.annotation.tailrec
|
||||||
|
import scala.util.{ Failure, Success, Try }
|
||||||
|
|
||||||
private[sbt] object IvyUtil {
|
private[sbt] object IvyUtil {
|
||||||
def separate[A, B](l: Seq[Either[A, B]]): (Seq[A], Seq[B]) =
|
def separate[A, B](l: Seq[Either[A, B]]): (Seq[A], Seq[B]) =
|
||||||
(l.flatMap(_.left.toOption), l.flatMap(_.right.toOption))
|
(l.flatMap(_.left.toOption), l.flatMap(_.right.toOption))
|
||||||
|
|
||||||
|
@tailrec
|
||||||
|
final def retryWithBackoff[T](
|
||||||
|
f: => T,
|
||||||
|
predicate: Throwable => Boolean,
|
||||||
|
maxAttempts: Int,
|
||||||
|
retry: Int = 0
|
||||||
|
): T = {
|
||||||
|
// Using Try helps in catching NonFatal exceptions only
|
||||||
|
Try {
|
||||||
|
f
|
||||||
|
} match {
|
||||||
|
case Success(value) => value
|
||||||
|
case Failure(e) if predicate(e) && retry < (maxAttempts - 1) =>
|
||||||
|
// max 8s backoff
|
||||||
|
val backoff = math.min(math.pow(2d, retry.toDouble).toLong * 1000L, 8000L)
|
||||||
|
Thread.sleep(backoff)
|
||||||
|
retryWithBackoff(f, predicate, maxAttempts, retry + 1)
|
||||||
|
case Failure(e) => throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object TransientNetworkException {
|
||||||
|
private val _r = """.*HTTP response code: (503|429).*""".r
|
||||||
|
|
||||||
|
@inline private def check(s: String): Boolean = {
|
||||||
|
if (s == null) return false
|
||||||
|
|
||||||
|
_r.pattern.matcher(s).matches()
|
||||||
|
}
|
||||||
|
|
||||||
|
def apply(t: Throwable): Boolean = t match {
|
||||||
|
case _: SocketException | _: SocketTimeoutException => true
|
||||||
|
case e: IOException if check(e.getMessage) => true
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue