You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by fd...@apache.org on 2011/04/20 20:29:33 UTC
svn commit: r1095477 - in /couchdb/trunk: share/www/script/test/
src/couchdb/ test/etap/
Author: fdmanana
Date: Wed Apr 20 18:29:33 2011
New Revision: 1095477
URL: http://svn.apache.org/viewvc?rev=1095477&view=rev
Log:
Track used space for database and view index files
The database and view group info URIs now expose a new field, named "data_size",
which corresponds to the number of bytes used by the current data snapshot.
Users can now use this value, compare it to the "disk_size" value (total file size)
and decide whether or not to trigger a compaction based on this comparison.
This new value is an approximation and therefore not 100% accurate (but close enough).
Closes COUCHDB-1132.
Special thanks to Adam Kocoloski and Robert Dionne for they're good work on this
feature as well.
Modified:
couchdb/trunk/share/www/script/test/compact.js
couchdb/trunk/share/www/script/test/view_compaction.js
couchdb/trunk/src/couchdb/couch_btree.erl
couchdb/trunk/src/couchdb/couch_db.erl
couchdb/trunk/src/couchdb/couch_db.hrl
couchdb/trunk/src/couchdb/couch_db_updater.erl
couchdb/trunk/src/couchdb/couch_doc.erl
couchdb/trunk/src/couchdb/couch_file.erl
couchdb/trunk/src/couchdb/couch_key_tree.erl
couchdb/trunk/src/couchdb/couch_stream.erl
couchdb/trunk/src/couchdb/couch_view_compactor.erl
couchdb/trunk/src/couchdb/couch_view_group.erl
couchdb/trunk/test/etap/010-file-basics.t
couchdb/trunk/test/etap/011-file-headers.t
couchdb/trunk/test/etap/020-btree-basics.t
Modified: couchdb/trunk/share/www/script/test/compact.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/compact.js?rev=1095477&r1=1095476&r2=1095477&view=diff
==============================================================================
--- couchdb/trunk/share/www/script/test/compact.js (original)
+++ couchdb/trunk/share/www/script/test/compact.js Wed Apr 20 18:29:33 2011
@@ -31,8 +31,12 @@ couchTests.compact = function(debug) {
T(db.save(binAttDoc).ok);
var originalsize = db.info().disk_size;
+ var originaldatasize = db.info().data_size;
var start_time = db.info().instance_start_time;
+ TEquals("number", typeof originaldatasize, "data_size is a number");
+ T(originaldatasize < originalsize, "data size is < then db file size");
+
for(var i in docs) {
db.deleteDoc(docs[i]);
}
@@ -55,5 +59,7 @@ couchTests.compact = function(debug) {
T(xhr.getResponseHeader("Content-Type") == "text/plain");
T(db.info().doc_count == 1);
T(db.info().disk_size < deletesize);
+ TEquals("number", typeof db.info().data_size, "data_size is a number");
+ T(db.info().data_size < db.info().disk_size, "data size is < then db file size");
};
Modified: couchdb/trunk/share/www/script/test/view_compaction.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/view_compaction.js?rev=1095477&r1=1095476&r2=1095477&view=diff
==============================================================================
--- couchdb/trunk/share/www/script/test/view_compaction.js (original)
+++ couchdb/trunk/share/www/script/test/view_compaction.js Wed Apr 20 18:29:33 2011
@@ -81,6 +81,10 @@ couchTests.view_compaction = function(de
T(resp.view_index.update_seq === 3001);
var disk_size_before_compact = resp.view_index.disk_size;
+ var data_size_before_compact = resp.view_index.data_size;
+
+ TEquals("number", typeof data_size_before_compact, "data size is a number");
+ T(data_size_before_compact < disk_size_before_compact, "data size < file size");
// compact view group
var xhr = CouchDB.request("POST", "/" + db.name + "/_compact" + "/foo");
@@ -101,4 +105,6 @@ couchTests.view_compaction = function(de
resp = db.designInfo("_design/foo");
T(resp.view_index.update_seq === 3001);
T(resp.view_index.disk_size < disk_size_before_compact);
+ TEquals("number", typeof resp.view_index.data_size, "data size is a number");
+ T(resp.view_index.data_size < resp.view_index.disk_size, "data size < file size");
};
\ No newline at end of file
Modified: couchdb/trunk/src/couchdb/couch_btree.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_btree.erl?rev=1095477&r1=1095476&r2=1095477&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_btree.erl (original)
+++ couchdb/trunk/src/couchdb/couch_btree.erl Wed Apr 20 18:29:33 2011
@@ -13,7 +13,7 @@
-module(couch_btree).
-export([open/2, open/3, query_modify/4, add/2, add_remove/3]).
--export([fold/4, full_reduce/1, final_reduce/2, foldl/3, foldl/4]).
+-export([fold/4, full_reduce/1, final_reduce/2, size/1, foldl/3, foldl/4]).
-export([fold_reduce/4, lookup/2, get_state/1, set_options/2]).
-include("couch_db.hrl").
@@ -92,8 +92,16 @@ fold_reduce(#btree{root=Root}=Bt, Fun, A
full_reduce(#btree{root=nil,reduce=Reduce}) ->
{ok, Reduce(reduce, [])};
-full_reduce(#btree{root={_P, Red}}) ->
- {ok, Red}.
+full_reduce(#btree{root=Root}) ->
+ {ok, element(2, Root)}.
+
+size(#btree{root = nil}) ->
+ 0;
+size(#btree{root = {_P, _Red}}) ->
+ % pre 1.2 format
+ nil;
+size(#btree{root = {_P, _Red, Size}}) ->
+ Size.
% wraps a 2 arity function with the proper 3 arity function
convert_fun_arity(Fun) when is_function(Fun, 2) ->
@@ -150,7 +158,7 @@ fold(#btree{root=Root}=Bt, Fun, Acc, Opt
end,
case Result of
{ok, Acc2}->
- {_P, FullReduction} = Root,
+ FullReduction = element(2, Root),
{ok, {[], [FullReduction]}, Acc2};
{stop, LastReduction, Acc2} ->
{ok, LastReduction, Acc2}
@@ -202,7 +210,8 @@ lookup(#btree{root=Root, less=Less}=Bt,
lookup(_Bt, nil, Keys) ->
{ok, [{Key, not_found} || Key <- Keys]};
-lookup(Bt, {Pointer, _Reds}, Keys) ->
+lookup(Bt, Node, Keys) ->
+ Pointer = element(1, Node),
{NodeType, NodeList} = get_node(Bt, Pointer),
case NodeType of
kp_node ->
@@ -292,7 +301,8 @@ modify_node(Bt, RootPointerInfo, Actions
nil ->
NodeType = kv_node,
NodeList = [];
- {Pointer, _Reds} ->
+ _Tuple ->
+ Pointer = element(1, RootPointerInfo),
{NodeType, NodeList} = get_node(Bt, Pointer)
end,
NodeTuple = list_to_tuple(NodeList),
@@ -316,10 +326,21 @@ modify_node(Bt, RootPointerInfo, Actions
reduce_node(#btree{reduce=nil}, _NodeType, _NodeList) ->
[];
reduce_node(#btree{reduce=R}, kp_node, NodeList) ->
- R(rereduce, [Red || {_K, {_P, Red}} <- NodeList]);
+ R(rereduce, [element(2, Node) || {_K, Node} <- NodeList]);
reduce_node(#btree{reduce=R}=Bt, kv_node, NodeList) ->
R(reduce, [assemble(Bt, K, V) || {K, V} <- NodeList]).
+reduce_tree_size(kv_node, NodeSize, _KvList) ->
+ NodeSize;
+reduce_tree_size(kp_node, NodeSize, []) ->
+ NodeSize;
+reduce_tree_size(kp_node, _NodeSize, [{_K, {_P, _Red}} | _]) ->
+ % pre 1.2 format
+ nil;
+reduce_tree_size(kp_node, _NodeSize, [{_K, {_P, _Red, nil}} | _]) ->
+ nil;
+reduce_tree_size(kp_node, NodeSize, [{_K, {_P, _Red, Sz}} | NodeList]) ->
+ reduce_tree_size(kp_node, NodeSize + Sz, NodeList).
get_node(#btree{fd = Fd}, NodePos) ->
{ok, {NodeType, NodeList}} = couch_file:pread_term(Fd, NodePos),
@@ -331,9 +352,10 @@ write_node(Bt, NodeType, 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}),
+ {ok, Pointer, Size} = couch_file:append_term(Bt#btree.fd, {NodeType, ANodeList}),
{LastKey, _} = lists:last(ANodeList),
- {LastKey, {Pointer, reduce_node(Bt, NodeType, ANodeList)}}
+ SubTreeSize = reduce_tree_size(NodeType, Size, ANodeList),
+ {LastKey, {Pointer, reduce_node(Bt, NodeType, ANodeList), SubTreeSize}}
end
||
ANodeList <- NodeListList
@@ -449,8 +471,9 @@ modify_kvnode(Bt, NodeTuple, LowerBound,
reduce_stream_node(_Bt, _Dir, nil, _KeyStart, _KeyEnd, GroupedKey, GroupedKVsAcc,
GroupedRedsAcc, _KeyGroupFun, _Fun, Acc) ->
{ok, Acc, GroupedRedsAcc, GroupedKVsAcc, GroupedKey};
-reduce_stream_node(Bt, Dir, {P, _R}, KeyStart, KeyEnd, GroupedKey, GroupedKVsAcc,
+reduce_stream_node(Bt, Dir, Node, KeyStart, KeyEnd, GroupedKey, GroupedKVsAcc,
GroupedRedsAcc, KeyGroupFun, Fun, Acc) ->
+ P = element(1, Node),
case get_node(Bt, P) of
{kp_node, NodeList} ->
reduce_stream_kp_node(Bt, Dir, NodeList, KeyStart, KeyEnd, GroupedKey,
@@ -559,7 +582,7 @@ reduce_stream_kp_node2(Bt, Dir, NodeList
[FirstGrouped | RestGrouped] = lists:reverse(Grouped0),
{RestGrouped, [FirstGrouped | Ungrouped0]}
end,
- GroupedReds = [R || {_, {_,R}} <- GroupedNodes],
+ GroupedReds = [element(2, Node) || {_, Node} <- GroupedNodes],
case UngroupedNodes of
[{_Key, NodeInfo}|RestNodes] ->
{ok, Acc2, GroupedRedsAcc2, GroupedKVsAcc2, GroupedKey2} =
@@ -576,7 +599,8 @@ adjust_dir(fwd, List) ->
adjust_dir(rev, List) ->
lists:reverse(List).
-stream_node(Bt, Reds, {Pointer, _Reds}, StartKey, InRange, Dir, Fun, Acc) ->
+stream_node(Bt, Reds, Node, StartKey, InRange, Dir, Fun, Acc) ->
+ Pointer = element(1, Node),
{NodeType, NodeList} = get_node(Bt, Pointer),
case NodeType of
kp_node ->
@@ -585,7 +609,8 @@ stream_node(Bt, Reds, {Pointer, _Reds},
stream_kv_node(Bt, Reds, adjust_dir(Dir, NodeList), StartKey, InRange, Dir, Fun, Acc)
end.
-stream_node(Bt, Reds, {Pointer, _Reds}, InRange, Dir, Fun, Acc) ->
+stream_node(Bt, Reds, Node, InRange, Dir, Fun, Acc) ->
+ Pointer = element(1, Node),
{NodeType, NodeList} = get_node(Bt, Pointer),
case NodeType of
kp_node ->
@@ -596,8 +621,9 @@ stream_node(Bt, Reds, {Pointer, _Reds},
stream_kp_node(_Bt, _Reds, [], _InRange, _Dir, _Fun, Acc) ->
{ok, Acc};
-stream_kp_node(Bt, Reds, [{_Key, {Pointer, Red}} | Rest], InRange, Dir, Fun, Acc) ->
- case stream_node(Bt, Reds, {Pointer, Red}, InRange, Dir, Fun, Acc) of
+stream_kp_node(Bt, Reds, [{_Key, Node} | Rest], InRange, Dir, Fun, Acc) ->
+ Red = element(2, Node),
+ case stream_node(Bt, Reds, Node, InRange, Dir, Fun, Acc) of
{ok, Acc2} ->
stream_kp_node(Bt, [Red | Reds], Rest, InRange, Dir, Fun, Acc2);
{stop, LastReds, Acc2} ->
@@ -606,10 +632,12 @@ stream_kp_node(Bt, Reds, [{_Key, {Pointe
drop_nodes(_Bt, Reds, _StartKey, []) ->
{Reds, []};
-drop_nodes(Bt, Reds, StartKey, [{NodeKey, {Pointer, Red}} | RestKPs]) ->
+drop_nodes(Bt, Reds, StartKey, [{NodeKey, Node} | RestKPs]) ->
case less(Bt, NodeKey, StartKey) of
- true -> drop_nodes(Bt, [Red | Reds], StartKey, RestKPs);
- false -> {Reds, [{NodeKey, {Pointer, Red}} | RestKPs]}
+ true ->
+ drop_nodes(Bt, [element(2, Node) | Reds], StartKey, RestKPs);
+ false ->
+ {Reds, [{NodeKey, Node} | RestKPs]}
end.
stream_kp_node(Bt, Reds, KPs, StartKey, InRange, Dir, Fun, Acc) ->
@@ -626,16 +654,17 @@ stream_kp_node(Bt, Reds, KPs, StartKey,
% everything sorts before it
{Reds, KPs};
{RevBefore, [FirstAfter | Drop]} ->
- {[Red || {_K,{_P,Red}} <- Drop] ++ Reds,
+ {[element(2, Node) || {_K, Node} <- Drop] ++ Reds,
[FirstAfter | lists:reverse(RevBefore)]}
end
end,
case NodesToStream of
[] ->
{ok, Acc};
- [{_Key, {Pointer, Red}} | Rest] ->
- case stream_node(Bt, NewReds, {Pointer, Red}, StartKey, InRange, Dir, Fun, Acc) of
+ [{_Key, Node} | Rest] ->
+ case stream_node(Bt, NewReds, Node, StartKey, InRange, Dir, Fun, Acc) of
{ok, Acc2} ->
+ Red = element(2, Node),
stream_kp_node(Bt, [Red | NewReds], Rest, InRange, Dir, Fun, Acc2);
{stop, LastReds, Acc2} ->
{stop, LastReds, Acc2}
Modified: couchdb/trunk/src/couchdb/couch_db.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_db.erl?rev=1095477&r1=1095476&r2=1095477&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_db.erl (original)
+++ couchdb/trunk/src/couchdb/couch_db.erl Wed Apr 20 18:29:33 2011
@@ -252,23 +252,38 @@ get_db_info(Db) ->
update_seq=SeqNum,
name=Name,
instance_start_time=StartTime,
- committed_update_seq=CommittedUpdateSeq} = Db,
+ committed_update_seq=CommittedUpdateSeq,
+ fulldocinfo_by_id_btree = IdBtree,
+ docinfo_by_seq_btree = SeqBtree
+ } = Db,
{ok, Size} = couch_file:bytes(Fd),
- {ok, {Count, DelCount}} = couch_btree:full_reduce(by_id_btree(Db)),
+ {ok, DbReduction} = couch_btree:full_reduce(by_id_btree(Db)),
InfoList = [
{db_name, Name},
- {doc_count, Count},
- {doc_del_count, DelCount},
+ {doc_count, element(1, DbReduction)},
+ {doc_del_count, element(2, DbReduction)},
{update_seq, SeqNum},
{purge_seq, couch_db:get_purge_seq(Db)},
{compact_running, Compactor/=nil},
{disk_size, Size},
+ {data_size, db_data_size(
+ couch_btree:size(SeqBtree), couch_btree:size(IdBtree), DbReduction)},
{instance_start_time, StartTime},
{disk_format_version, DiskVersion},
{committed_update_seq, CommittedUpdateSeq}
],
{ok, InfoList}.
+db_data_size(nil, _, _) ->
+ null;
+db_data_size(_, nil, _) ->
+ null;
+db_data_size(_, _, {_Count, _DelCount}) ->
+ % pre 1.2 format, upgraded on compaction
+ null;
+db_data_size(SeqBtreeSize, IdBtreeSize, {_Count, _DelCount, DocAndAttsSize}) ->
+ SeqBtreeSize + IdBtreeSize + DocAndAttsSize.
+
get_design_docs(Db) ->
{ok,_, Docs} = couch_btree:fold(by_id_btree(Db),
fun(#full_doc_info{id= <<"_design/",_/binary>>}=FullDocInfo, _Reds, AccDocs) ->
@@ -510,8 +525,14 @@ prep_and_validate_updates(Db, [DocBucket
[{ok, #full_doc_info{rev_tree=OldRevTree}=OldFullDocInfo}|RestLookups],
AllowConflict, AccPrepped, AccErrors) ->
Leafs = couch_key_tree:get_all_leafs(OldRevTree),
- LeafRevsDict = dict:from_list([{{Start, RevId}, {Deleted, Sp, Revs}} ||
- {{Deleted, Sp, _Seq}, {Start, [RevId|_]}=Revs} <- Leafs]),
+ LeafRevsDict = dict:from_list([
+ begin
+ Deleted = element(1, LeafVal),
+ Sp = element(2, LeafVal),
+ {{Start, RevId}, {Deleted, Sp, Revs}}
+ end ||
+ {LeafVal, {Start, [RevId | _]} = Revs} <- Leafs
+ ]),
{PreppedBucket, AccErrors3} = lists:foldl(
fun(Doc, {Docs2Acc, AccErrors2}) ->
case prep_and_validate_update(Db, Doc, OldFullDocInfo,
@@ -738,7 +759,9 @@ make_first_doc_on_disk(_Db, _Id, _Pos, [
nil;
make_first_doc_on_disk(Db, Id, Pos, [{_Rev, ?REV_MISSING}|RestPath]) ->
make_first_doc_on_disk(Db, Id, Pos - 1, RestPath);
-make_first_doc_on_disk(Db, Id, Pos, [{_Rev, {IsDel, Sp, _Seq}} |_]=DocPath) ->
+make_first_doc_on_disk(Db, Id, Pos, [{_Rev, RevValue} |_]=DocPath) ->
+ IsDel = element(1, RevValue),
+ Sp = element(2, RevValue),
Revs = [Rev || {Rev, _} <- DocPath],
make_doc(Db, Id, IsDel, Sp, {Pos, Revs}).
@@ -954,9 +977,9 @@ enum_docs_since_reduce_to_count(Reds) ->
fun couch_db_updater:btree_by_seq_reduce/2, Reds).
enum_docs_reduce_to_count(Reds) ->
- {Count, _DelCount} = couch_btree:final_reduce(
+ FinalRed = couch_btree:final_reduce(
fun couch_db_updater:btree_by_id_reduce/2, Reds),
- Count.
+ element(1, FinalRed).
changes_since(Db, Style, StartSeq, Fun, Acc) ->
changes_since(Db, Style, StartSeq, Fun, [], Acc).
@@ -1081,7 +1104,9 @@ open_doc_revs_int(Db, IdRevs, Options) -
?REV_MISSING ->
% we have the rev in our list but know nothing about it
{{not_found, missing}, {Pos, Rev}};
- {IsDeleted, SummaryPtr, _UpdateSeq} ->
+ RevValue ->
+ IsDeleted = element(1, RevValue),
+ SummaryPtr = element(2, RevValue),
{ok, make_doc(Db, Id, IsDeleted, SummaryPtr, FoundRevPath)}
end
end, FoundRevs),
@@ -1128,12 +1153,15 @@ doc_meta_info(#doc_info{high_seq=Seq,rev
couch_key_tree:get_full_key_paths(RevTree, [Rev]),
[{revs_info, Pos, lists:map(
- fun({Rev1, {true, _Sp, _UpdateSeq}}) ->
- {Rev1, deleted};
- ({Rev1, {false, _Sp, _UpdateSeq}}) ->
- {Rev1, available};
- ({Rev1, ?REV_MISSING}) ->
- {Rev1, missing}
+ fun({Rev1, ?REV_MISSING}) ->
+ {Rev1, missing};
+ ({Rev1, RevValue}) ->
+ case element(1, RevValue) of
+ true ->
+ {Rev1, deleted};
+ false ->
+ {Rev1, available}
+ end
end, RevPath)}]
end ++
case lists:member(conflicts, Options) of
Modified: couchdb/trunk/src/couchdb/couch_db.hrl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_db.hrl?rev=1095477&r1=1095476&r2=1095477&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_db.hrl (original)
+++ couchdb/trunk/src/couchdb/couch_db.hrl Wed Apr 20 18:29:33 2011
@@ -56,7 +56,8 @@
{id = <<"">>,
update_seq = 0,
deleted = false,
- rev_tree = []
+ rev_tree = [],
+ leafs_size = 0
}).
-record(httpd,
@@ -128,7 +129,7 @@
% if the disk revision is incremented, then new upgrade logic will need to be
% added to couch_db_updater:init_db.
--define(LATEST_DISK_VERSION, 5).
+-define(LATEST_DISK_VERSION, 6).
-record(db_header,
{disk_version = ?LATEST_DISK_VERSION,
Modified: couchdb/trunk/src/couchdb/couch_db_updater.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_db_updater.erl?rev=1095477&r1=1095476&r2=1095477&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_db_updater.erl (original)
+++ couchdb/trunk/src/couchdb/couch_db_updater.erl Wed Apr 20 18:29:33 2011
@@ -67,7 +67,7 @@ handle_call(increment_update_seq, _From,
{reply, {ok, Db2#db.update_seq}, Db2};
handle_call({set_security, NewSec}, _From, Db) ->
- {ok, Ptr} = couch_file:append_term(Db#db.updater_fd, NewSec),
+ {ok, Ptr, _} = couch_file:append_term(Db#db.updater_fd, NewSec),
Db2 = commit_data(Db#db{security=NewSec, security_ptr=Ptr,
update_seq=Db#db.update_seq+1}),
ok = gen_server:call(Db2#db.main_pid, {db_updated, Db2}),
@@ -119,7 +119,9 @@ handle_call({purge_docs, IdRevs}, _From,
{DocInfoToUpdate, NewSeq} = lists:mapfoldl(
fun(#full_doc_info{rev_tree=Tree}=FullInfo, SeqAcc) ->
Tree2 = couch_key_tree:map_leafs(
- fun(_RevId, {IsDeleted, BodyPointer, _UpdateSeq}) ->
+ fun(_RevId, LeafVal) ->
+ IsDeleted = element(1, LeafVal),
+ BodyPointer = element(2, LeafVal),
{IsDeleted, BodyPointer, SeqAcc + 1}
end, Tree),
{couch_doc:to_doc_info(FullInfo#full_doc_info{rev_tree=Tree2}),
@@ -133,7 +135,7 @@ handle_call({purge_docs, IdRevs}, _From,
DocInfoToUpdate, SeqsToRemove),
{ok, DocInfoByIdBTree2} = couch_btree:add_remove(DocInfoByIdBTree,
FullDocInfoToUpdate, IdsToRemove),
- {ok, Pointer} = couch_file:append_term(Fd, IdRevsPurged),
+ {ok, Pointer, _} = couch_file:append_term(Fd, IdRevsPurged),
Db2 = commit_data(
Db#db{
@@ -307,38 +309,74 @@ btree_by_id_split(#full_doc_info{id=Id,
deleted=Deleted, rev_tree=Tree}) ->
DiskTree =
couch_key_tree:map(
- fun(_RevId, {IsDeleted, BodyPointer, UpdateSeq}) ->
- {if IsDeleted -> 1; true -> 0 end, BodyPointer, UpdateSeq};
- (_RevId, ?REV_MISSING) ->
- ?REV_MISSING
+ fun(_RevId, ?REV_MISSING) ->
+ ?REV_MISSING;
+ (_RevId, RevValue) ->
+ IsDeleted = element(1, RevValue),
+ BodyPointer = element(2, RevValue),
+ UpdateSeq = element(3, RevValue),
+ Size = case tuple_size(RevValue) of
+ 4 ->
+ element(4, RevValue);
+ 3 ->
+ % pre 1.2 format, will be upgraded on compaction
+ nil
+ end,
+ {if IsDeleted -> 1; true -> 0 end, BodyPointer, UpdateSeq, Size}
end, Tree),
{Id, {Seq, if Deleted -> 1; true -> 0 end, DiskTree}}.
btree_by_id_join(Id, {HighSeq, Deleted, DiskTree}) ->
- Tree =
- couch_key_tree:map(
- fun(_RevId, {IsDeleted, BodyPointer, UpdateSeq}) ->
- {IsDeleted == 1, BodyPointer, UpdateSeq};
- (_RevId, ?REV_MISSING) ->
- ?REV_MISSING
- end, DiskTree),
-
- #full_doc_info{id=Id, update_seq=HighSeq, deleted=Deleted==1, rev_tree=Tree}.
+ {Tree, LeafsSize} =
+ couch_key_tree:mapfold(
+ fun(_RevId, {IsDeleted, BodyPointer, UpdateSeq}, _Type, _Acc) ->
+ % pre 1.2 format, will be upgraded on compaction
+ {{IsDeleted == 1, BodyPointer, UpdateSeq, nil}, nil};
+ (_RevId, {IsDeleted, BodyPointer, UpdateSeq, Size}, leaf, Acc) ->
+ Acc2 = sum_leaf_sizes(Acc, Size),
+ {{IsDeleted == 1, BodyPointer, UpdateSeq, Size}, Acc2};
+ (_RevId, {IsDeleted, BodyPointer, UpdateSeq, Size}, branch, Acc) ->
+ {{IsDeleted == 1, BodyPointer, UpdateSeq, Size}, Acc};
+ (_RevId, ?REV_MISSING, _Type, Acc) ->
+ {?REV_MISSING, Acc}
+ end, 0, DiskTree),
+ #full_doc_info{
+ id = Id,
+ update_seq = HighSeq,
+ deleted = (Deleted == 1),
+ rev_tree = Tree,
+ leafs_size = LeafsSize
+ }.
btree_by_id_reduce(reduce, FullDocInfos) ->
lists:foldl(
- fun(#full_doc_info{deleted = false}, {NotDeleted, Deleted}) ->
- {NotDeleted + 1, Deleted};
- (#full_doc_info{deleted = true}, {NotDeleted, Deleted}) ->
- {NotDeleted, Deleted + 1}
+ fun(Info, {NotDeleted, Deleted, Size}) ->
+ Size2 = sum_leaf_sizes(Size, Info#full_doc_info.leafs_size),
+ case Info#full_doc_info.deleted of
+ true ->
+ {NotDeleted, Deleted + 1, Size2};
+ false ->
+ {NotDeleted + 1, Deleted, Size2}
+ end
end,
- {0, 0}, FullDocInfos);
-btree_by_id_reduce(rereduce, [FirstRed | RestReds]) ->
+ {0, 0, 0}, FullDocInfos);
+btree_by_id_reduce(rereduce, Reds) ->
lists:foldl(
- fun({NotDeleted, Deleted}, {AccNotDeleted, AccDeleted}) ->
- {AccNotDeleted + NotDeleted, AccDeleted + Deleted}
+ fun({NotDeleted, Deleted}, {AccNotDeleted, AccDeleted, _AccSize}) ->
+ % pre 1.2 format, will be upgraded on compaction
+ {AccNotDeleted + NotDeleted, AccDeleted + Deleted, nil};
+ ({NotDeleted, Deleted, Size}, {AccNotDeleted, AccDeleted, AccSize}) ->
+ AccSize2 = sum_leaf_sizes(AccSize, Size),
+ {AccNotDeleted + NotDeleted, AccDeleted + Deleted, AccSize2}
end,
- FirstRed, RestReds).
+ {0, 0, 0}, Reds).
+
+sum_leaf_sizes(nil, _) ->
+ nil;
+sum_leaf_sizes(_, nil) ->
+ nil;
+sum_leaf_sizes(Size1, Size2) ->
+ Size1 + Size2.
btree_by_seq_reduce(reduce, DocInfos) ->
% count the number of documents
@@ -365,6 +403,7 @@ init_db(DbName, Filepath, Fd, ReaderFd,
2 -> throw({database_disk_version_error, ?OLD_DISK_VERSION_ERROR});
3 -> throw({database_disk_version_error, ?OLD_DISK_VERSION_ERROR});
4 -> Header1#db_header{security_ptr = nil}; % 0.10 and pre 0.11
+ 5 -> Header1; % pre 1.2
?LATEST_DISK_VERSION -> Header1;
_ -> throw({database_disk_version_error, "Incorrect disk header version"})
end,
@@ -452,8 +491,8 @@ flush_trees(_Db, [], AccFlushedTrees) ->
flush_trees(#db{updater_fd = Fd} = Db,
[InfoUnflushed | RestUnflushed], AccFlushed) ->
#full_doc_info{update_seq=UpdateSeq, rev_tree=Unflushed} = InfoUnflushed,
- Flushed = couch_key_tree:map(
- fun(_Rev, Value) ->
+ {Flushed, LeafsSize} = couch_key_tree:mapfold(
+ fun(_Rev, Value, Type, Acc) ->
case Value of
#doc{atts=Atts,deleted=IsDeleted}=Doc ->
% this node value is actually an unwritten document summary,
@@ -476,14 +515,28 @@ flush_trees(#db{updater_fd = Fd} = Db,
" changed. Possibly retrying.", []),
throw(retry)
end,
- {ok, NewSummaryPointer} =
+ {ok, NewSummaryPointer, SummarySize} =
couch_file:append_term_md5(Fd, {Doc#doc.body, DiskAtts}),
- {IsDeleted, NewSummaryPointer, UpdateSeq};
- _ ->
- Value
+ TotalSize = lists:foldl(
+ fun(#att{att_len = L}, A) -> A + L end, SummarySize, Atts),
+ NewValue = {IsDeleted, NewSummaryPointer, UpdateSeq, TotalSize},
+ case Type of
+ leaf ->
+ {NewValue, Acc + TotalSize};
+ branch ->
+ {NewValue, Acc}
+ end;
+ {_, _, _, LeafSize} when Type =:= leaf ->
+ {Value, Acc + LeafSize};
+ _ ->
+ {Value, Acc}
end
- end, Unflushed),
- flush_trees(Db, RestUnflushed, [InfoUnflushed#full_doc_info{rev_tree=Flushed} | AccFlushed]).
+ end, 0, Unflushed),
+ InfoFlushed = InfoUnflushed#full_doc_info{
+ rev_tree = Flushed,
+ leafs_size = LeafsSize
+ },
+ flush_trees(Db, RestUnflushed, [InfoFlushed | AccFlushed]).
send_result(Client, Id, OriginalRevs, NewResult) ->
@@ -763,12 +816,20 @@ copy_docs(Db, #db{updater_fd = DestFd} =
NewFullDocInfos1 = lists:map(
fun({ok, #full_doc_info{rev_tree=RevTree}=Info}) ->
Info#full_doc_info{rev_tree=couch_key_tree:map(
- fun(_Rev, {IsDel, Sp, Seq}, leaf) ->
- DocBody = copy_doc_attachments(Db, Sp, DestFd),
- {ok, Pos} = couch_file:append_term_md5(DestFd, DocBody),
- {IsDel, Pos, Seq};
- (_, _, branch) ->
- ?REV_MISSING
+ fun(_, _, branch) ->
+ ?REV_MISSING;
+ (_Rev, LeafVal, leaf) ->
+ IsDel = element(1, LeafVal),
+ Sp = element(2, LeafVal),
+ Seq = element(3, LeafVal),
+ {_Body, AttsInfo} = Summary = copy_doc_attachments(
+ Db, Sp, DestFd),
+ {ok, Pos, SummarySize} =
+ couch_file:append_term_md5(DestFd, Summary),
+ TotalLeafSize = lists:foldl(
+ fun({_, _, _, AttLen, _, _, _, _}, S) -> S + AttLen end,
+ SummarySize, AttsInfo),
+ {IsDel, Pos, Seq, TotalLeafSize}
end, RevTree)}
end, LookupResults),
@@ -827,7 +888,7 @@ copy_compact(Db, NewDb0, Retry) ->
% copy misc header values
if NewDb3#db.security /= Db#db.security ->
- {ok, Ptr} = couch_file:append_term(NewDb3#db.updater_fd, Db#db.security),
+ {ok, Ptr, _} = couch_file:append_term(NewDb3#db.updater_fd, Db#db.security),
NewDb4 = NewDb3#db{security=Db#db.security, security_ptr=Ptr};
true ->
NewDb4 = NewDb3
@@ -858,7 +919,7 @@ start_copy_compact(#db{name=Name,filepat
NewDb = init_db(Name, CompactFile, Fd, ReaderFd, Header, Db#db.options),
NewDb2 = if PurgeSeq > 0 ->
{ok, PurgedIdsRevs} = couch_db:get_last_purged(Db),
- {ok, Pointer} = couch_file:append_term(Fd, PurgedIdsRevs),
+ {ok, Pointer, _} = couch_file:append_term(Fd, PurgedIdsRevs),
NewDb#db{header=Header#db_header{purge_seq=PurgeSeq, purged_docs=Pointer}};
true ->
NewDb
Modified: couchdb/trunk/src/couchdb/couch_doc.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_doc.erl?rev=1095477&r1=1095476&r2=1095477&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_doc.erl (original)
+++ couchdb/trunk/src/couchdb/couch_doc.erl Wed Apr 20 18:29:33 2011
@@ -308,10 +308,15 @@ max_seq([#rev_info{seq=Seq}|Rest], Max)
max_seq(Rest, if Max > Seq -> Max; true -> Seq end).
to_doc_info_path(#full_doc_info{id=Id,rev_tree=Tree}) ->
- RevInfosAndPath =
- [{#rev_info{deleted=Del,body_sp=Bp,seq=Seq,rev={Pos,RevId}}, Path} ||
- {{Del, Bp, Seq},{Pos, [RevId|_]}=Path} <-
- couch_key_tree:get_all_leafs(Tree)],
+ RevInfosAndPath = [
+ {#rev_info{
+ deleted = element(1, LeafVal),
+ body_sp = element(2, LeafVal),
+ seq = element(3, LeafVal),
+ rev = {Pos, RevId}
+ }, Path} || {LeafVal, {Pos, [RevId | _]} = Path} <-
+ couch_key_tree:get_all_leafs(Tree)
+ ],
SortedRevInfosAndPath = lists:sort(
fun({#rev_info{deleted=DeletedA,rev=RevA}, _PathA},
{#rev_info{deleted=DeletedB,rev=RevB}, _PathB}) ->
Modified: couchdb/trunk/src/couchdb/couch_file.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_file.erl?rev=1095477&r1=1095476&r2=1095477&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_file.erl (original)
+++ couchdb/trunk/src/couchdb/couch_file.erl Wed Apr 20 18:29:33 2011
@@ -70,8 +70,9 @@ open(Filepath, Options) ->
%%----------------------------------------------------------------------
%% Purpose: To append an Erlang term to the end of the file.
%% Args: Erlang term to serialize and append to the file.
-%% Returns: {ok, Pos} where Pos is the file offset to the beginning the
-%% serialized term. Use pread_term to read the term back.
+%% Returns: {ok, Pos, NumBytesWritten} where Pos is the file offset to
+%% the beginning the serialized term. Use pread_term to read the term
+%% back.
%% or {error, Reason}.
%%----------------------------------------------------------------------
@@ -85,8 +86,8 @@ append_term_md5(Fd, Term) ->
%%----------------------------------------------------------------------
%% Purpose: To append an Erlang binary to the end of the file.
%% Args: Erlang term to serialize and append to the file.
-%% Returns: {ok, Pos} where Pos is the file offset to the beginning the
-%% serialized term. Use pread_term to read the term back.
+%% Returns: {ok, Pos, NumBytesWritten} where Pos is the file offset to the
+%% beginning the serialized term. Use pread_term to read the term back.
%% or {error, Reason}.
%%----------------------------------------------------------------------
@@ -337,9 +338,10 @@ handle_call({truncate, Pos}, _From, #fil
handle_call({append_bin, Bin}, _From, #file{fd = Fd, eof = Pos} = File) ->
Blocks = make_blocks(Pos rem ?SIZE_BLOCK, Bin),
+ Size = iolist_size(Blocks),
case file:write(Fd, Blocks) of
ok ->
- {reply, {ok, Pos}, File#file{eof = Pos + iolist_size(Blocks)}};
+ {reply, {ok, Pos, Size}, File#file{eof = Pos + Size}};
Error ->
{reply, Error, File}
end;
Modified: couchdb/trunk/src/couchdb/couch_key_tree.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_key_tree.erl?rev=1095477&r1=1095476&r2=1095477&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_key_tree.erl (original)
+++ couchdb/trunk/src/couchdb/couch_key_tree.erl Wed Apr 20 18:29:33 2011
@@ -48,8 +48,8 @@
-module(couch_key_tree).
-export([merge/3, 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,
- get_all_leafs_full/1,stem/2,map_leafs/2]).
+-export([get_all_leafs/1, count_leafs/1, remove_leafs/2, get_all_leafs_full/1, stem/2]).
+-export([map/2, mapfold/3, map_leafs/2]).
-include("couch_db.hrl").
@@ -334,6 +334,23 @@ map_simple(Fun, Pos, [{Key, Value, SubTr
[{Key, Value2, map_simple(Fun, Pos + 1, SubTree)} | map_simple(Fun, Pos, RestTree)].
+mapfold(_Fun, Acc, []) ->
+ {[], Acc};
+mapfold(Fun, Acc, [{Pos, Tree} | Rest]) ->
+ {[NewTree], Acc2} = mapfold_simple(Fun, Acc, Pos, [Tree]),
+ {Rest2, Acc3} = mapfold(Fun, Acc2, Rest),
+ {[{Pos, NewTree} | Rest2], Acc3}.
+
+mapfold_simple(_Fun, Acc, _Pos, []) ->
+ {[], Acc};
+mapfold_simple(Fun, Acc, Pos, [{Key, Value, SubTree} | RestTree]) ->
+ {Value2, Acc2} = Fun({Pos, Key}, Value,
+ if SubTree == [] -> leaf; true -> branch end, Acc),
+ {SubTree2, Acc3} = mapfold_simple(Fun, Acc2, Pos + 1, SubTree),
+ {RestTree2, Acc4} = mapfold_simple(Fun, Acc3, Pos, RestTree),
+ {[{Key, Value2, SubTree2} | RestTree2], Acc4}.
+
+
map_leafs(_Fun, []) ->
[];
map_leafs(Fun, [{Pos, Tree}|Rest]) ->
Modified: couchdb/trunk/src/couchdb/couch_stream.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_stream.erl?rev=1095477&r1=1095476&r2=1095477&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_stream.erl (original)
+++ couchdb/trunk/src/couchdb/couch_stream.erl Wed Apr 20 18:29:33 2011
@@ -237,7 +237,7 @@ handle_call({write, Bin}, _From, Stream)
Md5_2 = Md5,
Written2 = Written;
WriteBin2 ->
- {ok, Pos} = couch_file:append_binary(Fd, WriteBin2),
+ {ok, Pos, _} = couch_file:append_binary(Fd, WriteBin2),
WrittenLen2 = WrittenLen + iolist_size(WriteBin2),
Md5_2 = couch_util:md5_update(Md5, WriteBin2),
Written2 = [{Pos, iolist_size(WriteBin2)}|Written]
@@ -277,7 +277,7 @@ handle_call(close, _From, Stream) ->
[] ->
{lists:reverse(Written), WrittenLen, IdenLen, Md5Final, IdenMd5Final};
_ ->
- {ok, Pos} = couch_file:append_binary(Fd, WriteBin2),
+ {ok, Pos, _} = couch_file:append_binary(Fd, WriteBin2),
StreamInfo = lists:reverse(Written, [{Pos, iolist_size(WriteBin2)}]),
StreamLen = WrittenLen + iolist_size(WriteBin2),
{StreamInfo, StreamLen, IdenLen, Md5Final, IdenMd5Final}
Modified: couchdb/trunk/src/couchdb/couch_view_compactor.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_view_compactor.erl?rev=1095477&r1=1095476&r2=1095477&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_view_compactor.erl (original)
+++ couchdb/trunk/src/couchdb/couch_view_compactor.erl Wed Apr 20 18:29:33 2011
@@ -41,7 +41,8 @@ compact_group(Group, EmptyGroup) ->
views = EmptyViews
} = EmptyGroup,
- {ok, {Count, _}} = couch_btree:full_reduce(Db#db.fulldocinfo_by_id_btree),
+ {ok, DbReduce} = couch_btree:full_reduce(Db#db.fulldocinfo_by_id_btree),
+ Count = element(1, DbReduce),
<<"_design", ShortName/binary>> = GroupId,
DbName = couch_db:name(Db),
Modified: couchdb/trunk/src/couchdb/couch_view_group.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_view_group.erl?rev=1095477&r1=1095476&r2=1095477&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_view_group.erl (original)
+++ couchdb/trunk/src/couchdb/couch_view_group.erl Wed Apr 20 18:29:33 2011
@@ -533,15 +533,18 @@ get_group_info(State) ->
#group{
fd = Fd,
sig = GroupSig,
+ id_btree = Btree,
def_lang = Lang,
current_seq=CurrentSeq,
- purge_seq=PurgeSeq
+ purge_seq=PurgeSeq,
+ views = Views
} = Group,
{ok, Size} = couch_file:bytes(Fd),
[
{signature, ?l2b(hex_sig(GroupSig))},
{language, Lang},
{disk_size, Size},
+ {data_size, view_group_data_size(Btree, Views)},
{updater_running, UpdaterPid /= nil},
{compact_running, CompactorPid /= nil},
{waiting_commit, WaitingCommit},
@@ -550,6 +553,21 @@ get_group_info(State) ->
{purge_seq, PurgeSeq}
].
+view_group_data_size(MainBtree, Views) ->
+ lists:foldl(
+ fun(#view{btree = Btree}, Acc) ->
+ sum_btree_sizes(Acc, couch_btree:size(Btree))
+ end,
+ couch_btree:size(MainBtree),
+ Views).
+
+sum_btree_sizes(nil, _) ->
+ null;
+sum_btree_sizes(_, nil) ->
+ null;
+sum_btree_sizes(Size1, Size2) ->
+ Size1 + Size2.
+
% maybe move to another module
design_doc_to_view_group(#doc{id=Id,body={Fields}}) ->
Language = couch_util:get_value(<<"language">>, Fields, <<"javascript">>),
Modified: couchdb/trunk/test/etap/010-file-basics.t
URL: http://svn.apache.org/viewvc/couchdb/trunk/test/etap/010-file-basics.t?rev=1095477&r1=1095476&r2=1095477&view=diff
==============================================================================
--- couchdb/trunk/test/etap/010-file-basics.t (original)
+++ couchdb/trunk/test/etap/010-file-basics.t Wed Apr 20 18:29:33 2011
@@ -12,6 +12,11 @@
% License for the specific language governing permissions and limitations under
% the License.
+-define(etap_match(Got, Expected, Desc),
+ etap:fun_is(fun(XXXXXX) ->
+ case XXXXXX of Expected -> true; _ -> false end
+ end, Got, Desc)).
+
filename() -> test_util:build_file("test/etap/temp.010").
main(_) ->
@@ -43,14 +48,14 @@ test() ->
etap:is({ok, 0}, couch_file:bytes(Fd),
"Newly created files have 0 bytes."),
- etap:is({ok, 0}, couch_file:append_term(Fd, foo),
+ ?etap_match(couch_file:append_term(Fd, foo), {ok, 0, _},
"Appending a term returns the previous end of file position."),
{ok, Size} = couch_file:bytes(Fd),
etap:is_greater(Size, 0,
"Writing a term increased the file size."),
- etap:is({ok, Size}, couch_file:append_binary(Fd, <<"fancy!">>),
+ ?etap_match(couch_file:append_binary(Fd, <<"fancy!">>), {ok, Size, _},
"Appending a binary returns the current file size."),
etap:is({ok, foo}, couch_file:pread_term(Fd, 0),
@@ -64,12 +69,12 @@ test() ->
"Reading a binary at a term position returns the term as binary."
),
- {ok, BinPos} = couch_file:append_binary(Fd, <<131,100,0,3,102,111,111>>),
+ {ok, BinPos, _} = couch_file:append_binary(Fd, <<131,100,0,3,102,111,111>>),
etap:is({ok, foo}, couch_file:pread_term(Fd, BinPos),
"Reading a term from a written binary term representation succeeds."),
BigBin = list_to_binary(lists:duplicate(100000, 0)),
- {ok, BigBinPos} = couch_file:append_binary(Fd, BigBin),
+ {ok, BigBinPos, _} = couch_file:append_binary(Fd, BigBin),
etap:is({ok, BigBin}, couch_file:pread_binary(Fd, BigBinPos),
"Reading a large term from a written representation succeeds."),
@@ -77,13 +82,13 @@ test() ->
etap:is({ok, hello}, couch_file:read_header(Fd),
"Reading a header succeeds."),
- {ok, BigBinPos2} = couch_file:append_binary(Fd, BigBin),
+ {ok, BigBinPos2, _} = couch_file:append_binary(Fd, BigBin),
etap:is({ok, BigBin}, couch_file:pread_binary(Fd, BigBinPos2),
"Reading a large term from a written representation succeeds 2."),
% append_binary == append_iolist?
% Possible bug in pread_iolist or iolist() -> append_binary
- {ok, IOLPos} = couch_file:append_binary(Fd, ["foo", $m, <<"bam">>]),
+ {ok, IOLPos, _} = couch_file:append_binary(Fd, ["foo", $m, <<"bam">>]),
{ok, IoList} = couch_file:pread_iolist(Fd, IOLPos),
etap:is(<<"foombam">>, iolist_to_binary(IoList),
"Reading an results in a binary form of the written iolist()"),
Modified: couchdb/trunk/test/etap/011-file-headers.t
URL: http://svn.apache.org/viewvc/couchdb/trunk/test/etap/011-file-headers.t?rev=1095477&r1=1095476&r2=1095477&view=diff
==============================================================================
--- couchdb/trunk/test/etap/011-file-headers.t (original)
+++ couchdb/trunk/test/etap/011-file-headers.t Wed Apr 20 18:29:33 2011
@@ -140,6 +140,6 @@ write_random_data(Fd, 0) ->
write_random_data(Fd, N) ->
Choices = [foo, bar, <<"bizzingle">>, "bank", ["rough", stuff]],
Term = lists:nth(random:uniform(4) + 1, Choices),
- {ok, _} = couch_file:append_term(Fd, Term),
+ {ok, _, _} = couch_file:append_term(Fd, Term),
write_random_data(Fd, N-1).
Modified: couchdb/trunk/test/etap/020-btree-basics.t
URL: http://svn.apache.org/viewvc/couchdb/trunk/test/etap/020-btree-basics.t?rev=1095477&r1=1095476&r2=1095477&view=diff
==============================================================================
--- couchdb/trunk/test/etap/020-btree-basics.t (original)
+++ couchdb/trunk/test/etap/020-btree-basics.t Wed Apr 20 18:29:33 2011
@@ -21,7 +21,7 @@ rows() -> 250.
main(_) ->
test_util:init_code_path(),
- etap:plan(48),
+ etap:plan(75),
case (catch test()) of
ok ->
etap:end_tests();
@@ -56,6 +56,7 @@ test_kvs(KeyValues) ->
etap:ok(is_record(Btree, btree), "Created btree is really a btree record"),
etap:is(Btree#btree.fd, Fd, "Btree#btree.fd is set correctly."),
etap:is(Btree#btree.root, nil, "Btree#btree.root is set correctly."),
+ etap:is(0, couch_btree:size(Btree), "Empty btrees have a 0 size."),
Btree1 = couch_btree:set_options(Btree, [{reduce, ReduceFun}]),
etap:is(Btree1#btree.reduce, ReduceFun, "Reduce function was set"),
@@ -66,6 +67,11 @@ test_kvs(KeyValues) ->
etap:ok(test_btree(Btree2, KeyValues),
"Adding all keys at once returns a complete btree."),
+ etap:is((couch_btree:size(Btree2) > 0), true,
+ "Non empty btrees have a size > 0."),
+ etap:is((couch_btree:size(Btree2) =< couch_file:bytes(Fd)), true,
+ "Btree size is <= file size."),
+
etap:fun_is(
fun
({ok, {kp_node, _}}) -> true;
@@ -79,25 +85,54 @@ test_kvs(KeyValues) ->
etap:ok(test_btree(Btree3, []),
"Removing all keys at once returns an empty btree."),
- Btree4 = lists:foldl(fun(KV, BtAcc) ->
+ etap:is(0, couch_btree:size(Btree3),
+ "After removing all keys btree size is 0."),
+
+ {Btree4, _} = lists:foldl(fun(KV, {BtAcc, PrevSize}) ->
{ok, BtAcc2} = couch_btree:add_remove(BtAcc, [KV], []),
- BtAcc2
- end, Btree3, KeyValues),
+ case couch_btree:size(BtAcc2) > PrevSize of
+ true ->
+ ok;
+ false ->
+ etap:is(false, true,
+ "After inserting a value, btree size did not increase.")
+ end,
+ {BtAcc2, couch_btree:size(BtAcc2)}
+ end, {Btree3, couch_btree:size(Btree3)}, KeyValues),
+
etap:ok(test_btree(Btree4, KeyValues),
"Adding all keys one at a time returns a complete btree."),
+ etap:is((couch_btree:size(Btree4) > 0), true,
+ "Non empty btrees have a size > 0."),
- Btree5 = lists:foldl(fun({K, _}, BtAcc) ->
+ {Btree5, _} = lists:foldl(fun({K, _}, {BtAcc, PrevSize}) ->
{ok, BtAcc2} = couch_btree:add_remove(BtAcc, [], [K]),
- BtAcc2
- end, Btree4, KeyValues),
+ case couch_btree:size(BtAcc2) < PrevSize of
+ true ->
+ ok;
+ false ->
+ etap:is(false, true,
+ "After removing a key, btree size did not decrease.")
+ end,
+ {BtAcc2, couch_btree:size(BtAcc2)}
+ end, {Btree4, couch_btree:size(Btree4)}, KeyValues),
etap:ok(test_btree(Btree5, []),
"Removing all keys one at a time returns an empty btree."),
+ etap:is(0, couch_btree:size(Btree5),
+ "After removing all keys, one by one, btree size is 0."),
KeyValuesRev = lists:reverse(KeyValues),
- Btree6 = lists:foldl(fun(KV, BtAcc) ->
+ {Btree6, _} = lists:foldl(fun(KV, {BtAcc, PrevSize}) ->
{ok, BtAcc2} = couch_btree:add_remove(BtAcc, [KV], []),
- BtAcc2
- end, Btree5, KeyValuesRev),
+ case couch_btree:size(BtAcc2) > PrevSize of
+ true ->
+ ok;
+ false ->
+ etap:is(false, true,
+ "After inserting a value, btree size did not increase.")
+ end,
+ {BtAcc2, couch_btree:size(BtAcc2)}
+ end, {Btree5, couch_btree:size(Btree5)}, KeyValuesRev),
etap:ok(test_btree(Btree6, KeyValues),
"Adding all keys in reverse order returns a complete btree."),
@@ -114,8 +149,14 @@ test_kvs(KeyValues) ->
etap:ok(test_add_remove(Btree6, Rem2Keys1, Rem2Keys0),
"Add/Remove opposite every other key."),
+ Size1 = couch_btree:size(Btree6),
{ok, Btree7} = couch_btree:add_remove(Btree6, [], [K||{K,_}<-Rem2Keys1]),
+ Size2 = couch_btree:size(Btree7),
+ etap:is((Size2 < Size1), true, "Btree size decreased"),
{ok, Btree8} = couch_btree:add_remove(Btree7, [], [K||{K,_}<-Rem2Keys0]),
+ Size3 = couch_btree:size(Btree8),
+ etap:is((Size3 < Size2), true, "Btree size decreased"),
+ etap:is(Size3, 0, "Empty btree has size 0."),
etap:ok(test_btree(Btree8, []),
"Removing both halves of every other key returns an empty btree."),