25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-11-26 16:26:24 +01:00

Implemented dirty (non-atomic) functions; added copyright notice

This commit is contained in:
Evgeniy Khramtsov 2010-08-31 18:06:02 +10:00
parent 1bd43bbd2d
commit 7a3aa8f97d
2 changed files with 139 additions and 37 deletions

View File

@ -4,6 +4,25 @@
%%% Description : Caching key-value table %%% Description : Caching key-value table
%%% %%%
%%% Created : 29 Aug 2010 by Evgeniy Khramtsov <ekhramtsov@process-one.net> %%% 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). -module(cache_tab).
@ -13,7 +32,9 @@
%% API %% API
-export([start_link/4, new/2, delete/1, delete/3, lookup/3, -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 %% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
@ -83,15 +104,43 @@ delete(Tab) ->
delete(Tab, Key, F) -> delete(Tab, Key, F) ->
?GEN_SERVER:call( ?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) -> lookup(Tab, Key, F) ->
?GEN_SERVER:call( ?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) -> insert(Tab, Key, Val, F) ->
?GEN_SERVER:call( ?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) -> info(Tab, Info) ->
case lists:map( case lists:map(
@ -168,6 +217,22 @@ handle_call({lookup, Key, F}, _From, #state{tab = T} = State) ->
end end
end 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) -> handle_call({insert, Key, Val, F}, _From, #state{tab = T} = State) ->
case treap:lookup(Key, T) of case treap:lookup(Key, T) of
{ok, _Prio, Val} -> {ok, _Prio, Val} ->
@ -187,6 +252,15 @@ handle_call({insert, Key, Val, F}, _From, #state{tab = T} = State) ->
{reply, ok, NewState} {reply, ok, NewState}
end end
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) -> handle_call({delete, Key, F}, _From, State) ->
NewState = treap_delete(Key, State), NewState = treap_delete(Key, State),
case catch F() of case catch F() of
@ -196,6 +270,9 @@ handle_call({delete, Key, F}, _From, State) ->
ok ok
end, end,
{reply, ok, NewState}; {reply, ok, NewState};
handle_call({dirty_delete, Key}, _From, State) ->
NewState = treap_delete(Key, State),
{reply, ok, NewState};
handle_call({info, Info}, _From, State) -> handle_call({info, Info}, _From, State) ->
Res = case Info of Res = case Info of
size -> size ->
@ -412,23 +489,30 @@ print_error(Operation, Args, Reason, State) ->
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%%% Tests %%% Tests
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-define(lookup, dirty_lookup).
-define(delete, dirty_delete).
-define(insert, dirty_insert).
%% -define(lookup, lookup).
%% -define(delete, delete).
%% -define(insert, insert).
test() -> test() ->
LifeTime = 2, LifeTime = 2,
ok = new(test_tbl, [{life_time, LifeTime}, {max_size, unlimited}]), ok = new(test_tbl, [{life_time, LifeTime}, {max_size, unlimited}]),
check([]), check([]),
ok = insert(test_tbl, "key", "value", fun() -> ok end), ok = ?insert(test_tbl, "key", "value", fun() -> ok end),
check([{"key", "value"}]), check([{"key", "value"}]),
{ok, "value"} = lookup(test_tbl, "key", fun() -> error end), {ok, "value"} = ?lookup(test_tbl, "key", fun() -> error end),
check([{"key", "value"}]), check([{"key", "value"}]),
io:format("** waiting for ~p seconds to check if cleaner works fine...~n", io:format("** waiting for ~p seconds to check if cleaner works fine...~n",
[LifeTime+1]), [LifeTime+1]),
timer:sleep(timer:seconds(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"}]), check([{"key1", "value1"}]),
ok = delete(test_tbl, "key1", fun() -> ok end), ok = ?delete(test_tbl, "key1", fun() -> ok end),
{ok, "value"} = lookup(test_tbl, "key", fun() -> {ok, "value"} end), {ok, "value"} = ?lookup(test_tbl, "key", fun() -> {ok, "value"} end),
check([{"key", "value"}]), check([{"key", "value"}]),
ok = delete(test_tbl, "key", fun() -> ok end), ok = ?delete(test_tbl, "key", fun() -> ok end),
check([]), check([]),
%% io:format("** testing buggy callbacks...~n"), %% io:format("** testing buggy callbacks...~n"),
%% delete(test_tbl, "key", fun() -> erlang:error(badarg) end), %% 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}]), ok = new(test_tbl, [{max_size, MaxSize}, {shrink_size, 1}, {warn, false}]),
lists:foreach( lists:foreach(
fun(N) -> 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())), end, lists:seq(1, MaxSize*get_proc_num())),
{ok, MaxSize} = info(test_tbl, size), {ok, MaxSize} = info(test_tbl, size),
delete(test_tbl), delete(test_tbl),
success. io:format("** testing speed, this may take a while...~n"),
%% io:format("** testing speed, this may take a while...~n"), test2(1000),
%% test2(1000), test2(10000),
%% test2(10000), test2(100000),
%% test2(100000), test2(1000000).
%% test2(1000000).
%% test2(Iter) -> test2(Iter) ->
%% ok = new(test_tbl, [{max_size, unlimited}, {life_time, unlimited}]), ok = new(test_tbl, [{max_size, unlimited}, {life_time, unlimited}]),
%% L = lists:seq(1, Iter), L = lists:seq(1, Iter),
%% T1 = now(), T1 = now(),
%% lists:foreach( lists:foreach(
%% fun(N) -> fun(N) ->
%% ok = insert(test_tbl, N, N, fun() -> ok end) ok = ?insert(test_tbl, N, N, fun() -> ok end)
%% end, L), end, L),
%% io:format("** average insert (size = ~p): ~p usec~n", io:format("** average insert (size = ~p): ~p usec~n",
%% [Iter, round(timer:now_diff(now(), T1)/Iter)]), [Iter, round(timer:now_diff(now(), T1)/Iter)]),
%% T2 = now(), T2 = now(),
%% lists:foreach( lists:foreach(
%% fun(N) -> fun(N) ->
%% {ok, N} = lookup(test_tbl, N, fun() -> ok end) {ok, N} = ?lookup(test_tbl, N, fun() -> ok end)
%% end, L), end, L),
%% io:format("** average lookup (size = ~p): ~p usec~n", io:format("** average lookup (size = ~p): ~p usec~n",
%% [Iter, round(timer:now_diff(now(), T2)/Iter)]), [Iter, round(timer:now_diff(now(), T2)/Iter)]),
%% {ok, Iter} = info(test_tbl, size), {ok, Iter} = info(test_tbl, size),
%% delete(test_tbl). delete(test_tbl).
check(List) -> check(List) ->
Size = length(List), Size = length(List),

View File

@ -1,9 +1,28 @@
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
%%% File : cache_tab_sup.erl %%% File : cache_tab_sup.erl
%%% Author : Evgeniy Khramtsov <ekhramtsov@process-one.net> %%% Author : Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%% Description : %%% Description : Cache tables supervisor
%%% %%%
%%% Created : 30 Aug 2010 by Evgeniy Khramtsov <ekhramtsov@process-one.net> %%% 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). -module(cache_tab_sup).