Skip to content

Commit

Permalink
Merge pull request tigertext#1 from kevinwilson541/feature/async-query
Browse files Browse the repository at this point in the history
added q_async function, plus tests and docs
  • Loading branch information
kevinwilson541 authored Feb 1, 2017
2 parents bf12ecb + 857999b commit 7effc8a
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 1 deletion.
15 changes: 14 additions & 1 deletion src/eredis.erl
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
-define(TIMEOUT, 5000).

-export([start_link/0, start_link/1, start_link/2, start_link/3, start_link/4,
start_link/5, start_link/6, stop/1, q/2, q/3, qp/2, qp/3, q_noreply/2]).
start_link/5, start_link/6, stop/1, q/2, q/3, qp/2, qp/3, q_noreply/2,
q_async/2, q_async/3]).

%% Exported for testing
-export([create_multibulk/1]).
Expand Down Expand Up @@ -102,6 +103,18 @@ qp(Client, Pipeline, Timeout) ->
q_noreply(Client, Command) ->
cast(Client, Command).

-spec q_async(Client::client(), Command::[any()]) -> ok.
% @doc Executes the command, and sends a message to this process with the response (with either error or success). Message is of the form `{response, Reply}', where `Reply' is the reply expected from `q/2'.
q_async(Client, Command) ->
q_async(Client, Command, self()).

-spec q_async(Client::client(), Command::[any()], Pid::pid()|atom()) -> ok.
%% @doc Executes the command, and sends a message to `Pid' with the response (with either or success).
%% @see 1_async/2
q_async(Client, Command, Pid) when is_pid(Pid) ->
Request = {request, create_multibulk(Command), Pid},
gen_server:cast(Client, Request).

%%
%% INTERNAL HELPERS
%%
Expand Down
18 changes: 18 additions & 0 deletions src/eredis_client.erl
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,15 @@ handle_cast({request, Req}, State) ->
{noreply, State1}
end;

handle_cast({request, Req, Pid}, State) ->
case do_request(Req, Pid, State) of
{reply, Reply, State1} ->
safe_send(Pid, {response, Reply}),
{noreply, State1};
{noreply, State1} ->
{noreply, State1}
end;

handle_cast(_Msg, State) ->
{noreply, State}.

Expand Down Expand Up @@ -279,9 +288,18 @@ receipient({_, From, _}) ->

safe_reply(undefined, _Value) ->
ok;
safe_reply(Pid, Value) when is_pid(Pid) ->
safe_send(Pid, {response, Value});
safe_reply(From, Value) ->
gen_server:reply(From, Value).

safe_send(Pid, Value) ->
try erlang:send(Pid, Value)
catch
Err:Reason ->
error_logger:info_msg("Failed to send message to ~p with reason ~p~n", [Pid, {Err, Reason}])
end.

%% @doc: Helper for connecting to Redis, authenticating and selecting
%% the correct database. These commands are synchronous and if Redis
%% returns something we don't expect, we crash. Returns {ok, State} or
Expand Down
12 changes: 12 additions & 0 deletions test/eredis_tests.erl
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,24 @@ q_noreply_test() ->
%% Even though q_noreply doesn't wait, it is sent before subsequent requests:
?assertEqual({ok, <<"bar">>}, eredis:q(C, ["GET", foo])).

q_async_test() ->
C = c(),
?assertEqual({ok, <<"OK">>}, eredis:q(C, ["SET", foo, bar])),
?assertEqual(ok, eredis:q_async(C, ["GET", foo], self())),
receive
{response, Msg} ->
?assertEqual(Msg, {ok, <<"bar">>}),
?assertMatch({ok, _}, eredis:q(C, ["DEL", foo]))
end.

c() ->
Res = eredis:start_link(),
?assertMatch({ok, _}, Res),
{ok, C} = Res,
C.



c_no_reconnect() ->
Res = eredis:start_link("127.0.0.1", 6379, 0, "", no_reconnect),
?assertMatch({ok, _}, Res),
Expand Down

0 comments on commit 7effc8a

Please sign in to comment.