Skip to content

Commit 4520d61

Browse files
authored
Merge pull request #139 from lpgauth/hot-path-opts
Trim server-loop overhead and add a socket NIF protocol
2 parents 4b85052 + 93e29ac commit 4520d61

11 files changed

Lines changed: 319 additions & 178 deletions

src/shackle.erl

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
-module(shackle).
22
-include("shackle_internal.hrl").
33

4-
-compile(inline).
5-
-compile({inline_size, 512}).
6-
74
%% public
85
-export([
96
call/2,
@@ -22,10 +19,10 @@
2219
-type external_request_id() :: term().
2320
-type inet_address() :: inet:ip_address() | inet:hostname().
2421
-type inet_port() :: inet:port_number().
25-
-type protocol() :: shackle_ssl| shackle_tcp | shackle_udp.
22+
-type protocol() :: shackle_socket | shackle_ssl | shackle_tcp | shackle_udp.
2623
-type request_id() :: {shackle_server:name(), reference()}.
2724
-type response() :: {external_request_id(), term()}.
28-
-type socket() :: inet:socket() | ssl:sslsocket().
25+
-type socket() :: inet:socket() | socket:socket() | ssl:sslsocket().
2926
-type socket_option() :: gen_tcp:connect_option() | gen_udp:option() | ssl:tls_client_option().
3027
-type socket_options() :: [socket_option()].
3128
-type table() :: atom().
@@ -103,7 +100,7 @@ cast(PoolName, Request, Pid, Timeout) ->
103100

104101
receive_response(RequestId) ->
105102
receive
106-
{#cast {request_id = RequestId}, Reply} ->
103+
{shackle_reply, RequestId, Reply} ->
107104
Reply
108105
end.
109106

@@ -112,7 +109,7 @@ receive_response(RequestId) ->
112109

113110
receive_response(RequestId, Timeout) ->
114111
receive
115-
{#cast {request_id = RequestId}, Reply} ->
112+
{shackle_reply, RequestId, Reply} ->
116113
Reply
117114
after Timeout ->
118115
{error, timeout}

src/shackle_app.erl

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

3333
start(_StartType, _StartArgs) ->
34+
persistent_term:put({shackle, telemetry},
35+
application:get_env(?APP, telemetry, true)),
3436
shackle_sup:start_link().
3537

3638
-spec stop(term()) ->

src/shackle_backlog.erl

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
-module(shackle_backlog).
22

3-
-compile(inline).
4-
-compile({inline_size, 512}).
5-
63
%% internal
74
-export([
85
check/3,

src/shackle_pool.erl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,6 @@ terminate() ->
120120
%% private
121121
cleanup(Name, OptionsRec) ->
122122
shackle_backlog:delete(Name),
123-
shackle_queue:delete(Name),
124123
shackle_status:delete(Name),
125124
cleanup_ets(Name, OptionsRec),
126125
cleanup_foil(Name, OptionsRec).
@@ -201,7 +200,6 @@ server_id(Name, PoolSize, round_robin) ->
201200

202201
setup(Name, #pool_options {pool_size = PoolSize} = OptionsRec) ->
203202
shackle_backlog:new(Name),
204-
shackle_queue:new(Name),
205203
shackle_status:new(Name, PoolSize),
206204
setup_ets(Name, OptionsRec),
207205
setup_foil(Name, OptionsRec).

src/shackle_queue.erl

Lines changed: 0 additions & 80 deletions
This file was deleted.

src/shackle_server.erl

Lines changed: 53 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
-module(shackle_server).
22
-include("shackle_internal.hrl").
33

4-
-compile(inline).
5-
-compile({inline_size, 512}).
6-
74
-export([
85
start_link/2
96
]).
@@ -26,7 +23,8 @@
2623
pool_name :: shackle_pool:name(),
2724
port :: shackle:inet_port(),
2825
protocol :: shackle:protocol(),
29-
queue :: shackle:table(),
26+
queue = #{} :: #{shackle:external_request_id() =>
27+
{shackle:cast(), reference()}},
3028
reconnect_state :: undefined | reconnect_state(),
3129
socket :: undefined | shackle:socket(),
3230
socket_options :: shackle:socket_options(),
@@ -86,7 +84,6 @@ init(Name, Parent, Opts) ->
8684
pool_name = PoolName,
8785
port = Port,
8886
protocol = Protocol,
89-
queue = shackle_queue:table_name(PoolName),
9087
reconnect_state = ReconnectState,
9188
socket_options = SocketOptions
9289
}, undefined}}.
@@ -104,7 +101,6 @@ handle_msg({Request, #cast {
104101
timeout = Timeout
105102
} = Cast}, {#state {
106103
client = Client,
107-
id = Id,
108104
pool_name = PoolName,
109105
protocol = Protocol,
110106
queue = Queue,
@@ -118,14 +114,15 @@ handle_msg({Request, #cast {
118114
shackle_telemetry:send(Client, iolist_size(Data)),
119115
case ExtRequestId of
120116
undefined ->
121-
reply(ok, Cast, State);
117+
reply(ok, Cast, State),
118+
{ok, {State, ClientState2}};
122119
_ ->
123120
Msg = {timeout, ExtRequestId},
124121
TimerRef = erlang:send_after(Timeout, self(), Msg),
125-
shackle_queue:add(Queue, Id, ExtRequestId, Cast,
126-
TimerRef)
127-
end,
128-
{ok, {State, ClientState2}};
122+
Queue2 = maps:put(ExtRequestId, {Cast, TimerRef},
123+
Queue),
124+
{ok, {State#state {queue = Queue2}, ClientState2}}
125+
end;
129126
{error, Reason} ->
130127
?WARN(PoolName, "send error: ~p", [Reason]),
131128
Protocol:close(Socket),
@@ -139,6 +136,24 @@ handle_msg({Request, #cast {
139136
reply({error, client_crash}, Cast, State),
140137
{ok, {State, ClientState}}
141138
end;
139+
handle_msg({'$socket', Socket, select, _Handle}, {#state {
140+
socket = Socket
141+
} = State, ClientState}) ->
142+
143+
case shackle_socket:recv(Socket) of
144+
{ok, Data} ->
145+
handle_msg_data(Socket, Data, State, ClientState);
146+
wait ->
147+
{ok, {State, ClientState}};
148+
{error, closed} ->
149+
handle_msg_close(Socket, State, ClientState);
150+
{error, Reason} ->
151+
handle_msg_error(Socket, Reason, State, ClientState)
152+
end;
153+
handle_msg({'$socket', _Socket, select, _Handle}, {State, ClientState}) ->
154+
{ok, {State, ClientState}};
155+
handle_msg({'$socket', Socket, abort, _Info}, {State, ClientState}) ->
156+
handle_msg_close(Socket, State, ClientState);
142157
handle_msg({ssl, Socket, Data}, {State, ClientState}) ->
143158
handle_msg_data(Socket, Data, State, ClientState);
144159
handle_msg({ssl_closed, Socket}, {State, ClientState}) ->
@@ -187,7 +202,6 @@ handle_msg(?MSG_CONNECT, {#state {
187202
end;
188203
handle_msg({timeout, ExtRequestId}, {#state {
189204
client = Client,
190-
id = Id,
191205
pool_name = PoolName,
192206
protocol = Protocol,
193207
queue = Queue,
@@ -199,8 +213,8 @@ handle_msg({timeout, ExtRequestId}, {#state {
199213
try Client:handle_timeout(ExtRequestId, ClientState) of
200214
{ok, Reply, ClientState2} ->
201215
shackle_telemetry:handle_timeout(Client),
202-
process_responses([Reply], State),
203-
{ok, {State, ClientState2}};
216+
State2 = process_responses([Reply], State),
217+
{ok, {State2, ClientState2}};
204218
{error, Reason, ClientState2} ->
205219
?WARN(PoolName, "handle_timeout error: ~p", [Reason]),
206220
Protocol:close(Socket),
@@ -213,14 +227,14 @@ handle_msg({timeout, ExtRequestId}, {#state {
213227
close(State, ClientState)
214228
end;
215229
false ->
216-
case shackle_queue:remove(Queue, Id, ExtRequestId) of
217-
{ok, Cast, _TimerRef} ->
230+
case maps:take(ExtRequestId, Queue) of
231+
{{Cast, _TimerRef}, Queue2} ->
218232
shackle_telemetry:timeout(Client),
219-
reply({error, timeout}, Cast, State);
220-
{error, not_found} ->
221-
ok
222-
end,
223-
{ok, {State, ClientState}}
233+
reply({error, timeout}, Cast, State),
234+
{ok, {State#state {queue = Queue2}, ClientState}};
235+
error ->
236+
{ok, {State, ClientState}}
237+
end
224238
end;
225239
handle_msg(Msg, {#state {
226240
pool_name = PoolName
@@ -303,8 +317,8 @@ client_setup(Client, PoolName, Protocol, Socket, ClientState) ->
303317

304318
close(#state {id = Id} = State, ClientState) ->
305319
shackle_status:disable(Id),
306-
reply_all({error, socket_closed}, State),
307-
reconnect(State, ClientState).
320+
State2 = reply_all({error, socket_closed}, State),
321+
reconnect(State2, ClientState).
308322

309323
connect(Protocol, Address, Port, SocketOptions, PoolName) ->
310324
case inet:getaddrs(Address, inet) of
@@ -342,8 +356,8 @@ handle_msg_data(Socket, Data, #state {
342356
shackle_telemetry:recv(Client, size(Data)),
343357
try Client:handle_data(Data, ClientState) of
344358
{ok, Replies, ClientState2} ->
345-
process_responses(Replies, State),
346-
{ok, {State, ClientState2}};
359+
State2 = process_responses(Replies, State),
360+
{ok, {State2, ClientState2}};
347361
{error, Reason, ClientState2} ->
348362
?WARN(PoolName, "handle_data error: ~p", [Reason]),
349363
Protocol:close(Socket),
@@ -370,27 +384,26 @@ handle_msg_error(Socket, Reason, #state {
370384
handle_msg_error(_Socket, _Reason, State, ClientState) ->
371385
{ok, {State, ClientState}}.
372386

373-
process_responses([], _State) ->
374-
ok;
387+
process_responses([], State) ->
388+
State;
375389
process_responses([{ExtRequestId, Reply} | T], #state {
376390
client = Client,
377-
id = Id,
378391
queue = Queue
379392
} = State) ->
380393

381394
shackle_telemetry:replies(Client),
382-
case shackle_queue:remove(Queue, Id, ExtRequestId) of
383-
{ok, #cast {timestamp = Timestamp} = Cast, TimerRef} ->
395+
case maps:take(ExtRequestId, Queue) of
396+
{{#cast {timestamp = Timestamp} = Cast, TimerRef}, Queue2} ->
384397
shackle_telemetry:found(Client),
385398
Diff = erlang:monotonic_time(microsecond) - Timestamp,
386399
shackle_telemetry:reply(Client, Diff),
387400
erlang:cancel_timer(TimerRef),
388-
reply(Reply, Cast, State);
389-
{error, not_found} ->
401+
reply(Reply, Cast, State),
402+
process_responses(T, State#state {queue = Queue2});
403+
error ->
390404
shackle_telemetry:not_found(Client),
391-
ok
392-
end,
393-
process_responses(T, State).
405+
process_responses(T, State)
406+
end.
394407

395408
reconnect(State, undefined) ->
396409
reconnect_timer(State, undefined);
@@ -459,22 +472,18 @@ reply(_Reply, #cast {pid = undefined}, #state {
459472

460473
shackle_backlog:decrement(Backlog, Id),
461474
ok;
462-
reply(Reply, #cast {pid = Pid} = Cast, #state {
475+
reply(Reply, #cast {pid = Pid, request_id = RequestId}, #state {
463476
backlog = Backlog,
464477
id = Id
465478
}) ->
466479

467480
shackle_backlog:decrement(Backlog, Id),
468-
Pid ! {Cast, Reply},
481+
Pid ! {shackle_reply, RequestId, Reply},
469482
ok.
470483

471-
reply_all(Reply, #state {
472-
id = Id,
473-
queue = Queue
474-
} = State) ->
475-
476-
Requests = shackle_queue:clear(Queue, Id),
477-
reply_all(Reply, Requests, State).
484+
reply_all(Reply, #state {queue = Queue} = State) ->
485+
reply_all(Reply, maps:values(Queue), State),
486+
State#state {queue = #{}}.
478487

479488
reply_all(_Reply, [], _State) ->
480489
ok;

0 commit comments

Comments
 (0)