Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Added caching for BTree nodes (for both DBs and view indexes) and for…
… documents (per DB).

Caching settings are tunable in the .ini configuration.
  • Loading branch information
fdmanana committed Oct 9, 2010
1 parent 152f2b9 commit c6361d1
Show file tree
Hide file tree
Showing 13 changed files with 468 additions and 29 deletions.
1 change: 1 addition & 0 deletions configure.ac
Expand Up @@ -408,6 +408,7 @@ AC_CONFIG_FILES([src/erlang-oauth/Makefile])
AC_CONFIG_FILES([src/etap/Makefile])
AC_CONFIG_FILES([src/ibrowse/Makefile])
AC_CONFIG_FILES([src/mochiweb/Makefile])
AC_CONFIG_FILES([src/term_cache/Makefile])
AC_CONFIG_FILES([test/Makefile])
AC_CONFIG_FILES([test/bench/Makefile])
AC_CONFIG_FILES([test/etap/Makefile])
Expand Down
6 changes: 6 additions & 0 deletions etc/couchdb/default.ini.tpl.in
Expand Up @@ -12,6 +12,12 @@ os_process_timeout = 5000 ; 5 seconds. for view and external servers.
max_dbs_open = 100
delayed_commits = true ; set this to false to ensure an fsync before 201 Created is returned
uri_file = %localstaterundir%/couch.uri
database_btree_cache_size = 100 ; max number of cached BTree nodes per database (set to 0 to disable)
database_btree_cache_policy = lru ; must be lru or mru
view_btree_cache_size = 100 ; max number of cached BTree nodes per view group (set to 0 to disable)
view_btree_cache_policy = lru ; must be lru or mru
doc_cache_size = 0 ; max number of cached documents per database (set to 0 to disable)
doc_cache_policy = lru ; must be lru or mru

[httpd]
port = 5984
Expand Down
2 changes: 1 addition & 1 deletion src/Makefile.am
Expand Up @@ -10,4 +10,4 @@
## License for the specific language governing permissions and limitations under
## the License.

SUBDIRS = couchdb erlang-oauth etap ibrowse mochiweb
SUBDIRS = couchdb erlang-oauth etap ibrowse mochiweb term_cache
40 changes: 30 additions & 10 deletions src/couchdb/couch_btree.erl
Expand Up @@ -24,7 +24,8 @@
extract_kv = fun({Key, Value}) -> {Key, Value} end,
assemble_kv = fun(Key, Value) -> {Key, Value} end,
less = fun(A, B) -> A < B end,
reduce = nil
reduce = nil,
cache = nil
}).

extract(#btree{extract_kv=Extract}, Value) ->
Expand All @@ -38,7 +39,11 @@ less(#btree{less=Less}, A, B) ->

% pass in 'nil' for State if a new Btree.
open(State, Fd) ->
{ok, #btree{root=State, fd=Fd}}.
open(State, Fd, []).

open(State, Fd, Options) ->
{ok, set_options(#btree{root=State, fd=Fd}, Options)}.


set_options(Bt, []) ->
Bt;
Expand All @@ -49,10 +54,9 @@ set_options(Bt, [{join, Assemble}|Rest]) ->
set_options(Bt, [{less, Less}|Rest]) ->
set_options(Bt#btree{less=Less}, Rest);
set_options(Bt, [{reduce, Reduce}|Rest]) ->
set_options(Bt#btree{reduce=Reduce}, Rest).

open(State, Fd, Options) ->
{ok, set_options(#btree{root=State, fd=Fd}, Options)}.
set_options(Bt#btree{reduce=Reduce}, Rest);
set_options(Bt, [{cache, Cache}|Rest]) ->
set_options(Bt#btree{cache=Cache}, Rest).

get_state(#btree{root=Root}) ->
Root.
Expand Down Expand Up @@ -326,17 +330,33 @@ reduce_node(#btree{reduce=R}=Bt, kv_node, NodeList) ->
R(reduce, [assemble(Bt, K, V) || {K, V} <- NodeList]).


get_node(#btree{fd = Fd}, NodePos) ->
get_node(#btree{fd = Fd, cache = nil}, NodePos) ->
{ok, {NodeType, NodeList}} = couch_file:pread_term(Fd, NodePos),
{NodeType, NodeList}.
{NodeType, NodeList};
get_node(#btree{fd = Fd, cache = Cache}, NodePos) when is_pid(Cache) ->
case term_cache_trees:get(Cache, NodePos) of
{ok, Node} ->
Node;
not_found ->
{ok, {_Type, _NodeList} = Node} = couch_file:pread_term(Fd, NodePos),
ok = term_cache_trees:put(Cache, NodePos, Node),
Node
end.

write_node(Bt, NodeType, NodeList) ->
write_node(#btree{cache = Cache} = Bt, NodeType, NodeList) ->
% split up nodes into smaller sizes
NodeListList = chunkify(NodeList),
% now write out each chunk and return the KeyPointer pairs for those nodes
ResultList = [
begin
{ok, Pointer} = couch_file:append_term(Bt#btree.fd, {NodeType, ANodeList}),
Node = {NodeType, ANodeList},
{ok, Pointer} = couch_file:append_term(Bt#btree.fd, Node),
case Cache of
Pid when is_pid(Pid) ->
ok = term_cache_trees:put(Cache, Pointer, Node);
nil ->
ok
end,
{LastKey, _} = lists:last(ANodeList),
{LastKey, {Pointer, reduce_node(Bt, NodeType, ANodeList)}}
end
Expand Down
13 changes: 11 additions & 2 deletions src/couchdb/couch_db.erl
Expand Up @@ -1118,8 +1118,17 @@ doc_meta_info(#doc_info{high_seq=Seq,revs=[#rev_info{rev=Rev}|RestInfo]}, RevTre
read_doc(#db{fd=Fd}, OldStreamPointer) when is_tuple(OldStreamPointer) ->
% 09 UPGRADE CODE
couch_stream:old_read_term(Fd, OldStreamPointer);
read_doc(#db{fd=Fd}, Pos) ->
couch_file:pread_term(Fd, Pos).
read_doc(#db{fd = Fd, doc_cache = nil}, Pos) ->
couch_file:pread_term(Fd, Pos);
read_doc(#db{fd = Fd, doc_cache = Cache}, Pos) when is_pid(Cache) ->
case term_cache_trees:get(Cache, Pos) of
{ok, Data} ->
{ok, Data};
not_found ->
{ok, Data} = couch_file:pread_term(Fd, Pos),
ok = term_cache_trees:put(Cache, Pos, Data),
{ok, Data}
end.


doc_to_tree(#doc{revs={Start, RevIds}}=Doc) ->
Expand Down
7 changes: 5 additions & 2 deletions src/couchdb/couch_db.hrl
Expand Up @@ -174,7 +174,9 @@
waiting_delayed_commit = nil,
revs_limit = 1000,
fsync_options = [],
is_sys_db = false
is_sys_db = false,
btree_cache = nil,
doc_cache = nil
}).


Expand Down Expand Up @@ -233,7 +235,8 @@
current_seq=0,
purge_seq=0,
query_server=nil,
waiting_delayed_commit=nil
waiting_delayed_commit=nil,
btree_cache=nil
}).

-record(view,
Expand Down
37 changes: 33 additions & 4 deletions src/couchdb/couch_db_updater.erl
Expand Up @@ -52,7 +52,8 @@ terminate(_Reason, Db) ->
couch_file:close(Db#db.fd),
couch_util:shutdown_sync(Db#db.compactor_pid),
couch_util:shutdown_sync(Db#db.fd_ref_counter),
ok.
ok = term_cache_trees:stop(Db#db.btree_cache),
ok = term_cache_trees:stop(Db#db.doc_cache).

handle_call(get_db, _From, Db) ->
{reply, {ok, Db}, Db};
Expand Down Expand Up @@ -238,6 +239,10 @@ handle_info(delayed_commit, Db) ->
ok = gen_server:call(Db2#db.main_pid, {db_updated, Db2}),
{noreply, Db2}
end;
handle_info({'EXIT', Pid, Reason}, #db{btree_cache = Pid} = Db) ->
{stop, {btree_cache_died, Reason}, Db};
handle_info({'EXIT', Pid, Reason}, #db{doc_cache = Pid} = Db) ->
{stop, {doc_cache_died, Reason}, Db};
handle_info({'EXIT', _Pid, normal}, Db) ->
{noreply, Db};
handle_info({'EXIT', _Pid, Reason}, Db) ->
Expand Down Expand Up @@ -381,14 +386,17 @@ init_db(DbName, Filepath, Fd, Header0) ->
_ -> ok
end,

BtreeCache = new_btree_cache(),
{ok, IdBtree} = couch_btree:open(Header#db_header.fulldocinfo_by_id_btree_state, Fd,
[{split, fun(X) -> btree_by_id_split(X) end},
{join, fun(X,Y) -> btree_by_id_join(X,Y) end},
{reduce, fun(X,Y) -> btree_by_id_reduce(X,Y) end}]),
{reduce, fun(X,Y) -> btree_by_id_reduce(X,Y) end},
{cache, BtreeCache}]),
{ok, SeqBtree} = couch_btree:open(Header#db_header.docinfo_by_seq_btree_state, Fd,
[{split, fun(X) -> btree_by_seq_split(X) end},
{join, fun(X,Y) -> btree_by_seq_join(X,Y) end},
{reduce, fun(X,Y) -> btree_by_seq_reduce(X,Y) end}]),
{reduce, fun(X,Y) -> btree_by_seq_reduce(X,Y) end},
{cache, BtreeCache}]),
{ok, LocalDocsBtree} = couch_btree:open(Header#db_header.local_docs_btree_state, Fd),
case Header#db_header.security_ptr of
nil ->
Expand Down Expand Up @@ -418,7 +426,9 @@ init_db(DbName, Filepath, Fd, Header0) ->
security_ptr = SecurityPtr,
instance_start_time = StartTime,
revs_limit = Header#db_header.revs_limit,
fsync_options = FsyncOptions
fsync_options = FsyncOptions,
btree_cache = BtreeCache,
doc_cache = new_doc_cache()
}.


Expand Down Expand Up @@ -877,3 +887,22 @@ start_copy_compact(#db{name=Name,filepath=Filepath}=Db) ->
close_db(NewDb2),
gen_server:cast(Db#db.update_pid, {compact_done, CompactFile}).

new_btree_cache() ->
Size = list_to_integer(couch_util:trim(
couch_config:get("couchdb", "database_btree_cache_size", "100"))),
Policy = list_to_atom(couch_util:trim(
couch_config:get("couchdb", "database_btree_cache_policy", "lru"))),
new_cache(Size, Policy).

new_doc_cache() ->
Size = list_to_integer(
couch_util:trim(couch_config:get("couchdb", "doc_cache_size", "0"))),
Policy = list_to_atom(couch_util:trim(
couch_config:get("couchdb", "doc_cache_policy", "lru"))),
new_cache(Size, Policy).

new_cache(Size, Policy) when Size > 0 ->
{ok, Cache} = term_cache_trees:start_link([{size, Size}, {policy, Policy}]),
Cache;
new_cache(_, _) ->
nil.
29 changes: 22 additions & 7 deletions src/couchdb/couch_view_group.erl
Expand Up @@ -278,6 +278,10 @@ handle_info(delayed_commit, #group_state{db_name=DbName,group=Group}=State) ->
{noreply, State#group_state{waiting_commit=true}}
end;

handle_info({'EXIT', Pid, Reason},
#group_state{group = #group{btree_cache = Pid}} = State) ->
{stop, {btree_cache_died, Reason}, State};

handle_info({'EXIT', FromPid, {new_group, #group{db=Db}=Group}},
#group_state{db_name=DbName,
updater_pid=UpPid,
Expand Down Expand Up @@ -346,7 +350,7 @@ terminate(Reason, #group_state{updater_pid=Update, compactor_pid=Compact}=S) ->
reply_all(S, Reason),
couch_util:shutdown_sync(Update),
couch_util:shutdown_sync(Compact),
ok.
ok = term_cache_trees:stop((S#group_state.group)#group.btree_cache).

code_change(_OldVsn, State, _Extra) ->
{ok, State}.
Expand Down Expand Up @@ -579,7 +583,8 @@ init_group(Db, Fd, #group{def_lang=Lang,views=Views}=
Group, IndexHeader) ->
#index_header{seq=Seq, purge_seq=PurgeSeq,
id_btree_state=IdBtreeState, view_states=ViewStates} = IndexHeader,
{ok, IdBtree} = couch_btree:open(IdBtreeState, Fd),
BtreeCache = new_btree_cache(),
{ok, IdBtree} = couch_btree:open(IdBtreeState, Fd, [{cache, BtreeCache}]),
Views2 = lists:zipwith(
fun(BtreeState, #view{reduce_funs=RedFuns,options=Options}=View) ->
FunSrcs = [FunSrc || {_Name, FunSrc} <- RedFuns],
Expand All @@ -605,12 +610,22 @@ init_group(Db, Fd, #group{def_lang=Lang,views=Views}=
Less = fun(A,B) -> A < B end
end,
{ok, Btree} = couch_btree:open(BtreeState, Fd,
[{less, Less},
{reduce, ReduceFun}]),
[{less, Less}, {reduce, ReduceFun}, {cache, BtreeCache}]),
View#view{btree=Btree}
end,
ViewStates, Views),
Group#group{db=Db, fd=Fd, current_seq=Seq, purge_seq=PurgeSeq,
id_btree=IdBtree, views=Views2}.


id_btree=IdBtree, btree_cache = BtreeCache, views=Views2}.

new_btree_cache() ->
case list_to_integer(couch_util:trim(
couch_config:get("couchdb", "btree_cache_size", "100"))) of
Size when Size > 0 ->
Policy = list_to_atom(couch_util:trim(
couch_config:get("couchdb", "btree_cache_policy", "lru"))),
{ok, BtreeCache} = term_cache_trees:start_link(
[{size, Size}, {policy, Policy}]),
BtreeCache;
_ ->
nil
end.
31 changes: 31 additions & 0 deletions src/term_cache/Makefile.am
@@ -0,0 +1,31 @@
## Licensed under the Apache License, Version 2.0 (the "License"); you may not
## use this file except in compliance with the License. You may obtain a copy of
## the License at
##
## http://www.apache.org/licenses/LICENSE-2.0
##
## Unless required by applicable law or agreed to in writing, software
## distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
## WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
## License for the specific language governing permissions and limitations under
## the License.

termcacheebindir = $(localerlanglibdir)/term_cache/ebin

termcache_file_collection = \
term_cache_trees.erl

termcacheebin_make_generated_file_list = \
term_cache_trees.beam

termcacheebin_DATA = \
$(termcacheebin_make_generated_file_list)

EXTRA_DIST = \
$(termcache_file_collection)

CLEANFILES = \
$(termcacheebin_make_generated_file_list)

%.beam: %.erl
$(ERLC) $(ERLC_FLAGS) $<

0 comments on commit c6361d1

Please sign in to comment.