Skip to content

Commit 30e62be

Browse files
committed
Add max_requests pool option
Pools accept a max_requests option (default infinity), passed through to shackle 0.8.0's client option of the same name: the connection is closed and reopened after that many requests, capping how long a keepalive connection is reused. The eunit HTTP fixture counts accepted connections so the test can assert that a pool_size 1 / max_requests 2 pool recycles instead of holding one connection. Closes #4
1 parent 292c556 commit 30e62be

6 files changed

Lines changed: 36 additions & 0 deletions

File tree

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,13 @@ ok
7272
<td>1024</td>
7373
<td>maximum number of concurrent requests per connection</td>
7474
</tr>
75+
<tr>
76+
<td>max_requests</td>
77+
<td>pos_integer() | infinity</td>
78+
<td>infinity</td>
79+
<td>number of requests after which a connection is closed and
80+
reopened</td>
81+
</tr>
7582
<tr>
7683
<td>pool_size</td>
7784
<td>pos_integer()</td>

include/buoy.hrl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
-type hostname() :: binary().
4646
-type method() :: get | head | post | put | {custom, binary()}.
4747
-type option() :: {backlog_size, pos_integer()} |
48+
{max_requests, pos_integer() | infinity} |
4849
{pool_size, pos_integer()} |
4950
{pool_strategy, random | round_robin} |
5051
{protocol, shackle_socket | shackle_tcp} |

include/buoy_internal.hrl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
-define(DEFAULT_BODY, undefined).
1414
-define(DEFAULT_HEADERS, []).
1515
-define(DEFAULT_IP, "127.0.0.1").
16+
-define(DEFAULT_MAX_REQUESTS, infinity).
1617
-define(DEFAULT_PID, self()).
1718
-define(DEFAULT_POOL_OPTIONS, []).
1819
-define(DEFAULT_POOL_SIZE, 16).

src/buoy_pool.erl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ terminate() ->
9898

9999
%% private
100100
client_options(Protocol, Hostname, Port, Options) ->
101+
MaxRequests = ?LOOKUP(max_requests, Options, ?DEFAULT_MAX_REQUESTS),
101102
Reconnect = ?LOOKUP(reconnect, Options, ?DEFAULT_RECONNECT),
102103
ReconnectTimeMax = ?LOOKUP(reconnect_time_max, Options,
103104
?DEFAULT_RECONNECT_MAX),
@@ -107,6 +108,7 @@ client_options(Protocol, Hostname, Port, Options) ->
107108
?DEFAULT_SOCKET_OPTIONS),
108109

109110
[{ip, binary_to_list(Hostname)},
111+
{max_requests, MaxRequests},
110112
{port, Port},
111113
{protocol, shackle_protocol(Protocol, Options)},
112114
{reconnect, Reconnect},

test/buoy_http_server.erl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
-module(buoy_http_server).
22

33
-export([
4+
connection_count/0,
45
start/0,
56
stop/0
67
]).
78

89
-define(PORT, 8080).
910

1011
%% public
12+
connection_count() ->
13+
counters:get(persistent_term:get({?MODULE, connections}), 1).
14+
1115
start() ->
1216
Self = self(),
1317
Pid = spawn(fun () -> init(Self) end),
@@ -30,6 +34,7 @@ stop() ->
3034
%% private
3135
init(Parent) ->
3236
register(?MODULE, self()),
37+
persistent_term:put({?MODULE, connections}, counters:new(1, [])),
3338
{ok, LSocket} = gen_tcp:listen(?PORT, [
3439
binary,
3540
{active, false},
@@ -42,6 +47,7 @@ init(Parent) ->
4247

4348
accept(LSocket) ->
4449
{ok, Socket} = gen_tcp:accept(LSocket),
50+
counters:add(persistent_term:get({?MODULE, connections}), 1, 1),
4551
Pid = spawn_link(fun () ->
4652
receive go -> connection(Socket) end
4753
end),

test/buoy_tests.erl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ buoy_test_() ->
3636
fun telemetry_disabled_subtest/0
3737
]}.
3838

39+
buoy_max_requests_test_() ->
40+
{setup,
41+
fun () -> setup([{max_requests, 2}, {pool_size, 1}]) end,
42+
fun (_) -> cleanup() end,
43+
[
44+
fun max_requests_subtest/0
45+
]}.
46+
3947
buoy_socket_test_() ->
4048
case list_to_integer(erlang:system_info(otp_release)) >= 28 of
4149
true ->
@@ -68,6 +76,17 @@ get_subtest() ->
6876
{ok, ?RESP_2} = buoy:get(?URL(?URL_2), #{}),
6977
{ok, ?RESP_4} = buoy:get(?URL(?URL_4), #{}).
7078

79+
max_requests_subtest() ->
80+
1 = buoy_http_server:connection_count(),
81+
lists:foreach(fun (_) ->
82+
{ok, ?RESP_1} = buoy:get(?URL(?URL_1), #{}),
83+
{ok, ?RESP_1} = buoy:get(?URL(?URL_1), #{}),
84+
%% recycling closes the socket after the 2nd response and
85+
%% reconnects asynchronously; wait it out before the next pair
86+
timer:sleep(100)
87+
end, lists:seq(1, 3)),
88+
true = buoy_http_server:connection_count() >= 3.
89+
7190
pool_subtest() ->
7291
{error, pool_already_started} = buoy_pool:start(?URL(?URL_1)),
7392
ok = buoy_pool:stop(?URL(?URL_1)),

0 commit comments

Comments
 (0)