Skip to content

Instantly share code, notes, and snippets.

@tilgovi
Created August 31, 2010 21:03
Show Gist options
  • Save tilgovi/559749 to your computer and use it in GitHub Desktop.
Save tilgovi/559749 to your computer and use it in GitHub Desktop.
diff --git a/src/couchdb/couch_db_updater.erl b/src/couchdb/couch_db_updater.erl
index 19a4c16..6099d0a 100644
--- a/src/couchdb/couch_db_updater.erl
+++ b/src/couchdb/couch_db_updater.erl
@@ -439,15 +439,42 @@ refresh_validate_doc_funs(Db) ->
% rev tree functions
-flush_trees(_Db, [], AccFlushedTrees) ->
- {ok, lists:reverse(AccFlushedTrees)};
-flush_trees(#db{fd=Fd,header=Header}=Db,
- [InfoUnflushed | RestUnflushed], AccFlushed) ->
- #full_doc_info{update_seq=UpdateSeq, rev_tree=Unflushed} = InfoUnflushed,
- Flushed = couch_key_tree:map(
- fun(_Rev, Value) ->
+flush_trees(_Db, [], AccDocInfo, []) ->
+ {ok, lists:reverse(AccDocInfo)};
+flush_trees(#db{fd=Fd,header=Header}, [], AccDocInfo, AccUnflushed) ->
+ {ok, NewSummaryPointers} =
+ case Header#db_header.disk_version < 4 of
+ true ->
+ couch_file:append_terms(Fd, AccUnflushed);
+ false ->
+ couch_file:append_terms_md5(Fd, AccUnflushed)
+ end,
+ {Flushed, []} =
+ lists:foldl(
+ fun(#full_doc_info{update_seq=UpdateSeq, rev_tree=RevTree}=DocInfo,
+ {FlushedInfoAcc, UnusedSummaryPointers}) ->
+ {FlushedRevTree, UnusedSummaryPointers2} =
+ couch_key_tree:mapfoldl(
+ fun(_Rev, Value, SummaryPointers) ->
+ case Value of
+ #doc{deleted=IsDeleted} ->
+ [NewSummaryPointer|Rest] = SummaryPointers,
+ {{IsDeleted, NewSummaryPointer, UpdateSeq}, Rest};
+ _ ->
+ {Value, SummaryPointers}
+ end
+ end, UnusedSummaryPointers, RevTree),
+ {[DocInfo#full_doc_info{rev_tree=FlushedRevTree}|FlushedInfoAcc],
+ UnusedSummaryPointers2}
+ end, {[], NewSummaryPointers}, AccDocInfo),
+ {ok, Flushed};
+flush_trees(#db{fd=Fd}=Db, [InfoUnflushed | RestUnflushed],
+ AccDocInfo, AccUnflushed) ->
+ #full_doc_info{rev_tree=RevTree} = InfoUnflushed,
+ AccUnflushed2 = couch_key_tree:foldl(
+ fun(_Rev, Value, Acc) ->
case Value of
- #doc{atts=Atts,deleted=IsDeleted}=Doc ->
+ #doc{atts=Atts}=Doc ->
% this node value is actually an unwritten document summary,
% write to disk.
% make sure the Fd in the written bins is the same Fd we are
@@ -468,20 +495,13 @@ flush_trees(#db{fd=Fd,header=Header}=Db,
" changed. Possibly retrying.", []),
throw(retry)
end,
- {ok, NewSummaryPointer} =
- case Header#db_header.disk_version < 4 of
- true ->
- couch_file:append_term(Fd, {Doc#doc.body, DiskAtts});
- false ->
- couch_file:append_term_md5(Fd, {Doc#doc.body, DiskAtts})
- end,
- {IsDeleted, NewSummaryPointer, UpdateSeq};
+ UnflushedBody = {Doc#doc.body, DiskAtts},
+ [UnflushedBody|Acc];
_ ->
- Value
+ Acc
end
- end, Unflushed),
- flush_trees(Db, RestUnflushed, [InfoUnflushed#full_doc_info{rev_tree=Flushed} | AccFlushed]).
-
+ end, AccUnflushed, RevTree),
+ flush_trees(Db, RestUnflushed, [InfoUnflushed | AccDocInfo], AccUnflushed2).
send_result(Client, Id, OriginalRevs, NewResult) ->
% used to send a result to the client
@@ -605,7 +625,7 @@ update_docs_int(Db, DocsList, NonRepDocs, MergeConflicts, FullCommit) ->
% Write out the document summaries (the bodies are stored in the nodes of
% the trees, the attachments are already written to disk)
- {ok, FlushedFullDocInfos} = flush_trees(Db2, NewFullDocInfos, []),
+ {ok, FlushedFullDocInfos} = flush_trees(Db2, NewFullDocInfos, [], []),
{IndexFullDocInfos, IndexDocInfos} =
new_index_entries(FlushedFullDocInfos, [], []),
diff --git a/src/couchdb/couch_file.erl b/src/couchdb/couch_file.erl
index 0a89171..f5e7530 100644
--- a/src/couchdb/couch_file.erl
+++ b/src/couchdb/couch_file.erl
@@ -27,6 +27,8 @@
-export([append_term/2, pread_term/2, pread_iolist/2, write_header/2]).
-export([pread_binary/2, read_header/1, truncate/2, upgrade_old_header/2]).
-export([append_term_md5/2,append_binary_md5/2]).
+-export([append_terms/2, append_terms_md5/2]).
+-export([append_binaries/2, append_binaries_md5/2]).
-export([init/1, terminate/2, handle_call/3, handle_cast/2, code_change/3, handle_info/2]).
-export([delete/2,delete/3,init_delete_dir/1]).
@@ -74,6 +76,11 @@ append_term(Fd, Term) ->
append_term_md5(Fd, Term) ->
append_binary_md5(Fd, term_to_binary(Term)).
+append_terms(Fd, Terms) ->
+ append_terms(Fd, lists:map(fun term_to_binary/1, Terms)).
+
+append_terms_md5(Fd, Terms) ->
+ append_binaries_md5(Fd, lists:map(fun term_to_binary/1, Terms)).
%%----------------------------------------------------------------------
%% Purpose: To append an Erlang binary to the end of the file.
@@ -93,6 +100,22 @@ append_binary_md5(Fd, Bin) ->
gen_server:call(Fd, {append_bin,
[<<1:1/integer,Size:31/integer>>, couch_util:md5(Bin), Bin]}, infinity).
+append_binaries(Fd, Bins) ->
+ gen_server:call(Fd, {append_bins,
+ lists:map(
+ fun(Bin) ->
+ Size = iolist_size(Bin),
+ [<<0:1/integer,Size:31/integer>>, Bin]
+ end, Bins)}, infinity).
+
+append_binaries_md5(Fd, Bins) ->
+ gen_server:call(Fd, {append_bins,
+ lists:map(
+ fun(Bin) ->
+ Size = iolist_size(Bin),
+ [<<1:1/integer,Size:31/integer>>, couch_util:md5(Bin), Bin]
+ end, Bins)}, infinity).
+
%%----------------------------------------------------------------------
%% Purpose: Reads a term from a file that was written with append_term
@@ -331,6 +354,19 @@ handle_call({append_bin, Bin}, _From, #file{fd=Fd, eof=Pos}=File) ->
Error ->
{reply, Error, File}
end;
+handle_call({append_bins, Bins}, _From, #file{fd=Fd, eof=Pos}=File) ->
+ {BlockPosList, NewPos} = lists:mapfoldl(
+ fun(Bin, OutPos) ->
+ Blocks = make_blocks(OutPos rem ?SIZE_BLOCK, Bin),
+ {{Blocks, OutPos}, OutPos+iolist_size(Blocks)}
+ end, Pos, Bins),
+ {BlockList, PosList} = lists:unzip(BlockPosList),
+ case file:write(Fd, BlockList) of
+ ok ->
+ {reply, {ok, PosList}, File#file{eof=NewPos}};
+ Error ->
+ {reply, Error, File}
+ end;
handle_call({write_header, Bin}, _From, #file{fd=Fd, eof=Pos}=File) ->
BinSize = size(Bin),
case Pos rem ?SIZE_BLOCK of
diff --git a/src/couchdb/couch_key_tree.erl b/src/couchdb/couch_key_tree.erl
index 4fe09bf..ad4d092 100644
--- a/src/couchdb/couch_key_tree.erl
+++ b/src/couchdb/couch_key_tree.erl
@@ -13,7 +13,7 @@
-module(couch_key_tree).
-export([merge/2, find_missing/2, get_key_leafs/2, get_full_key_paths/2, get/2]).
--export([map/2, get_all_leafs/1, count_leafs/1, remove_leafs/2,
+-export([map/2, foldl/3, mapfoldl/3, get_all_leafs/1, count_leafs/1, remove_leafs/2,
get_all_leafs_full/1,stem/2,map_leafs/2]).
% a key tree looks like this:
@@ -294,6 +294,33 @@ map_simple(Fun, Pos, [{Key, Value, SubTree} | RestTree]) ->
if SubTree == [] -> leaf; true -> branch end),
[{Key, Value2, map_simple(Fun, Pos + 1, SubTree)} | map_simple(Fun, Pos, RestTree)].
+foldl(_Fun, Acc, []) ->
+ Acc;
+foldl(Fun, Acc, [{Pos, Tree}|Rest]) ->
+ Acc2 = foldl_simple(Fun, Acc, Pos, [Tree]),
+ foldl(Fun, Acc2, Rest).
+
+foldl_simple(_Fun, Acc, _Pos, []) ->
+ Acc;
+foldl_simple(Fun, Acc, Pos, [{Key, Value, SubTree} | RestTree]) ->
+ Acc2 = Fun({Pos, Key}, Value, Acc),
+ Acc3 = foldl_simple(Fun, Acc2, Pos + 1, SubTree),
+ foldl_simple(Fun, Acc3, Pos, RestTree).
+
+mapfoldl(_Fun, Acc, []) ->
+ {[], Acc};
+mapfoldl(Fun, Acc, [{Pos, Tree}|Rest]) ->
+ {[NewTree], Acc2} = mapfoldl_simple(Fun, Acc, Pos, [Tree]),
+ {NewRest, Acc3} = mapfoldl(Fun, Acc2, Rest),
+ {[{Pos, NewTree} | NewRest], Acc3}.
+
+mapfoldl_simple(_Fun, Acc, _Pos, []) ->
+ {[], Acc};
+mapfoldl_simple(Fun, Acc, Pos, [{Key, Value, Subtree} | RestTree ]) ->
+ {Value2, Acc2} = Fun({Pos, Key}, Value, Acc),
+ {NewSubTree, Acc3} = mapfoldl_simple(Fun, Acc2, Pos, Subtree),
+ {NewRestTree, Acc4} = mapfoldl_simple(Fun, Acc3, Pos, RestTree),
+ {[{Key, Value2, NewSubTree} | NewRestTree], Acc4}.
map_leafs(_Fun, []) ->
[];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment