retry with backoff while publishing

This commit is contained in:
izharahmd 2020-09-13 18:22:48 +05:30
parent 1e870ce40b
commit fa0f859552
3 changed files with 55 additions and 2 deletions

View File

@ -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)
}

View File

@ -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 =>

View File

@ -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
}
}
}