Skip to content

Commit

Permalink
Custom ResponseClassifier to retry all 502,503,504 (#27)
Browse files Browse the repository at this point in the history
* Retry all 502,503,504

* Add comments
  • Loading branch information
erdody authored Jul 18, 2019
1 parent 45e1257 commit 9670303
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ io.buoyant.linkerd.protocol.http.RetryableIdempotent5XXInitializer
io.buoyant.linkerd.protocol.http.RetryableRead5XXInitializer
io.buoyant.linkerd.protocol.http.AllSuccessfulInitializer
io.buoyant.linkerd.protocol.http.RetryableAll5XXInitializer
io.buoyant.linkerd.protocol.http.RetryableAllGatewayErrorInitializer
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,39 @@ object ResponseClassifiers {
}
}

/**
* Matches Gateway error responses (502, 503, 504), which usually are safe
* to be retried independently of the request method
*/
object GatewayErrorResponses {

object Failure {
def unapply(rsp: Response): Boolean = rsp.status match {
case Status.BadGateway | Status.ServiceUnavailable | Status.GatewayTimeout => true
case _ => false
}

// There are probably some (linkerd-generated) failures that aren't
// really retryable... For now just check if it's a failure.
object Retryable {
def unapply(rsp: Response): Boolean = Failure.unapply(rsp)
}
}
}

object RetryableGatewayErrorResult {
private[this] val retryableThrow: PartialFunction[Try[Nothing], Boolean] =
TimeoutAndWriteExceptionsOnly.orElse(ChannelClosedExceptionsOnly)
.orElse(FramingExceptionsOnly)
.orElse { case _ => false }

def unapply(rsp: Try[Any]): Boolean = rsp match {
case Return(GatewayErrorResponses.Failure.Retryable()) => true
case Throw(e) => retryableThrow(Throw(e))
case _ => false
}
}

/**
* Classifies 5XX responses as failures. If the method is idempotent
* (as described by RFC2616), it is classified as retryable.
Expand All @@ -107,6 +140,14 @@ object ResponseClassifiers {
case ReqRep(Requests.All(), RetryableResult()) => ResponseClass.RetryableFailure
}

/**
* Classifies 502,503 and 504 responses as retryable failures, for all request methods.
*/
val RetryableAllGatewayErrorFailures: ResponseClassifier =
ResponseClassifier.named("RetryableAllGatewayErrorFailures") {
case ReqRep(Requests.All(), RetryableGatewayErrorResult()) => ResponseClass.RetryableFailure
}

/**
* Classifies 5XX responses as failures. If the method is a read
* operation, it is classified as retryable.
Expand Down Expand Up @@ -171,6 +212,17 @@ class RetryableAll5XXInitializer extends ResponseClassifierInitializer {

object RetryableAll5XXInitializer extends RetryableAll5XXInitializer

class RetryableAllGatewayErrorConfig extends ResponseClassifierConfig {
def mk: ResponseClassifier = ResponseClassifiers.RetryableAllGatewayErrorFailures
}

class RetryableAllGatewayErrorInitializer extends ResponseClassifierInitializer {
val configClass = classOf[RetryableAllGatewayErrorConfig]
override val configId = "io.l5d.http.retryableAllGatewayError"
}

object RetryableAllGatewayErrorInitializer extends RetryableAllGatewayErrorInitializer

class RetryableRead5XXConfig extends ResponseClassifierConfig {
def mk: ResponseClassifier = ResponseClassifiers.RetryableReadFailures
}
Expand Down
2 changes: 1 addition & 1 deletion project/Base.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ object Base {
class Base extends Build {
import Base._

val headVersion = "1.5.2-medallia-1.10.0"
val headVersion = "1.5.2-medallia-1.11.0"
val openJdkVersion = "8u151"
val openJ9Version = "jdk8u162-b12_openj9-0.8.0"

Expand Down

0 comments on commit 9670303

Please sign in to comment.