Permalink
Browse files

Refactor couch_server for performance

This is a backport of a change in BigCouch that changes the behaviour of
couch_server to be more performant as well as be more robust under heavy
load.

There are two main parts to this change. First, it removes the need to
have synchronous message passing between the couch_server gen_server and
every process that wants to open a database that is already open. It
does this by using a protected ets table that is used to by pass having
to talk directly to couch_server.

The second major portion of this patch rewrites the LRU handling inside
couch_server so that it doesn't turn into an N^2 loop when the
max_dbs_limit is reached and all databases are in use.

apply patch from @davisp edited for rcouch.
  • Loading branch information...
1 parent 90e96bf commit b4b1e632c6c21a9d6d8a8027b8d2587983d245d2 @benoitc benoitc committed Jul 4, 2012
@@ -0,0 +1,48 @@
+-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}),
+ 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.
Oops, something went wrong.

0 comments on commit b4b1e63

Please sign in to comment.