mirror of
https://github.com/valitydev/cache.git
synced 2024-11-06 01:45:19 +00:00
commit
6264aec468
@ -1,7 +1,7 @@
|
||||
{application, cache,
|
||||
[
|
||||
{description, "in-memory cache"},
|
||||
{vsn, "0.1.0"},
|
||||
{vsn, "git"},
|
||||
{modules, [
|
||||
cache,
|
||||
cache_sup,
|
||||
|
@ -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).
|
||||
|
||||
|
@ -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.
|
||||
|
||||
%%
|
||||
|
@ -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)]
|
||||
}
|
||||
}.
|
||||
}.
|
@ -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, [
|
||||
|
Loading…
Reference in New Issue
Block a user