mirror of
https://github.com/processone/ejabberd.git
synced 2024-12-22 17:28:25 +01:00
Implemented dirty (non-atomic) functions; added copyright notice
This commit is contained in:
parent
c754c91ad1
commit
c75b7b2b12
@ -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),
|
||||||
|
@ -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).
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user