Skip to content

Commit fb6faec

Browse files
committed
Revert couch_lru to use gb_trees
Recently couch_lru was changed to use ets tables. During eprof profiling it showed improved performance however recently in a larger test with more concurrent updates and 5000 max dbs open it showed a significant degradation compared to the previous (gb_tree-based) version. GH Issue apache#528
1 parent 1fa3f58 commit fb6faec

File tree

2 files changed

+27
-144
lines changed

2 files changed

+27
-144
lines changed

src/couch/src/couch_lru.erl

Lines changed: 27 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,56 +16,48 @@
1616
-include_lib("couch/include/couch_db.hrl").
1717

1818
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}
4435
end.
4536

37+
%% Attempt to close the oldest idle database.
38+
close({Tree, _} = Cache) ->
39+
close_int(gb_trees:next(gb_trees:iterator(Tree)), Cache).
4640

4741
%% internals
4842

49-
close_int('$end_of_table', _Updates, _Dbs) ->
43+
close_int(none, _) ->
5044
false;
51-
close_int({_Count, DbName} = Key, Updates, Dbs) ->
45+
close_int({Lru, DbName, Iter}, {Tree, Dict} = Cache) ->
5246
case ets:update_element(couch_dbs, DbName, {#db.fd_monitor, locked}) of
5347
true ->
5448
[#db{main_pid = Pid} = Db] = ets:lookup(couch_dbs, DbName),
5549
case couch_db:is_idle(Db) of true ->
5650
true = ets:delete(couch_dbs, DbName),
5751
true = ets:delete(couch_dbs_pid_to_name, Pid),
5852
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)}};
6254
false ->
6355
true = ets:update_element(couch_dbs, DbName, {#db.fd_monitor, nil}),
6456
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))
6658
end;
6759
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.

src/couch/test/couch_lru_tests.erl

Lines changed: 0 additions & 109 deletions
This file was deleted.

0 commit comments

Comments
 (0)