Packages
erlcloud
0.9.2-rc.1
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.