Packages
erlcloud
0.9.2
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, default_retry/2
]).
-export_type([should_retry/0, retry_fun/0]).
-type should_retry() :: {retry | error, #aws_request{}}.
-type retry_fun() :: fun((#aws_request{}) -> should_retry()).
%% Internal impl api
-export([request/3]).
%% Error returns maintained for backwards compatibility
-spec no_retry(#aws_request{}) -> should_retry().
no_retry(Request) ->
{error, Request}.
%% Sleep after an attempt
-spec backoff(pos_integer()) -> ok.
backoff(1) -> ok;
backoff(Attempt) ->
timer:sleep(random:uniform((1 bsl (Attempt - 1)) * 100)).
%% Currently matches DynamoDB retry
%% It's likely this is too many retries for other services
-define(NUM_ATTEMPTS, 10).
-spec default_retry(#aws_request{}) -> should_retry().
default_retry(Request) ->
default_retry(Request, ?NUM_ATTEMPTS).
-spec default_retry(#aws_request{}, integer()) -> should_retry().
default_retry(#aws_request{attempt = Attempt} = Request, MaxAttempts)
when Attempt >= MaxAttempts ->
{error, Request};
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) ->
request_and_retry(Config, ResultFun, {retry, Request}).
request_and_retry(_, _, {error, Request}) ->
Request;
request_and_retry(Config, ResultFun, {retry, Request}) ->
#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,
case erlcloud_httpc:request(URI, Method, Headers, Body, Config#aws_config.timeout, Config) 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))
end;
{error, Reason} ->
Request4 = Request2#aws_request{
response_type = error,
error_type = httpc,
httpc_error_reason = Reason},
request_and_retry(Config, ResultFun, RetryFun(Request4))
end.