Merge pull request #1 from artefactop/otp_behaviour

Otp behaviour
This commit is contained in:
fogfish 2013-07-19 13:36:25 -07:00
commit 6264aec468
5 changed files with 130 additions and 26 deletions

View File

@ -1,7 +1,7 @@
{application, cache,
[
{description, "in-memory cache"},
{vsn, "0.1.0"},
{vsn, "git"},
{modules, [
cache,
cache_sup,

View File

@ -1,14 +1,33 @@
-module(cache).
-export([start/0]).
-export([start_link/2, i/1, put/3, get/2, evict/1, stop/1]).
-export([
start_link/0,
start_link/1,
start_link/2,
i/0,
i/1,
put/2,
put/3,
put_ttl/3,
put_ttl/4,
get/1,
get/2,
evict/0,
evict/1,
stop/0,
stop/1
]).
-define(NAME, cache).
%%
start() ->
AppFile = code:where_is_file(atom_to_list(?MODULE) ++ ".app"),
{ok, [{application, _, List}]} = file:consult(AppFile),
{ok, [{application, _, List}]} = file:consult(AppFile),
Apps = proplists:get_value(applications, List, []),
lists:foreach(
fun(X) ->
fun(X) ->
ok = case application:start(X) of
{error, {already_started, X}} -> ok;
Ret -> Ret
@ -18,32 +37,79 @@ start() ->
),
application:start(?MODULE).
%%
start_link() ->
Env = proplists:delete(included_applications, application:get_all_env()),
start_link(?NAME, Env).
start_link(Opts) ->
start_link(?NAME, Opts).
%%
%%
start_link(Name, Opts) ->
cache_bucket:start_link(Name, Opts).
cache_bucket:start_link(Name, Opts).
i() ->
i(?NAME).
i(Cache) ->
cache_bucket:i(Cache).
cache_bucket:i(Cache).
%%
%%
-spec put(Key::term(), Val::term()) -> ok.
put(Key, Val) ->
put(?NAME, Key, Val).
-spec put(Cache::atom(), Key::term(), Val::term()) -> ok.
put(Cache, Key, Val) ->
cache_bucket:put(Cache, Key, Val).
cache_bucket:put(Cache, Key, Val).
-spec put_ttl(Key::term(), Val::term(), TTL::non_neg_integer()) -> ok.
put_ttl(Key, Val, TTL) when is_integer(TTL) ->
put_ttl(?NAME, Key, Val, TTL).
-spec put_ttl(Cache::atom(), Key::term(), Val::term(), TTL::non_neg_integer()) -> ok.
put_ttl(Cache, Key, Val, TTL) when is_integer(TTL) ->
cache_bucket:put_ttl(Cache, Key, Val, TTL * 1000000).
%%
%%
-spec get(Key::term()) -> {ok, value} | none.
get(Key) ->
get(?NAME, Key).
-spec get(Cache::atom(), Key::term()) -> {ok, value} | none.
get(Cache, Key) ->
cache_bucket:get(Cache, Key).
cache_bucket:get(Cache, Key).
%%
%%
-spec evict() -> ok.
evict() ->
evict(?NAME).
-spec evict(Cache::atom()) -> ok.
evict(Cache) ->
cache_bucket:evict(Cache).
cache_bucket:evict(Cache).
%%
%%
-spec stop() -> ok.
stop() ->
stop(?NAME).
-spec stop(Cache::atom()) -> ok.
stop(Cache) ->
cache_bucket:stop(Cache).
cache_bucket:stop(Cache).

View File

@ -4,7 +4,7 @@
-author('Dmitry Kolesnikov <dmkolesnikov@gmail.com>').
-export([start_link/2]).
-export([i/1, put/3, get/2, evict/1, stop/1]).
-export([i/1, put/3, put_ttl/4, get/2, evict/1, stop/1]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-define(DEFAULT_POLICY, lru).
@ -85,6 +85,9 @@ i(Cache) ->
put(Cache, Key, Val) ->
gen_server:cast(Cache, {put, Key, Val}).
put_ttl(Cache, Key, Val, TTL) ->
gen_server:cast(Cache, {put, Key, Val, TTL}).
get(Cache, Key) ->
gen_server:call(Cache, {get, Key}, infinity).
@ -113,16 +116,16 @@ handle_call(info, _Tx, #cache{elements=E, access=A, hit=Hit, miss=Miss}=S) ->
],
{reply, {ok, Info}, S};
handle_call({get, Key}, _Tx, #cache{ttl=TTL, elements=E, access=A, hit=Hit, miss=Miss}=S) ->
handle_call({get, Key}, _Tx, #cache{elements=E, access=A, hit=Hit, miss=Miss}=S) ->
Now = usec(),
case ets:lookup(E, Key) of
[] ->
{reply, none, S#cache{miss=Miss + 1}};
[{Key, _Val, Expire0}] when Expire0 =< Now ->
[{Key, _Val, Expire0, _TTL}] when Expire0 =< Now ->
{reply, none, S#cache{miss=Miss + 1}};
[{Key, Val, Expire0}] ->
[{Key, Val, Expire0, TTL}] ->
Expire = Now + TTL,
ets:insert(E, {Key, Val, Expire}),
ets:insert(E, {Key, Val, Expire, TTL}),
ets:delete(A, Expire0),
ets:insert(A, {Expire, Key}),
{reply, {ok, Val}, S#cache{hit=Hit + 1}}
@ -136,9 +139,15 @@ handle_call(_Req, _Tx, S) ->
%%
%%
handle_cast({put, Key, Val, TTL}, #cache{elements=E, access=A}=S) ->
Expire = usec() + TTL,
ets:insert(E, {Key, Val, Expire, TTL}),
ets:insert(A, {Expire, Key}),
{noreply, S};
handle_cast({put, Key, Val}, #cache{ttl=TTL, elements=E, access=A}=S) ->
Expire = usec() + TTL,
ets:insert(E, {Key, Val, Expire}),
ets:insert(E, {Key, Val, Expire, TTL}),
ets:insert(A, {Expire, Key}),
{noreply, S};
@ -182,7 +191,7 @@ code_change(_Vsn, S, _Extra) ->
%%
%%
usec() ->
{Mega, Sec, USec} = erlang:now(),
{Mega, Sec, USec} = os:timestamp(),
(Mega * 1000000 + Sec) * 1000000 + USec.
%%

View File

@ -2,18 +2,32 @@
-module(cache_sup).
-behaviour(supervisor).
-author('Dmitry Kolesnikov <dmkolesnikov@gmail.com>').
-author('Jose Luis Navarro <jlnavarro@gmail.com>').
-export([start_link/0, init/1]).
%% API
-export([start_link/0]).
%% Supervisor callbacks
-export([init/1]).
%% Helper macro for declaring children of supervisor
-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).
%% ===================================================================
%% API functions
%% ===================================================================
%%
%%
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
{ok,
%% ===================================================================
%% Supervisor callbacks
%% ===================================================================
init([]) ->
{ok,
{
{one_for_one, 4, 1800},
[]
[?CHILD(cache, worker)]
}
}.
}.

View File

@ -54,6 +54,21 @@ lifecyle_3_test() ->
{ok, val4} = cache:get(test, key4),
cache:stop(test).
lifecyle_ttl_test() ->
cache:start(),
ok = cache:put_ttl(key1, val1, 1),
ok = cache:put_ttl(key2, val2, 1),
timer:sleep(1000),
ok = cache:put_ttl(key3, val3, 1),
ok = cache:put_ttl(key4, val4, 1),
timer:sleep(950),
none = cache:get(key1),
none = cache:get(key2),
{ok, val3} = cache:get(key3),
{ok, val4} = cache:get(key4),
cache:stop().
evict_lru_1_test() ->
cache:start(),
{ok, _} = cache:start_link(test, [