環境
- Julia 1.3.0
retry
ある処理がエラーになってしまったとき、時間を置いて再度実行したらエラーが出ないことはよく有ることです。例えばサーバーにうまく接続できないとか。 そんなときに使うのが retry 機構です。retry 機構を使うことでエラー時の再実行を容易にすることが出来ます。
Julia の retry 機構は以下のようなインターフェースになっています。
retry(f; delays=ExponentialBackOff(), check=nothing) -> Function
関数 f
が実際に実行される処理、delays
が再実行する間隔と回数、check
はどのエラーによって再実行するかをコントロールするときに指定するもの(っぽい)です。
https://docs.julialang.org/en/v1/base/base/#Base.retry
retry()
の返り値は第1引数の関数 f
に retry 機構を付与したものと考えればよいと思います。
以下の例は1秒間隔で最大3回関数 f
を実行
julia> x = 0 0 julia> function f() global x += 1 if x < 3 println("x = ", x) error() else return println("Hello World") end end f (generic function with 1 method) julia> retry(f, delays=[1, 1, 1])() x = 1 x = 2 Hello World
do ... end
ブロックを使って以下のようにも書けます。ある処理の一部分に retry 機構を付けたい場合はこちらのほうが便利かもしれません。
julia> retry(delays=[1, 1, 1]) do global x += 1 if x < 3 println("x = ", x) error() else return println("Hello World") end end() # retry の返り値は関数なので最後に `([args...])` を忘れずに x = 1 x = 2 Hello World
実用的にはこんな感じでしょうか。
retry(delays=[1, 1, 1]) do url HTTP.request("GET", url) end("http://httpbin.org/ip")
Exponential BackOff
待つ間隔を指数関数的に増やす場合は ExponentialBackOff
を使います。
ExponentialBackOff(; n=1, first_delay=0.05, max_delay=10.0, factor=5.0, jitter=0.1)
https://docs.julialang.org/en/v1/base/base/#Base.ExponentialBackOff
n
が retry する回数で、k回目の retry 時の待ち時間は以下の式で与えられます。
ここで は 区間 の一様乱数
AWS でのエラーの再試行とエクスポネンシャルバックオフ - AWS 全般のリファレンス
julia> ExponentialBackOff(n=5, first_delay=0.05, max_delay=10, jitter=0) |> collect 5-element Array{Float64,1}: 0.05 0.25 1.25 6.25 10.0 julia> [0.05 * 5^(i-1) for i in 1:5] 5-element Array{Float64,1}: 0.05 0.25 1.25 6.25 31.25
Document の但し書きについて
公式 Document を見ると
Before Julia 1.2 this signature was restricted to
f::Function
.
という但し書きがあります。ようするに retry
の定義が 1.2 より前では
retry(f::Function; delays=ExponentialBackOff(), check=nothing) -> Function
だったということですが、
Return an anonymous function that calls function f.
とあるのだから、f::Function
で一見問題ない気がします。
issue や PR をちゃんと見ていないので正確ではありませんが、おそらく Function-like objects などを取れるようになったということを言いたいのかなと私は思いました。
https://docs.julialang.org/en/v1/manual/methods/#Function-like-objects-1
julia> struct Polynomial{R} coeffs::Vector{R} end julia> function (p::Polynomial)(x) v = p.coeffs[end] for i = (length(p.coeffs)-1):-1:1 v = v*x + p.coeffs[i] end return v end julia> (p::Polynomial)() = p(5) julia> p = Polynomial([1,10,100]) Polynomial{Int64}([1, 10, 100]) # Julia 1.2 未満 julia> retry(p) ERROR: MethodError: no method matching retry(::Polynomial{Int64}) Closest candidates are: retry(::Function; delays, check) at error.jl:198 Stacktrace: [1] top-level scope at none:0 # Julia 1.2 以降 julia> retry(p) #50 (generic function with 1 method)