1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
-module(couch_lru).
-export([new/0, insert/2, update/2, close/1]).
-include("couch_db.hrl").
new() ->
{gb_trees:empty(), dict:new()}.
insert(DbName, {Tree0, Dict0}) ->
Lru = now(),
{gb_trees:insert(Lru, DbName, Tree0), dict:store(DbName, Lru, Dict0)}.
update(DbName, {Tree0, Dict0}) ->
case dict:find(DbName, Dict0) of
{ok, Old} ->
New = now(),
Tree = gb_trees:insert(New, DbName, gb_trees:delete(Old, Tree0)),
Dict = dict:store(DbName, New, Dict0),
{Tree, Dict};
error ->
% We closed this database before processing the update. Ignore
{Tree0, Dict0}
end.
close({Tree, _} = Cache) ->
close_int(gb_trees:next(gb_trees:iterator(Tree)), Cache).
%% internals
close_int(none, _) ->
erlang:error(all_dbs_active);
close_int({Lru, DbName, Iter}, {Tree, Dict} = Cache) ->
case ets:update_element(couch_dbs, DbName, {#db.fd_monitor, locked}) of
true ->
[#db{main_pid = Pid} = Db] = ets:lookup(couch_dbs, DbName),
case couch_db:is_idle(Db) of true ->
true = ets:delete(couch_dbs, DbName),
exit(Pid, kill),
{gb_trees:delete(Lru, Tree), dict:erase(DbName, Dict)};
false ->
true = ets:update_element(couch_dbs, DbName, {#db.fd_monitor, nil}),
twig:log(warn, "~p old active ~s", [?MODULE, Db#db.name]),
close_int(gb_trees:next(Iter), update(DbName, Cache))
end;
false ->
NewTree = gb_trees:delete(Lru, Tree),
NewIter = gb_trees:iterator(NewTree),
close_int(gb_trees:next(NewIter), {NewTree, dict:erase(DbName, Dict)})
end.
|