Packages
erlcloud
3.3.8
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,
only_http_errors/1,
lambda_fun_errors/1
]).
-export_type([should_retry/0, retry_fun/0, response_type_fun/0]).
-type should_retry() :: {retry | error, #aws_request{}}.
-type retry_fun() :: fun((#aws_request{}) -> should_retry()).
-type response_type_fun() :: fun((#aws_request{}) -> ok | error).
%% 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).
-spec only_http_errors(#aws_request{}) -> ok | error.
only_http_errors(#aws_request{response_status=Status})
when Status >= 200, Status < 300
->
ok;
only_http_errors(_) ->
error.
-spec lambda_fun_errors(#aws_request{}) -> ok | error.
lambda_fun_errors(#aws_request{response_status=Status, response_headers=ResponseHeaders})
when Status >= 200, Status < 300
->
case lists:keymember("x-amz-function-error", 1, ResponseHeaders) of
true ->
error;
false ->
ok
end;
lambda_fun_errors(_) ->
error.
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,
ResponseTypeFun = Config#aws_config.retry_response_type,
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{
error_type = aws,
response_status = Status,
response_status_line = StatusLine,
response_headers = ResponseHeaders,
response_body = ResponseBody},
ResponseType = ResponseTypeFun(Request3),
Request4 = ResultFun(Request3#aws_request{response_type=ResponseType}),
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.