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 maxPublishAttempts: Int = java.lang.Integer.getInteger("sbt.repository.publish.attempts", 3)
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import sbt.librarymanagement.{ ModuleDescriptorConfiguration => InlineConfigurat
|
|||
import syntax._
|
||||
import InternalDefaults._
|
||||
import UpdateClassifiersUtil._
|
||||
import sbt.internal.librarymanagement.IvyUtil.TransientNetworkException
|
||||
|
||||
object IvyActions {
|
||||
|
||||
|
|
@ -497,8 +498,14 @@ object IvyActions {
|
|||
checkFilesPresent(artifacts)
|
||||
try {
|
||||
resolver.beginPublishTransaction(module.getModuleRevisionId(), overwrite);
|
||||
for ((artifact, file) <- artifacts)
|
||||
resolver.publish(artifact, file, overwrite)
|
||||
artifacts.foreach {
|
||||
case (artifact, file) =>
|
||||
IvyUtil.retryWithBackoff(
|
||||
resolver.publish(artifact, file, overwrite),
|
||||
TransientNetworkException.apply,
|
||||
maxAttempts = LMSysProp.maxPublishAttempts
|
||||
)
|
||||
}
|
||||
resolver.commitPublishTransaction()
|
||||
} catch {
|
||||
case e: Throwable =>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,50 @@
|
|||
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 {
|
||||
def separate[A, B](l: Seq[Either[A, B]]): (Seq[A], Seq[B]) =
|
||||
(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