mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-26 16:26:24 +01:00
Disable LRU caching algorithm for LDAP shared rosters
This commit is contained in:
parent
29fbe6d8e1
commit
e3afec9465
@ -53,6 +53,7 @@
|
|||||||
miss = 0,
|
miss = 0,
|
||||||
procs_num,
|
procs_num,
|
||||||
cache_missed,
|
cache_missed,
|
||||||
|
lru,
|
||||||
shrink_size}).
|
shrink_size}).
|
||||||
|
|
||||||
-define(PROCNAME, ?MODULE).
|
-define(PROCNAME, ?MODULE).
|
||||||
@ -62,6 +63,7 @@
|
|||||||
-define(MAX_SIZE, 1000).
|
-define(MAX_SIZE, 1000).
|
||||||
-define(WARN, true).
|
-define(WARN, true).
|
||||||
-define(CACHE_MISSED, true).
|
-define(CACHE_MISSED, true).
|
||||||
|
-define(LRU, true).
|
||||||
-define(LIFETIME, 600). %% 10 minutes
|
-define(LIFETIME, 600). %% 10 minutes
|
||||||
|
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
@ -109,7 +111,7 @@ delete(Tab, Key, F) ->
|
|||||||
dirty_delete(Tab, Key, F) ->
|
dirty_delete(Tab, Key, F) ->
|
||||||
F(),
|
F(),
|
||||||
?GEN_SERVER:call(
|
?GEN_SERVER:call(
|
||||||
get_proc_by_hash(Tab, Key), {dirty_delete, Key}, ?CALL_TIMEOUT).
|
get_proc_by_hash(Tab, Key), {cache_delete, Key}, ?CALL_TIMEOUT).
|
||||||
|
|
||||||
lookup(Tab, Key, F) ->
|
lookup(Tab, Key, F) ->
|
||||||
?GEN_SERVER:call(
|
?GEN_SERVER:call(
|
||||||
@ -117,20 +119,21 @@ lookup(Tab, Key, F) ->
|
|||||||
|
|
||||||
dirty_lookup(Tab, Key, F) ->
|
dirty_lookup(Tab, Key, F) ->
|
||||||
Proc = get_proc_by_hash(Tab, Key),
|
Proc = get_proc_by_hash(Tab, Key),
|
||||||
case ?GEN_SERVER:call(Proc, {dirty_lookup, Key}, ?CALL_TIMEOUT) of
|
case ?GEN_SERVER:call(Proc, {cache_lookup, Key}, ?CALL_TIMEOUT) of
|
||||||
{ok, '$cached_mismatch'} ->
|
{ok, '$cached_mismatch'} ->
|
||||||
error;
|
error;
|
||||||
{ok, Val} ->
|
{ok, Val} ->
|
||||||
{ok, Val};
|
{ok, Val};
|
||||||
_ ->
|
_ ->
|
||||||
case F() of
|
{Result, NewVal} = case F() of
|
||||||
{ok, Val} ->
|
{ok, Val} ->
|
||||||
?GEN_SERVER:call(
|
{{ok, Val}, Val};
|
||||||
Proc, {dirty_insert, Key, Val}, ?CALL_TIMEOUT),
|
_ ->
|
||||||
{ok, Val};
|
{error, '$cached_mismatch'}
|
||||||
_ ->
|
end,
|
||||||
error
|
?GEN_SERVER:call(
|
||||||
end
|
Proc, {cache_insert, Key, NewVal}, ?CALL_TIMEOUT),
|
||||||
|
Result
|
||||||
end.
|
end.
|
||||||
|
|
||||||
insert(Tab, Key, Val, F) ->
|
insert(Tab, Key, Val, F) ->
|
||||||
@ -140,7 +143,7 @@ insert(Tab, Key, Val, F) ->
|
|||||||
dirty_insert(Tab, Key, Val, F) ->
|
dirty_insert(Tab, Key, Val, F) ->
|
||||||
F(),
|
F(),
|
||||||
?GEN_SERVER:call(
|
?GEN_SERVER:call(
|
||||||
get_proc_by_hash(Tab, Key), {dirty_insert, Key, Val}, ?CALL_TIMEOUT).
|
get_proc_by_hash(Tab, Key), {cache_insert, Key, Val}, ?CALL_TIMEOUT).
|
||||||
|
|
||||||
info(Tab, Info) ->
|
info(Tab, Info) ->
|
||||||
case lists:map(
|
case lists:map(
|
||||||
@ -187,8 +190,9 @@ init([Tab, Opts, N, Pid]) ->
|
|||||||
{ok, do_setopts(State, Opts)}.
|
{ok, do_setopts(State, Opts)}.
|
||||||
|
|
||||||
handle_call({lookup, Key, F}, _From, #state{tab = T} = State) ->
|
handle_call({lookup, Key, F}, _From, #state{tab = T} = State) ->
|
||||||
|
CleanPrio = clean_priority(State#state.life_time),
|
||||||
case treap:lookup(Key, T) of
|
case treap:lookup(Key, T) of
|
||||||
{ok, _Prio, Val} ->
|
{ok, Prio, Val} when (State#state.lru == true) or (Prio =< CleanPrio) ->
|
||||||
Hits = State#state.hits,
|
Hits = State#state.hits,
|
||||||
NewState = treap_update(Key, Val, State#state{hits = Hits + 1}),
|
NewState = treap_update(Key, Val, State#state{hits = Hits + 1}),
|
||||||
case Val of
|
case Val of
|
||||||
@ -217,50 +221,36 @@ 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) ->
|
handle_call({cache_lookup, Key}, _From, #state{tab = T} = State) ->
|
||||||
|
CleanPrio = clean_priority(State#state.life_time),
|
||||||
case treap:lookup(Key, T) of
|
case treap:lookup(Key, T) of
|
||||||
{ok, _Prio, Val} ->
|
{ok, Prio, Val} when (State#state.lru == true) or (Prio =< CleanPrio) ->
|
||||||
Hits = State#state.hits,
|
Hits = State#state.hits,
|
||||||
NewState = treap_update(Key, Val, State#state{hits = Hits + 1}),
|
NewState = treap_update(Key, Val, State#state{hits = Hits + 1}),
|
||||||
{reply, {ok, Val}, NewState};
|
{reply, {ok, Val}, NewState};
|
||||||
_ ->
|
_ ->
|
||||||
Miss = State#state.miss,
|
Miss = State#state.miss,
|
||||||
NewState = State#state{miss = Miss + 1},
|
NewState = State#state{miss = Miss + 1},
|
||||||
if State#state.cache_missed ->
|
{reply, error, NewState}
|
||||||
{reply, error,
|
|
||||||
treap_insert(Key, '$cached_mismatch', NewState)};
|
|
||||||
true ->
|
|
||||||
{reply, error, NewState}
|
|
||||||
end
|
|
||||||
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} ->
|
||||||
{reply, ok, State};
|
{reply, ok, treap_update(Key, Val, State)};
|
||||||
Res ->
|
_ ->
|
||||||
case catch F() of
|
case catch F() of
|
||||||
{'EXIT', Reason} ->
|
{'EXIT', Reason} ->
|
||||||
print_error(insert, [Key, Val], Reason, State),
|
print_error(insert, [Key, Val], Reason, State),
|
||||||
{reply, ok, State};
|
{reply, ok, State};
|
||||||
_ ->
|
_ ->
|
||||||
NewState = case Res of
|
{reply, ok, treap_insert(Key, Val, State)}
|
||||||
{ok, _, _} ->
|
|
||||||
treap_update(Key, Val, State);
|
|
||||||
_ ->
|
|
||||||
treap_insert(Key, Val, State)
|
|
||||||
end,
|
|
||||||
{reply, ok, NewState}
|
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
handle_call({dirty_insert, Key, Val}, _From, #state{tab = T} = State) ->
|
handle_call({cache_insert, _, '$cached_mismatch'}, _From,
|
||||||
case treap:lookup(Key, T) of
|
#state{cache_missed = false} = State) ->
|
||||||
{ok, _Prio, Val} ->
|
{reply, ok, State};
|
||||||
{reply, ok, State};
|
handle_call({cache_insert, Key, Val}, _From, State) ->
|
||||||
{ok, _, _} ->
|
{reply, ok, treap_insert(Key, Val, State)};
|
||||||
{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
|
||||||
@ -270,7 +260,7 @@ handle_call({delete, Key, F}, _From, State) ->
|
|||||||
ok
|
ok
|
||||||
end,
|
end,
|
||||||
{reply, ok, NewState};
|
{reply, ok, NewState};
|
||||||
handle_call({dirty_delete, Key}, _From, State) ->
|
handle_call({cache_delete, Key}, _From, State) ->
|
||||||
NewState = treap_delete(Key, State),
|
NewState = treap_delete(Key, State),
|
||||||
{reply, ok, NewState};
|
{reply, ok, NewState};
|
||||||
handle_call({info, Info}, _From, State) ->
|
handle_call({info, Info}, _From, State) ->
|
||||||
@ -288,6 +278,7 @@ handle_call({info, Info}, _From, State) ->
|
|||||||
{hits, State#state.hits},
|
{hits, State#state.hits},
|
||||||
{miss, State#state.miss},
|
{miss, State#state.miss},
|
||||||
{cache_missed, State#state.cache_missed},
|
{cache_missed, State#state.cache_missed},
|
||||||
|
{lru, State#state.lru},
|
||||||
{warn, State#state.warn}];
|
{warn, State#state.warn}];
|
||||||
_ ->
|
_ ->
|
||||||
badarg
|
badarg
|
||||||
@ -372,10 +363,20 @@ do_setopts(#state{procs_num = N} = State, Opts) ->
|
|||||||
_ ->
|
_ ->
|
||||||
?CACHE_MISSED
|
?CACHE_MISSED
|
||||||
end,
|
end,
|
||||||
|
LRU = case proplists:get_value(
|
||||||
|
lru, Opts, State#state.lru) of
|
||||||
|
false ->
|
||||||
|
false;
|
||||||
|
true ->
|
||||||
|
true;
|
||||||
|
_ ->
|
||||||
|
?LRU
|
||||||
|
end,
|
||||||
State#state{max_size = MaxSize,
|
State#state{max_size = MaxSize,
|
||||||
warn = Warn,
|
warn = Warn,
|
||||||
life_time = LifeTime,
|
life_time = LifeTime,
|
||||||
cache_missed = CacheMissed,
|
cache_missed = CacheMissed,
|
||||||
|
lru = LRU,
|
||||||
shrink_size = ShrinkSize}.
|
shrink_size = ShrinkSize}.
|
||||||
|
|
||||||
get_proc_num() ->
|
get_proc_num() ->
|
||||||
@ -396,15 +397,36 @@ now_priority() ->
|
|||||||
{MSec, Sec, USec} = now(),
|
{MSec, Sec, USec} = now(),
|
||||||
-((MSec*1000000 + Sec)*1000000 + USec).
|
-((MSec*1000000 + Sec)*1000000 + USec).
|
||||||
|
|
||||||
treap_update(Key, Val, #state{tab = T} = State) ->
|
clean_priority(LifeTime) ->
|
||||||
Priority = now_priority(),
|
if is_integer(LifeTime) ->
|
||||||
NewT = treap:insert(Key, Priority, Val, T),
|
now_priority() + LifeTime;
|
||||||
State#state{tab = NewT}.
|
true ->
|
||||||
|
unlimited
|
||||||
|
end.
|
||||||
|
|
||||||
|
treap_update(Key, Val, #state{tab = T, lru = LRU} = State) ->
|
||||||
|
if LRU ->
|
||||||
|
Priority = now_priority(),
|
||||||
|
NewT = treap:insert(Key, Priority, Val, T),
|
||||||
|
State#state{tab = NewT};
|
||||||
|
true ->
|
||||||
|
State
|
||||||
|
end.
|
||||||
|
|
||||||
treap_insert(Key, Val, State) ->
|
treap_insert(Key, Val, State) ->
|
||||||
State1 = clean_treap(State),
|
State1 = clean_treap(State),
|
||||||
#state{size = Size} = State2 = shrink_treap(State1),
|
#state{size = Size} = State2 = shrink_treap(State1),
|
||||||
treap_update(Key, Val, State2#state{size = Size+1}).
|
T = State2#state.tab,
|
||||||
|
case treap:lookup(Key, T) of
|
||||||
|
{ok, _, Val} ->
|
||||||
|
treap_update(Key, Val, State2);
|
||||||
|
{ok, _, _} ->
|
||||||
|
NewT = treap:insert(Key, now_priority(), Val, T),
|
||||||
|
State2#state{tab = NewT};
|
||||||
|
_ ->
|
||||||
|
NewT = treap:insert(Key, now_priority(), Val, T),
|
||||||
|
State2#state{tab = NewT, size = Size+1}
|
||||||
|
end.
|
||||||
|
|
||||||
treap_delete(Key, #state{tab = T, size = Size} = State) ->
|
treap_delete(Key, #state{tab = T, size = Size} = State) ->
|
||||||
case treap:lookup(Key, T) of
|
case treap:lookup(Key, T) of
|
||||||
@ -492,9 +514,9 @@ print_error(Operation, Args, Reason, State) ->
|
|||||||
-define(lookup, dirty_lookup).
|
-define(lookup, dirty_lookup).
|
||||||
-define(delete, dirty_delete).
|
-define(delete, dirty_delete).
|
||||||
-define(insert, dirty_insert).
|
-define(insert, dirty_insert).
|
||||||
%% -define(lookup, lookup).
|
%%-define(lookup, lookup).
|
||||||
%% -define(delete, delete).
|
%%-define(delete, delete).
|
||||||
%% -define(insert, insert).
|
%%-define(insert, insert).
|
||||||
|
|
||||||
test() ->
|
test() ->
|
||||||
LifeTime = 2,
|
LifeTime = 2,
|
||||||
@ -504,7 +526,7 @@ test() ->
|
|||||||
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 LRU 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),
|
||||||
@ -531,13 +553,32 @@ test1() ->
|
|||||||
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),
|
||||||
io:format("** testing speed, this may take a while...~n"),
|
test2().
|
||||||
test2(1000),
|
|
||||||
test2(10000),
|
|
||||||
test2(100000),
|
|
||||||
test2(1000000).
|
|
||||||
|
|
||||||
test2(Iter) ->
|
test2() ->
|
||||||
|
LifeTime = 2,
|
||||||
|
ok = new(test_tbl, [{life_time, LifeTime},
|
||||||
|
{max_size, unlimited},
|
||||||
|
{lru, false}]),
|
||||||
|
check([]),
|
||||||
|
ok = ?insert(test_tbl, "key", "value", fun() -> ok end),
|
||||||
|
{ok, "value"} = ?lookup(test_tbl, "key", fun() -> error end),
|
||||||
|
check([{"key", "value"}]),
|
||||||
|
io:format("** waiting for ~p seconds to check if non-LRU works fine...~n",
|
||||||
|
[LifeTime+1]),
|
||||||
|
timer:sleep(timer:seconds(LifeTime+1)),
|
||||||
|
error = ?lookup(test_tbl, "key", fun() -> error end),
|
||||||
|
check([{"key", '$cached_mismatch'}]),
|
||||||
|
ok = ?insert(test_tbl, "key", "value1", fun() -> ok end),
|
||||||
|
check([{"key", "value1"}]),
|
||||||
|
delete(test_tbl),
|
||||||
|
io:format("** testing speed, this may take a while...~n"),
|
||||||
|
test3(1000),
|
||||||
|
test3(10000),
|
||||||
|
test3(100000),
|
||||||
|
test3(1000000).
|
||||||
|
|
||||||
|
test3(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(),
|
||||||
|
@ -239,9 +239,11 @@ init([Host, Opts]) ->
|
|||||||
State = parse_options(Host, Opts),
|
State = parse_options(Host, Opts),
|
||||||
cache_tab:new(shared_roster_ldap_user,
|
cache_tab:new(shared_roster_ldap_user,
|
||||||
[{max_size, State#state.user_cache_size},
|
[{max_size, State#state.user_cache_size},
|
||||||
|
{lru, false}, % We don't need LRU algorithm
|
||||||
{life_time, State#state.user_cache_validity}]),
|
{life_time, State#state.user_cache_validity}]),
|
||||||
cache_tab:new(shared_roster_ldap_group,
|
cache_tab:new(shared_roster_ldap_group,
|
||||||
[{max_size, State#state.group_cache_size},
|
[{max_size, State#state.group_cache_size},
|
||||||
|
{lru, false}, % We don't need LRU algorithm
|
||||||
{life_time, State#state.group_cache_validity}]),
|
{life_time, State#state.group_cache_validity}]),
|
||||||
ejabberd_hooks:add(roster_get, Host,
|
ejabberd_hooks:add(roster_get, Host,
|
||||||
?MODULE, get_user_roster, 70),
|
?MODULE, get_user_roster, 70),
|
||||||
|
Loading…
Reference in New Issue
Block a user