Skip to content

Commit ff81ff7

Browse files
committed
Add socket protocol support and a telemetry gate
Pools accept a protocol option (or the protocol app env) so http pools can run on shackle_socket, the socket NIF protocol added in shackle 0.7.4, instead of gen_tcp; https pools always use shackle_ssl. Setup and client specs widen to shackle:socket(). shackle_socket needs OTP 27.3+ at runtime, so the eunit coverage is gated on OTP 27+. Requests emit telemetry in the caller hot path on every call; the telemetry app env (read once into a persistent_term at app start) now disables emission entirely, mirroring shackle.
1 parent b6a5e5d commit ff81ff7

10 files changed

Lines changed: 111 additions & 28 deletions

File tree

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,14 @@ ok
8484
<td>random</td>
8585
<td>connection selection strategy</td>
8686
</tr>
87+
<tr>
88+
<td>protocol</td>
89+
<td>shackle_socket | shackle_tcp</td>
90+
<td>shackle_tcp</td>
91+
<td>shackle transport for http pools (shackle_socket runs on the
92+
socket NIF and needs OTP 27.3+); https pools always use
93+
shackle_ssl</td>
94+
</tr>
8795
<tr>
8896
<td>reconnect</td>
8997
<td>boolean()</td>
@@ -141,6 +149,9 @@ remains observable via shackle's own telemetry — buoy's events
141149
surface the buoy-level routing decision without duplicating that
142150
work.
143151

152+
Set the `telemetry` app env to `false` to skip event emission
153+
entirely (read once at application start).
154+
144155
## Tests
145156

146157
```makefile

include/buoy.hrl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
-type option() :: {backlog_size, pos_integer()} |
4848
{pool_size, pos_integer()} |
4949
{pool_strategy, random | round_robin} |
50+
{protocol, shackle_socket | shackle_tcp} |
5051
{reconnect, boolean()} |
5152
{reconnect_time_max, pos_integer() | infinity} |
5253
{reconnect_time_min, pos_integer()} |

rebar.config

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{deps, [
22
{foil, "0.1.6"},
3-
{shackle, "0.7.3"},
3+
{shackle, "0.7.4"},
44
{telemetry, "1.4.2"}
55
]}.
66

7-
{dialyzer, [{plt_extra_apps, [public_key]}]}.
7+
{dialyzer, [{plt_extra_apps, [public_key, ssl]}]}.
88

99
{ex_doc, [
1010
{api_reference, true},

rebar.lock

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,19 @@
22
[{<<"foil">>,{pkg,<<"foil">>,<<"0.1.6">>},0},
33
{<<"knot">>,{pkg,<<"knot">>,<<"0.1.2">>},1},
44
{<<"metal">>,{pkg,<<"metal">>,<<"0.1.2">>},1},
5-
{<<"shackle">>,{pkg,<<"shackle">>,<<"0.7.3">>},0},
5+
{<<"shackle">>,{pkg,<<"shackle">>,<<"0.7.4">>},0},
66
{<<"telemetry">>,{pkg,<<"telemetry">>,<<"1.4.2">>},0}]}.
77
[
88
{pkg_hash,[
99
{<<"foil">>, <<"31A9048E8EC6E8AC951ED3C4E4EB8F549465920C70F19E92CD9FC8A6867E7253">>},
1010
{<<"knot">>, <<"3B6C07CD24831D62752D879F031BCEDCFADB343738679EB1774A029D0A8B7D48">>},
1111
{<<"metal">>, <<"7501F14B075380F829498C4CE605643D6C7F0C55F60EBFCFBF694C33A558DF98">>},
12-
{<<"shackle">>, <<"DEDE0777BC297B212DE3DBE4377B391DA991FC011B4D5201960A5C4F11E83B2C">>},
12+
{<<"shackle">>, <<"CF7AF47CCA9C881C436E2E36EE91AB43CBF5C7AAF407146E00F4C4F66B5828B5">>},
1313
{<<"telemetry">>, <<"A0CB522801DFFB1C49FE6E30561BADFFC7B6D0E180DB1300DF759FAA22062855">>}]},
1414
{pkg_hash_ext,[
1515
{<<"foil">>, <<"8D34B12D712FFB30AD9E192F57885ABB1A61510CD3D9F6AA44470510A50B979E">>},
1616
{<<"knot">>, <<"42CB91A8024A8857D70A61B33E83D31370D5EB48681B366AE1203CB7C7CCFB38">>},
1717
{<<"metal">>, <<"CAC84E5CF2875B56E68874F3F0B3D598AF3EC30397EC2151D5C81C408961E6E4">>},
18-
{<<"shackle">>, <<"056944FEEB85ED0F3E1B7505FBC7174CDE0788993A19E9ADA85137A23CA56349">>},
18+
{<<"shackle">>, <<"43CCDE785082B9670BDFA40BF459D9CF602E7EB425FA31C73A66EBEE0E3CA9B5">>},
1919
{<<"telemetry">>, <<"928F6495066506077862C0D1646609EED891A4326BEE3126BA54B60AF61FEBB1">>}]}
2020
].

src/buoy.erl

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -69,16 +69,10 @@ async_request(Method, #buoy_url {
6969
Request = {request, Method, Path, Headers, Host, Body},
7070
Pid = buoy_opts(pid, BuoyOpts),
7171
Timeout = buoy_opts(timeout, BuoyOpts),
72-
telemetry:execute([buoy, request, sent],
73-
#{count => 1},
74-
#{method => Method, host => Host,
75-
async => true}),
72+
buoy_telemetry:request_sent(Method, Host, true),
7673
shackle:cast(PoolName, Request, Pid, Timeout);
7774
{error, Reason} = E ->
78-
telemetry:execute([buoy, request, error],
79-
#{count => 1},
80-
#{method => Method, host => Host,
81-
reason => Reason}),
75+
buoy_telemetry:request_error(Method, Host, Reason),
8276
E
8377
end.
8478

@@ -135,16 +129,10 @@ request(Method, #buoy_url {
135129
Body = buoy_opts(body, BuoyOpts),
136130
Request = {request, Method, Path, Headers, Host, Body},
137131
Timeout = buoy_opts(timeout, BuoyOpts),
138-
telemetry:execute([buoy, request, sent],
139-
#{count => 1},
140-
#{method => Method, host => Host,
141-
async => false}),
132+
buoy_telemetry:request_sent(Method, Host, false),
142133
shackle:call(PoolName, Request, Timeout);
143134
{error, Reason} = E ->
144-
telemetry:execute([buoy, request, error],
145-
#{count => 1},
146-
#{method => Method, host => Host,
147-
reason => Reason}),
135+
buoy_telemetry:request_error(Method, Host, Reason),
148136
E
149137
end.
150138

src/buoy_app.erl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ stop() ->
3030
{ok, pid()}.
3131

3232
start(_StartType, _StartArgs) ->
33+
persistent_term:put({buoy, telemetry},
34+
?GET_ENV(telemetry, true)),
3335
buoy_sup:start_link().
3436

3537
-spec stop(term()) ->

src/buoy_client.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ init(_Opts) ->
3333
queue = queue:new()
3434
}}.
3535

36-
-spec setup(inet:socket(), state()) ->
36+
-spec setup(shackle:socket(), state()) ->
3737
{ok, state()}.
3838

3939
setup(_Socket, State) ->

src/buoy_pool.erl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ client_options(Protocol, Hostname, Port, Options) ->
100100

101101
[{ip, binary_to_list(Hostname)},
102102
{port, Port},
103-
{protocol, shackle_protocol(Protocol)},
103+
{protocol, shackle_protocol(Protocol, Options)},
104104
{reconnect, Reconnect},
105105
{reconnect_time_max, ReconnectTimeMax},
106106
{reconnect_time_min, ReconnectTimeMin},
@@ -120,7 +120,7 @@ pool_options(Options) ->
120120
{pool_size, PoolSize},
121121
{pool_strategy, PoolStrategy}].
122122

123-
shackle_protocol(http) ->
124-
shackle_tcp;
125-
shackle_protocol(https) ->
123+
shackle_protocol(http, Options) ->
124+
?LOOKUP(protocol, Options, ?GET_ENV(protocol, shackle_tcp));
125+
shackle_protocol(https, _Options) ->
126126
shackle_ssl.

src/buoy_telemetry.erl

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
-module(buoy_telemetry).
2+
-include("buoy_internal.hrl").
3+
4+
-compile(inline).
5+
-compile({inline_size, 512}).
6+
7+
-export([
8+
request_error/3,
9+
request_sent/3
10+
]).
11+
12+
%% internal
13+
-spec request_error(method(), host(), atom()) ->
14+
ok.
15+
16+
request_error(Method, Host, Reason) ->
17+
case enabled() of
18+
true ->
19+
telemetry:execute([buoy, request, error], #{count => 1},
20+
#{method => Method, host => Host, reason => Reason});
21+
false ->
22+
ok
23+
end.
24+
25+
-spec request_sent(method(), host(), boolean()) ->
26+
ok.
27+
28+
request_sent(Method, Host, Async) ->
29+
case enabled() of
30+
true ->
31+
telemetry:execute([buoy, request, sent], #{count => 1},
32+
#{method => Method, host => Host, async => Async});
33+
false ->
34+
ok
35+
end.
36+
37+
%% private
38+
enabled() ->
39+
persistent_term:get({buoy, telemetry}, true).

test/buoy_tests.erl

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,27 @@ buoy_test_() ->
3232
fun put_subtest/0,
3333
fun head_subtest/0,
3434
fun telemetry_sent_subtest/0,
35-
fun telemetry_error_subtest/0
35+
fun telemetry_error_subtest/0,
36+
fun telemetry_disabled_subtest/0
3637
]}.
3738

39+
buoy_socket_test_() ->
40+
case list_to_integer(erlang:system_info(otp_release)) >= 27 of
41+
true ->
42+
{setup,
43+
fun () -> setup([{protocol, shackle_socket}]) end,
44+
fun (_) -> cleanup() end,
45+
[
46+
fun custom_subtest/0,
47+
fun get_subtest/0,
48+
fun post_subtest/0,
49+
fun put_subtest/0,
50+
fun head_subtest/0
51+
]};
52+
false ->
53+
[]
54+
end.
55+
3856
%% tests
3957
custom_subtest() ->
4058
{ok, ReqId} = buoy:async_custom(?VERB, ?URL(?URL_5), #{}),
@@ -118,16 +136,40 @@ telemetry_error_subtest() ->
118136
telemetry:detach(HandlerId)
119137
end.
120138

139+
telemetry_disabled_subtest() ->
140+
Self = self(),
141+
HandlerId = <<"buoy-test-disabled">>,
142+
ok = telemetry:attach(HandlerId, [buoy, request, sent],
143+
fun (Event, Measurements, Metadata, _) ->
144+
Self ! {telemetry, Event, Measurements, Metadata}
145+
end, undefined),
146+
persistent_term:put({buoy, telemetry}, false),
147+
try
148+
{ok, ?RESP_1} = buoy:get(?URL(?URL_1), #{}),
149+
receive
150+
{telemetry, _, _, _} ->
151+
erlang:error(unexpected_telemetry_event)
152+
after 200 ->
153+
ok
154+
end
155+
after
156+
persistent_term:put({buoy, telemetry}, true),
157+
telemetry:detach(HandlerId)
158+
end.
159+
121160
%% utils
122161
cleanup() ->
123162
buoy_pool:stop(?URL(?URL_1)),
124163
buoy_app:stop(),
125164
buoy_http_server:stop().
126165

127166
setup() ->
167+
setup([]).
168+
169+
setup(PoolOptions) ->
128170
error_logger:tty(false),
129171
{ok, _} = buoy_http_server:start(),
130172
timer:sleep(200),
131173
buoy_app:start(),
132-
buoy_pool:start(?URL(?URL_1)),
174+
buoy_pool:start(?URL(?URL_1), PoolOptions),
133175
timer:sleep(200).

0 commit comments

Comments
 (0)