Skip to content

Commit

Permalink
Pulled everything from japerk :
Browse files Browse the repository at this point in the history
  • Loading branch information
cstar committed Dec 2, 2009
1 parent e45cdf6 commit df4d58d
Show file tree
Hide file tree
Showing 14 changed files with 1,033 additions and 25 deletions.
5 changes: 5 additions & 0 deletions Emakefile
@@ -0,0 +1,5 @@
{"src/*", [
debug_info,
{i, "include/"},
{outdir, "ebin/"}
]}.
31 changes: 18 additions & 13 deletions Makefile
@@ -1,10 +1,10 @@
LIBDIR=`erl -eval 'io:format("~s~n", [code:lib_dir()])' -s init stop -noshell`
# store output so is only executed once
LIBDIR=$(shell erl -eval 'io:format("~s~n", [code:lib_dir()])' -s init stop -noshell)
# get application vsn from app file
VSN=$(shell erl -pa ebin/ -eval 'application:load(erldis), {ok, Vsn} = application:get_key(erldis, vsn), io:format("~s~n", [Vsn])' -s init stop -noshell)

all:
mkdir -p ebin/
(cd src;$(MAKE))
# test compile fails if eunit not present
#(cd test;$(MAKE))
@erl -make

clean: clean_tests
(cd src;$(MAKE) clean)
Expand All @@ -24,12 +24,17 @@ testrun: all
mkdir -p ebin/
(cd test;$(MAKE) test)

install: all
# original "mkdir -p {LIBDIR}/erldis-0.0.1/{ebin,include}"
# actually makes a directory called {ebin,include}
mkdir -p ${LIBDIR}/erldis-0.0.1/ebin
mkdir -p ${LIBDIR}/erldis-0.0.1/include
for i in ebin/*.beam; do install $$i $(LIBDIR)/erldis-0.0.1/$$i ; done
for i in include/*.hrl; do install $$i $(LIBDIR)/erldis-0.0.1/$$i ; done
install: all
mkdir -p $(ERL_LIBS)/erldis-$(VSN)/ebin
mkdir -p $(ERL_LIBS)/erldis-$(VSN)/include
for i in ebin/*.beam; do install $$i $(ERL_LIBS)/erldis-$(VSN)/$$i ; done
for i in include/*.hrl; do install $$i $(ERL_LIBS)/erldis-$(VSN)/$$i ; done
# also install .app file
install ebin/erldis.app $(LIBDIR)/erldis-0.0.1/ebin/erldis.app
install ebin/erldis.app $(ERL_LIBS)/erldis-$(VSN)/ebin/erldis.app
install ebin/erldis.appup $(ERL_LIBS)/erldis-$(VSN)/ebin/erldis.appup

plt:
@dialyzer --build_plt --plt .plt -q -r . -I include/

check: all
@dialyzer --check_plt --plt .plt -q -r . -I include/
11 changes: 11 additions & 0 deletions doc/overview.edoc
@@ -0,0 +1,11 @@
@doc Redis erlang client

From dialtone on erldis_client:
handle_info is the function that deals with parsing every line that comes from redis. save_or_reply is what is currently used to send replies back to the users.
going through the State parameters:
* socket is of course the current connection to redis
* buffer contains intermediate results of parsing. it's used for multi-bulk replies. but it gets handy also for single bulk replies, although clearly it will be at most long 1 in those cases
* reply_caller is a function used to abstract the reply to the user. since the call is synchronous, the client might put itself in listening slower than redis can answer the request. when it manages to listen before the answer it adds a call that registers the call request and when it's done the system uses that function to send back the results. if it comes afterwards it simply gets the results in save_or_reply the function checks for reply_caller and if it's not there it just appends to results
* the field remaining is for how many remaining packets you need to handle before the end of this call. this is also especially useful for multi-bulk replies. when a call is made it's set to 1 then the rest of the state machine figures out if only 1 packet will be received or how many more (the state machine is in erldis_proto)
* then the field calls is the number of calls that are waiting. basically at the end of a call remaining would go to 0. but if it goes to 0 the state machine would parse the remaining stuff. so using calls we keep track of how many more calls are incoming and until calls is 0 remaining is reset to 1 when it goes to 0 at the end of a parsing. and this is done in save_or_reply
* then pstate is the parser state, there can be only 4 right now error, hold, read, empty. error means that the next line is an error message. hold means that the next number is the number of multi-bulk items in the reply and is used to set the remaining field to something else than 1. read tells you how long the next field is so you need to read those bytes + 2 (\r\n are added by redis but not counted in the bytes... I fought hard to avoid this but salvatore didn't change it...). then empty means that the system is ready to accept a new reply. the function trim2 exists just to remove the \r\n at the end of the reply
7 changes: 4 additions & 3 deletions ebin/erldis.app
@@ -1,10 +1,11 @@
{application, erldis, [
{description, "Erlang Redis application"},
{vsn, "0.0.1"},
{vsn, "0.0.7"},
{registered, [erldis_sup]},
{mod, {erldis_app, []}},
% TODO: include eunit?
{applications, [kernel, stdlib]},
{modules, [erldis_client, erldis, erldis_proto, erldis_app, erldis_sup]},
{modules, [erldis_client, erldis, erldis_proto, erldis_app, erldis_sup,
erldis_sync_client, erldis_sets, erldis_dict, erldis_list]},
{env, [{host, "localhost"}, {port, 6379}, {timeout, 500}]}
]}.
]}.
65 changes: 65 additions & 0 deletions ebin/erldis.appup
@@ -0,0 +1,65 @@
{"0.0.7", [
{"0.0.6", [
{load_module, erldis_sync_client},
{load_module, erldis_dict},
{load_module, erldis_list}
]},
{"0.0.5", [
{load_module, erldis_sync_client},
{load_module, erldis_sets},
{add_module, erldis_dict},
{add_module, erldis_list}
]},
{"0.0.4", [
{load_module, erldis_sync_client},
{load_module, erldis_sets}
]},
{"0.0.3", [
{load_module, erldis_sync_client},
{load_module, erldis_sets}
]},
{"0.0.2", [
{add_module, erldis_sync_client},
{load_module, erldis_sets}
]},
{"0.0.1", [
{add_module, erldis_client},
{add_module, erldis_proto},
{load_module, erldis},
{delete_module, client},
{delete_module, proto},
{add_module, erldis_sets}
]}
], [
{"0.0.6", [
{load_module, erldis_sync_client},
{load_module, erldis_dict},
{load_module, erldis_list}
]},
{"0.0.5", [
{load_module, erldis_sync_client},
{load_module, erldis_sets},
{delete_module, erldis_dict},
{delete_module, erldis_list}
]},
{"0.0.4", [
{load_module, erldis_sync_client},
{load_module, erldis_sets}
]},
{"0.0.3", [
{load_module, erldis_sync_client},
{load_module, erldis_sets}
]},
{"0.0.2", [
{delete_module, erldis_sync_client},
{load_module, erldis_sets}
]},
{"0.0.1", [
{add_module, client},
{add_module, proto},
{load_module, erldis},
{delete_module, erldis_client},
{delete_module, erldis_proto},
{delete_module, erldis_sets}
]}
]}.
12 changes: 5 additions & 7 deletions src/erldis_client.erl
Expand Up @@ -256,18 +256,16 @@ handle_info({tcp, Socket, Data}, State=#redis{calls=Calls}) ->
inet:setopts(Socket, [{active, once}]),
{noreply, NewState};
handle_info({tcp_closed, Socket}, State=#redis{socket=Socket}) ->
{stop, erldis_client_tcp_host_socket_closed, State};
% japerk: shutdown Reason does not generate an error message
{stop, shutdown, State};
handle_info(_Info, State) -> {noreply, State}.


terminate(_Reason, State) ->
case State#redis.socket of
undefined ->
pass;
Socket ->
gen_tcp:close(Socket)
end,
ok.
undefined -> ok;
Socket -> gen_tcp:close(Socket)
end.


code_change(_OldVsn, State, _Extra) -> {ok, State}.
Expand Down
77 changes: 77 additions & 0 deletions src/erldis_dict.erl
@@ -0,0 +1,77 @@
-module(erldis_dict).

-export([append/3, append_list/3, erase/2, fetch/2, fetch_keys/2, find/2,
is_key/2, size/1, store/3, update/3, update/4,
update_counter/2, update_counter/3]).

% NOTE: use erldis_lists instead, fetch & find won't work for lists
append(Key, Value, Client) -> set_call(Client, rpush, Key, Value).

append_list(Key, Values, Client) ->
lists:foreach(fun(Value) -> append(Key, Value, Client) end, Values).

erase(Key, Client) -> scall(Client, del, [Key]).

fetch(Key, Client) ->
case scall(Client, get, [Key]) of
[nil] -> undefined;
[Value] -> Value
end.

% NOTE: this is only useful if keys have a known prefix
fetch_keys(Pattern, Client) -> scall(Client, keys, [Pattern]).

%filter(Pred, Client) -> ok.

find(Key, Client) ->
case fetch(Key, Client) of
undefined -> error;
Value -> {ok, Value}
end.

%fold(Fun, Acc0, Client) -> ok.

%from_list(List, Client) -> ok.

is_key(Key, Client) -> hd(scall(Client, exists, [Key])).

size(Client) ->
numeric_value(erldis_sync_client:scall(Client, dbsize)).

store(Key, [], Client) -> erase(Key, Client);
store(Key, Value, Client) -> set_call(Client, set, Key, Value).

%to_list(Client) -> ok.

% NOTE: update/3 & update/4 are not atomic

update(Key, Fun, Client) -> store(Key, Fun(fetch(Key, Client)), Client).

update(Key, Fun, Initial, Client) ->
case find(Key, Client) of
{ok, Value} -> store(Key, Fun(Value), Client);
error -> store(Key, Initial, Client)
end.

update_counter(Key, Client) -> update_counter(Key, 1, Client).

% NOTE: this returns new count value, not a modified dict
update_counter(Key, 1, Client) ->
numeric_value(scall(Client, incr, [Key]));
update_counter(Key, Incr, Client) ->
numeric_value(scall(Client, incrby, [Key, Incr])).

%%%%%%%%%%%%%
%% helpers %%
%%%%%%%%%%%%%

numeric_value([false]) -> 0;
numeric_value([true]) -> 1;
numeric_value([Val]) -> Val.

% TODO: copied from erldis_sets, abstract if possible, maybe use a macro

scall(Client, Cmd, Args) -> erldis_sync_client:scall(Client, Cmd, Args).

set_call(Client, Cmd, Key, Val) ->
erldis_sync_client:call(Client, Cmd, [[Key, length(Val)], [Val]]).

0 comments on commit df4d58d

Please sign in to comment.