aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>2010-08-31 18:06:02 +1000
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>2010-08-31 18:06:02 +1000
commitc75b7b2b125f9b6dd199a2e6a4a309c4add893f3 (patch)
treea484a6e7157b8f55bec83330bf328baa8c879932
parentAdd IPv6 support to mod_irc (thanks to Matthias Schiffer)(EJAB-1298) (diff)
Implemented dirty (non-atomic) functions; added copyright notice
-rw-r--r--src/cache_tab.erl157
-rw-r--r--src/cache_tab_sup.erl21
2 files changed, 140 insertions, 38 deletions
diff --git a/src/cache_tab.erl b/src/cache_tab.erl
index e2551ec78..75ef6159a 100644
--- a/src/cache_tab.erl
+++ b/src/cache_tab.erl
@@ -4,6 +4,25 @@
%%% Description : Caching key-value table
%%%
%%% Created : 29 Aug 2010 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
+%%%
+%%%
+%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
+%%%
+%%% This program is free software; you can redistribute it and/or
+%%% modify it under the terms of the GNU General Public License as
+%%% published by the Free Software Foundation; either version 2 of the
+%%% License, or (at your option) any later version.
+%%%
+%%% This program is distributed in the hope that it will be useful,
+%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
+%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%%% General Public License for more details.
+%%%
+%%% You should have received a copy of the GNU General Public License
+%%% along with this program; if not, write to the Free Software
+%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+%%% 02111-1307 USA
+%%%
%%%-------------------------------------------------------------------
-module(cache_tab).
@@ -13,7 +32,9 @@
%% API
-export([start_link/4, new/2, delete/1, delete/3, lookup/3,
- insert/4, info/2, tab2list/1, setopts/2, all/0, test/0]).
+ insert/4, info/2, tab2list/1, setopts/2,
+ dirty_lookup/3, dirty_insert/4, dirty_delete/3,
+ all/0, test/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
@@ -83,15 +104,43 @@ delete(Tab) ->
delete(Tab, Key, F) ->
?GEN_SERVER:call(
- get_proc_by_hash(Tab, Key), {delete, Key, F}, ?CALL_TIMEOUT).
+ get_proc_by_hash(Tab, Key), {delete, Key, F}, ?CALL_TIMEOUT).
+
+dirty_delete(Tab, Key, F) ->
+ F(),
+ ?GEN_SERVER:call(
+ get_proc_by_hash(Tab, Key), {dirty_delete, Key}, ?CALL_TIMEOUT).
lookup(Tab, Key, F) ->
?GEN_SERVER:call(
- get_proc_by_hash(Tab, Key), {lookup, Key, F}, ?CALL_TIMEOUT).
+ get_proc_by_hash(Tab, Key), {lookup, Key, F}, ?CALL_TIMEOUT).
+
+dirty_lookup(Tab, Key, F) ->
+ Proc = get_proc_by_hash(Tab, Key),
+ case ?GEN_SERVER:call(Proc, {dirty_lookup, Key}, ?CALL_TIMEOUT) of
+ {ok, '$cached_mismatch'} ->
+ error;
+ {ok, Val} ->
+ {ok, Val};
+ _ ->
+ case F() of
+ {ok, Val} ->
+ ?GEN_SERVER:call(
+ Proc, {dirty_insert, Key, Val}, ?CALL_TIMEOUT),
+ {ok, Val};
+ _ ->
+ error
+ end
+ end.
insert(Tab, Key, Val, F) ->
?GEN_SERVER:call(
- get_proc_by_hash(Tab, Key), {insert, Key, Val, F}, ?CALL_TIMEOUT).
+ get_proc_by_hash(Tab, Key), {insert, Key, Val, F}, ?CALL_TIMEOUT).
+
+dirty_insert(Tab, Key, Val, F) ->
+ F(),
+ ?GEN_SERVER:call(
+ get_proc_by_hash(Tab, Key), {dirty_insert, Key, Val}, ?CALL_TIMEOUT).
info(Tab, Info) ->
case lists:map(
@@ -168,6 +217,22 @@ handle_call({lookup, Key, F}, _From, #state{tab = T} = State) ->
end
end
end;
+handle_call({dirty_lookup, Key}, _From, #state{tab = T} = State) ->
+ case treap:lookup(Key, T) of
+ {ok, _Prio, Val} ->
+ Hits = State#state.hits,
+ NewState = treap_update(Key, Val, State#state{hits = Hits + 1}),
+ {reply, {ok, Val}, NewState};
+ _ ->
+ Miss = State#state.miss,
+ NewState = State#state{miss = Miss + 1},
+ if State#state.cache_missed ->
+ {reply, error,
+ treap_insert(Key, '$cached_mismatch', NewState)};
+ true ->
+ {reply, error, NewState}
+ end
+ end;
handle_call({insert, Key, Val, F}, _From, #state{tab = T} = State) ->
case treap:lookup(Key, T) of
{ok, _Prio, Val} ->
@@ -187,6 +252,15 @@ handle_call({insert, Key, Val, F}, _From, #state{tab = T} = State) ->
{reply, ok, NewState}
end
end;
+handle_call({dirty_insert, Key, Val}, _From, #state{tab = T} = State) ->
+ case treap:lookup(Key, T) of
+ {ok, _Prio, Val} ->
+ {reply, ok, State};
+ {ok, _, _} ->
+ {reply, ok, treap_update(Key, Val, State)};
+ _ ->
+ {reply, ok, treap_insert(Key, Val, State)}
+ end;
handle_call({delete, Key, F}, _From, State) ->
NewState = treap_delete(Key, State),
case catch F() of
@@ -196,6 +270,9 @@ handle_call({delete, Key, F}, _From, State) ->
ok
end,
{reply, ok, NewState};
+handle_call({dirty_delete, Key}, _From, State) ->
+ NewState = treap_delete(Key, State),
+ {reply, ok, NewState};
handle_call({info, Info}, _From, State) ->
Res = case Info of
size ->
@@ -412,23 +489,30 @@ print_error(Operation, Args, Reason, State) ->
%%--------------------------------------------------------------------
%%% Tests
%%--------------------------------------------------------------------
+-define(lookup, dirty_lookup).
+-define(delete, dirty_delete).
+-define(insert, dirty_insert).
+%% -define(lookup, lookup).
+%% -define(delete, delete).
+%% -define(insert, insert).
+
test() ->
LifeTime = 2,
ok = new(test_tbl, [{life_time, LifeTime}, {max_size, unlimited}]),
check([]),
- ok = insert(test_tbl, "key", "value", fun() -> ok end),
+ ok = ?insert(test_tbl, "key", "value", fun() -> ok end),
check([{"key", "value"}]),
- {ok, "value"} = lookup(test_tbl, "key", fun() -> error end),
+ {ok, "value"} = ?lookup(test_tbl, "key", fun() -> error end),
check([{"key", "value"}]),
io:format("** waiting for ~p seconds to check if cleaner works fine...~n",
[LifeTime+1]),
timer:sleep(timer:seconds(LifeTime+1)),
- ok = insert(test_tbl, "key1", "value1", fun() -> ok end),
+ ok = ?insert(test_tbl, "key1", "value1", fun() -> ok end),
check([{"key1", "value1"}]),
- ok = delete(test_tbl, "key1", fun() -> ok end),
- {ok, "value"} = lookup(test_tbl, "key", fun() -> {ok, "value"} end),
+ ok = ?delete(test_tbl, "key1", fun() -> ok end),
+ {ok, "value"} = ?lookup(test_tbl, "key", fun() -> {ok, "value"} end),
check([{"key", "value"}]),
- ok = delete(test_tbl, "key", fun() -> ok end),
+ ok = ?delete(test_tbl, "key", fun() -> ok end),
check([]),
%% io:format("** testing buggy callbacks...~n"),
%% delete(test_tbl, "key", fun() -> erlang:error(badarg) end),
@@ -443,36 +527,35 @@ test1() ->
ok = new(test_tbl, [{max_size, MaxSize}, {shrink_size, 1}, {warn, false}]),
lists:foreach(
fun(N) ->
- ok = insert(test_tbl, N, N, fun() -> ok end)
+ ok = ?insert(test_tbl, N, N, fun() -> ok end)
end, lists:seq(1, MaxSize*get_proc_num())),
{ok, MaxSize} = info(test_tbl, size),
delete(test_tbl),
- success.
-%% io:format("** testing speed, this may take a while...~n"),
-%% test2(1000),
-%% test2(10000),
-%% test2(100000),
-%% test2(1000000).
-
-%% test2(Iter) ->
-%% ok = new(test_tbl, [{max_size, unlimited}, {life_time, unlimited}]),
-%% L = lists:seq(1, Iter),
-%% T1 = now(),
-%% lists:foreach(
-%% fun(N) ->
-%% ok = insert(test_tbl, N, N, fun() -> ok end)
-%% end, L),
-%% io:format("** average insert (size = ~p): ~p usec~n",
-%% [Iter, round(timer:now_diff(now(), T1)/Iter)]),
-%% T2 = now(),
-%% lists:foreach(
-%% fun(N) ->
-%% {ok, N} = lookup(test_tbl, N, fun() -> ok end)
-%% end, L),
-%% io:format("** average lookup (size = ~p): ~p usec~n",
-%% [Iter, round(timer:now_diff(now(), T2)/Iter)]),
-%% {ok, Iter} = info(test_tbl, size),
-%% delete(test_tbl).
+ io:format("** testing speed, this may take a while...~n"),
+ test2(1000),
+ test2(10000),
+ test2(100000),
+ test2(1000000).
+
+test2(Iter) ->
+ ok = new(test_tbl, [{max_size, unlimited}, {life_time, unlimited}]),
+ L = lists:seq(1, Iter),
+ T1 = now(),
+ lists:foreach(
+ fun(N) ->
+ ok = ?insert(test_tbl, N, N, fun() -> ok end)
+ end, L),
+ io:format("** average insert (size = ~p): ~p usec~n",
+ [Iter, round(timer:now_diff(now(), T1)/Iter)]),
+ T2 = now(),
+ lists:foreach(
+ fun(N) ->
+ {ok, N} = ?lookup(test_tbl, N, fun() -> ok end)
+ end, L),
+ io:format("** average lookup (size = ~p): ~p usec~n",
+ [Iter, round(timer:now_diff(now(), T2)/Iter)]),
+ {ok, Iter} = info(test_tbl, size),
+ delete(test_tbl).
check(List) ->
Size = length(List),
diff --git a/src/cache_tab_sup.erl b/src/cache_tab_sup.erl
index d417c5488..a49593f5e 100644
--- a/src/cache_tab_sup.erl
+++ b/src/cache_tab_sup.erl
@@ -1,9 +1,28 @@
%%%-------------------------------------------------------------------
%%% File : cache_tab_sup.erl
%%% Author : Evgeniy Khramtsov <ekhramtsov@process-one.net>
-%%% Description :
+%%% Description : Cache tables supervisor
%%%
%%% Created : 30 Aug 2010 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
+%%%
+%%%
+%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
+%%%
+%%% This program is free software; you can redistribute it and/or
+%%% modify it under the terms of the GNU General Public License as
+%%% published by the Free Software Foundation; either version 2 of the
+%%% License, or (at your option) any later version.
+%%%
+%%% This program is distributed in the hope that it will be useful,
+%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
+%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%%% General Public License for more details.
+%%%
+%%% You should have received a copy of the GNU General Public License
+%%% along with this program; if not, write to the Free Software
+%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+%%% 02111-1307 USA
+%%%
%%%-------------------------------------------------------------------
-module(cache_tab_sup).