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."),