From 857999bb69129cad54215af071bdd5623fb216e8 Mon Sep 17 00:00:00 2001 From: Kevin Wilson Date: Tue, 31 Jan 2017 21:31:40 -0800 Subject: [PATCH] added q_async function, plus tests and docs --- src/eredis.erl | 15 ++++++++++++++- src/eredis_client.erl | 18 ++++++++++++++++++ test/eredis_tests.erl | 12 ++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/eredis.erl b/src/eredis.erl index 79b451b1..4cc3107c 100644 --- a/src/eredis.erl +++ b/src/eredis.erl @@ -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]). @@ -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 %% diff --git a/src/eredis_client.erl b/src/eredis_client.erl index 2b7e3d69..466ccfac 100644 --- a/src/eredis_client.erl +++ b/src/eredis_client.erl @@ -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}. @@ -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 diff --git a/test/eredis_tests.erl b/test/eredis_tests.erl index 0e674e1b..78c42cdd 100644 --- a/test/eredis_tests.erl +++ b/test/eredis_tests.erl @@ -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),