Packages
erlcloud
2.2.0
3.8.3
3.8.2
3.8.1
3.7.6
3.7.4
3.7.3
3.7.2
3.7.1
3.7.0
3.6.8
3.6.7
3.6.5
3.6.4
3.6.3
3.6.2
3.6.1
3.6.0
3.5.16
3.5.15
3.5.14
3.5.13
3.5.12
3.5.11
3.5.10
3.5.9
3.5.8
3.5.7
3.5.6
3.5.5
3.5.4
3.5.3
3.5.2
3.5.1
3.5.0
3.4.5
3.4.3
3.4.1
3.4.0
3.3.9
3.3.8
3.3.7
3.3.6
3.3.5
3.3.4
3.3.3
3.3.2
3.3.1
3.3.0
3.2.18
3.2.17
3.2.16
3.2.15
3.2.14
3.2.13
3.2.12
3.2.11
3.2.10
3.2.7
3.2.6
3.2.5
3.2.4
3.2.3
3.2.2
3.2.1
3.2.0
3.1.17
3.1.16
3.1.14
3.1.13
3.1.12
3.1.11
3.1.9
3.1.8
3.1.7
3.1.6
3.1.5
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
2.2.16
2.2.15
2.2.14
2.2.13
2.2.12
2.2.11
2.2.10
2.2.9
2.2.8
2.2.7
2.2.6
2.2.5
2.2.4
2.2.2
2.2.1
2.2.0
2.1.0
2.0.5
2.0.4
2.0.3
2.0.0
0.13.10
0.13.9
0.13.8
0.13.6
0.13.5
0.13.4
0.13.3
0.13.2
0.13.0
0.12.0
0.11.0
0.9.2
0.9.2-rc.1
0.9.1
0.9.0
AWS APIs library for Erlang
Current section
Files
Jump to
Current section
Files
src/erlcloud_retry.erl
%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% @author Ransom Richardson <ransom@ransomr.net>
%% @doc
%%
%% Implementation of retry logic for AWS requests
%%
%% Currently only used for S3, but will be extended to other services in the furture.
%%
%% The pluggable retry function provides a way to customize the retry behavior, as well
%% as log and customize errors that are generated by erlcloud.
%%
%% @end
-module(erlcloud_retry).
-include("erlcloud.hrl").
-include("erlcloud_aws.hrl").
%% Helpers
-export([backoff/1,
no_retry/1,
default_retry/1
]).
-export_type([should_retry/0, retry_fun/0]).
-type should_retry() :: {retry | error, #aws_request{}}.
-type retry_fun() :: fun((#aws_request{}, non_neg_integer()) -> should_retry()).
%% Internal impl api
-export([request/3]).
%% Error returns maintained for backwards compatibility
-spec no_retry(#aws_request{}) -> {error, #aws_request{}}.
no_retry(Request) ->
{error, Request}.
%% Sleep after an attempt
-spec backoff(pos_integer()) -> ok.
backoff(1) -> ok;
backoff(Attempt) ->
timer:sleep(erlcloud_util:rand_uniform((1 bsl (Attempt - 1)) * 100)).
-spec default_retry(#aws_request{}) -> should_retry().
default_retry(#aws_request{should_retry = false} = Request) ->
{error, Request};
default_retry(#aws_request{attempt = Attempt} = Request) ->
backoff(Attempt),
{retry, Request}.
request(Config, #aws_request{attempt = 0} = Request, ResultFun) ->
MaxAttempts = Config#aws_config.retry_num,
request_and_retry(Config, ResultFun, {retry, Request}, MaxAttempts).
request_and_retry(_, _, {_, Request}, 0) ->
Request;
request_and_retry(_, _, {error, Request}, _) ->
Request;
request_and_retry(Config, ResultFun, {retry, Request}, MaxAttempts) ->
#aws_request{
attempt = Attempt,
uri = URI,
method = Method,
request_headers = Headers,
request_body = Body
} = Request,
Request2 = Request#aws_request{attempt = Attempt + 1},
RetryFun = Config#aws_config.retry,
Rsp = erlcloud_httpc:request(URI, Method, Headers, Body,
erlcloud_aws:get_timeout(Config), Config),
case Rsp of
{ok, {{Status, StatusLine}, ResponseHeaders, ResponseBody}} ->
Request3 = Request2#aws_request{
response_type = if Status >= 200, Status < 300 -> ok; true -> error end,
error_type = aws,
response_status = Status,
response_status_line = StatusLine,
response_headers = ResponseHeaders,
response_body = ResponseBody},
Request4 = ResultFun(Request3),
case Request4#aws_request.response_type of
ok ->
Request4;
error ->
request_and_retry(
Config,
ResultFun,
RetryFun(Request4),
MaxAttempts - 1)
end;
{error, Reason} ->
Request4 = Request2#aws_request{
response_type = error,
error_type = httpc,
httpc_error_reason = Reason},
request_and_retry(
Config,
ResultFun,
RetryFun(Request4),
MaxAttempts - 1)
end.