|
16 | 16 | -include_lib("couch/include/couch_db.hrl"). |
17 | 17 |
|
18 | 18 | new() -> |
19 | | - Updates = ets:new(couch_lru_updates, [ordered_set]), |
20 | | - Dbs = ets:new(couch_lru_dbs, [set]), |
21 | | - {0, Updates, Dbs}. |
22 | | - |
23 | | -insert(DbName, {Count, Updates, Dbs}) -> |
24 | | - update(DbName, {Count, Updates, Dbs}). |
25 | | - |
26 | | -update(DbName, {Count, Updates, Dbs}) -> |
27 | | - case ets:lookup(Dbs, DbName) of |
28 | | - [] -> |
29 | | - true = ets:insert(Dbs, {DbName, Count}); |
30 | | - [{DbName, OldCount}] -> |
31 | | - true = ets:update_element(Dbs, DbName, {2, Count}), |
32 | | - true = ets:delete(Updates, {OldCount, DbName}) |
33 | | - end, |
34 | | - true = ets:insert(Updates, {{Count, DbName}}), |
35 | | - {Count + 1, Updates, Dbs}. |
36 | | - |
37 | | - |
38 | | -close({Count, Updates, Dbs}) -> |
39 | | - case close_int(ets:next(Updates, {-1, <<>>}), Updates, Dbs) of |
40 | | - true -> |
41 | | - {true, {Count, Updates, Dbs}}; |
42 | | - false -> |
43 | | - false |
| 19 | + {gb_trees:empty(), dict:new()}. |
| 20 | + |
| 21 | +insert(DbName, {Tree0, Dict0}) -> |
| 22 | + Lru = erlang:now(), |
| 23 | + {gb_trees:insert(Lru, DbName, Tree0), dict:store(DbName, Lru, Dict0)}. |
| 24 | + |
| 25 | +update(DbName, {Tree0, Dict0}) -> |
| 26 | + case dict:find(DbName, Dict0) of |
| 27 | + {ok, Old} -> |
| 28 | + New = erlang:now(), |
| 29 | + Tree = gb_trees:insert(New, DbName, gb_trees:delete(Old, Tree0)), |
| 30 | + Dict = dict:store(DbName, New, Dict0), |
| 31 | + {Tree, Dict}; |
| 32 | + error -> |
| 33 | + % We closed this database before processing the update. Ignore |
| 34 | + {Tree0, Dict0} |
44 | 35 | end. |
45 | 36 |
|
| 37 | +%% Attempt to close the oldest idle database. |
| 38 | +close({Tree, _} = Cache) -> |
| 39 | + close_int(gb_trees:next(gb_trees:iterator(Tree)), Cache). |
46 | 40 |
|
47 | 41 | %% internals |
48 | 42 |
|
49 | | -close_int('$end_of_table', _Updates, _Dbs) -> |
| 43 | +close_int(none, _) -> |
50 | 44 | false; |
51 | | -close_int({_Count, DbName} = Key, Updates, Dbs) -> |
| 45 | +close_int({Lru, DbName, Iter}, {Tree, Dict} = Cache) -> |
52 | 46 | case ets:update_element(couch_dbs, DbName, {#db.fd_monitor, locked}) of |
53 | 47 | true -> |
54 | 48 | [#db{main_pid = Pid} = Db] = ets:lookup(couch_dbs, DbName), |
55 | 49 | case couch_db:is_idle(Db) of true -> |
56 | 50 | true = ets:delete(couch_dbs, DbName), |
57 | 51 | true = ets:delete(couch_dbs_pid_to_name, Pid), |
58 | 52 | exit(Pid, kill), |
59 | | - true = ets:delete(Updates, Key), |
60 | | - true = ets:delete(Dbs, DbName), |
61 | | - true; |
| 53 | + {true, {gb_trees:delete(Lru, Tree), dict:erase(DbName, Dict)}}; |
62 | 54 | false -> |
63 | 55 | true = ets:update_element(couch_dbs, DbName, {#db.fd_monitor, nil}), |
64 | 56 | couch_stats:increment_counter([couchdb, couch_server, lru_skip]), |
65 | | - close_int(ets:next(Updates, Key), Updates, Dbs) |
| 57 | + close_int(gb_trees:next(Iter), update(DbName, Cache)) |
66 | 58 | end; |
67 | 59 | false -> |
68 | | - true = ets:delete(Updates, Key), |
69 | | - true = ets:delete(Dbs, DbName), |
70 | | - close_int(ets:next(Updates, Key), Updates, Dbs) |
71 | | - end. |
| 60 | + NewTree = gb_trees:delete(Lru, Tree), |
| 61 | + NewIter = gb_trees:iterator(NewTree), |
| 62 | + close_int(gb_trees:next(NewIter), {NewTree, dict:erase(DbName, Dict)}) |
| 63 | +end. |
0 commit comments