From 393b7b2bee98564496223c84f6228cfd4c0b2ecc Mon Sep 17 00:00:00 2001 From: mar Date: Wed, 11 Jan 2023 15:35:31 +0100 Subject: [PATCH 1/2] fix for issue #988 --> seemles highly customizable retry setup --- src/clientlayers/RetryRequest.jl | 39 ++++++++++++++++---------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/src/clientlayers/RetryRequest.jl b/src/clientlayers/RetryRequest.jl index 8fa5850ef..f587d6855 100644 --- a/src/clientlayers/RetryRequest.jl +++ b/src/clientlayers/RetryRequest.jl @@ -3,9 +3,7 @@ module RetryRequest using Sockets, LoggingExtras, MbedTLS, OpenSSL using ..IOExtras, ..Messages, ..Strings, ..ExceptionRequest, ..Exceptions -export retrylayer - -FALSE(x...) = false +export retrylayer, retry_check, isrecoverable """ retrylayer(handler) -> handler @@ -21,13 +19,12 @@ e.g. `Sockets.DNSError`, `Base.EOFError` and `HTTP.StatusError` (if status is `5xx`). """ function retrylayer(handler) - return function(req::Request; retry::Bool=true, retries::Int=4, - retry_delays::ExponentialBackOff=ExponentialBackOff(n = retries, factor=3.0), retry_check=FALSE, - retry_non_idempotent::Bool=false, kw...) - if !retry || retries == 0 - # no retry - return handler(req; kw...) - end + return function(req::Request; retries::Int=4, retry::Bool=retries>0, + retry_delays=ExponentialBackOff(n=retries, factor=3.0), retry_check=retry_check, + retry_non_idempotent::Bool=false, kw... + ) + retry || return handler(req; kw...) # no retry + retries = length(retry_delays) req.context[:allow_retries] = true req.context[:retryattempt] = 0 if retry_non_idempotent @@ -39,16 +36,10 @@ function retrylayer(handler) req_body_is_marked = true mark(req.body) end - retryattempt = Ref(0) - retry_request = Base.retry(handler, - delays=retry_delays, - check=(s, ex) -> begin - retryattempt[] += 1 - req.context[:retryattempt] = retryattempt[] - retry = retryable(req) || retryablebody(req) && _retry_check(s, ex, req, retry_check) - if retryattempt[] == retries - req.context[:retrylimitreached] = true - end + retry_request = Base.retry(handler, delays=retry_delays, + check = (s, ex) -> begin + req.context[:retrylimitreached] = (req.context[:retryattempt] += 1) > retries + retry = retryable(req) && _retry_check(s, ex, req, retry_check) if retry @debugv 1 "🔄 Retry $ex: $(sprintcompact(req))" reset!(req.response) @@ -72,6 +63,14 @@ function _retry_check(s, ex, req, check) resp_body = get(req.context, :response_body, nothing) return check(s, ex, req, resp_body !== nothing ? resp : nothing, resp_body) end +retry_check(s, ex, x...) = isrecoverable(ex) +isrecoverable(e) = false +isrecoverable(e::Union{Base.EOFError, Base.IOError, MbedTLS.MbedException, OpenSSL.OpenSSLError}) = true +isrecoverable(e::ArgumentError) = e.msg == "stream is closed or unusable" +isrecoverable(e::Sockets.DNSError) = true +isrecoverable(e::ConnectError) = true +isrecoverable(e::RequestError) = isrecoverable(e.error) +isrecoverable(e::StatusError) = retryable(e.status) function no_retry_reason(ex, req) buf = IOBuffer() From 8d9cb3ae563919c204368a5dd925ab637ece7118 Mon Sep 17 00:00:00 2001 From: mar Date: Thu, 12 Jan 2023 14:31:44 +0100 Subject: [PATCH 2/2] adapted isrecoverable to latest master --- src/clientlayers/RetryRequest.jl | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/clientlayers/RetryRequest.jl b/src/clientlayers/RetryRequest.jl index f587d6855..d6e1275d0 100644 --- a/src/clientlayers/RetryRequest.jl +++ b/src/clientlayers/RetryRequest.jl @@ -3,7 +3,7 @@ module RetryRequest using Sockets, LoggingExtras, MbedTLS, OpenSSL using ..IOExtras, ..Messages, ..Strings, ..ExceptionRequest, ..Exceptions -export retrylayer, retry_check, isrecoverable +export retrylayer, retry_check, isrecoverable, isstatuserror """ retrylayer(handler) -> handler @@ -58,19 +58,16 @@ function retrylayer(handler) end end +isstatuserror(e, status) = e isa HTTP.StatusError && e.status in status +isrecoverable(ex) = true +isrecoverable(ex::StatusError) = retryable(ex.status) +retry_check(s, ex, x...) = isrecoverable(ex) + function _retry_check(s, ex, req, check) resp = req.response resp_body = get(req.context, :response_body, nothing) return check(s, ex, req, resp_body !== nothing ? resp : nothing, resp_body) end -retry_check(s, ex, x...) = isrecoverable(ex) -isrecoverable(e) = false -isrecoverable(e::Union{Base.EOFError, Base.IOError, MbedTLS.MbedException, OpenSSL.OpenSSLError}) = true -isrecoverable(e::ArgumentError) = e.msg == "stream is closed or unusable" -isrecoverable(e::Sockets.DNSError) = true -isrecoverable(e::ConnectError) = true -isrecoverable(e::RequestError) = isrecoverable(e.error) -isrecoverable(e::StatusError) = retryable(e.status) function no_retry_reason(ex, req) buf = IOBuffer()