You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by rn...@apache.org on 2014/08/06 13:54:38 UTC

[01/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Repository: couchdb-couch
Updated Branches:
  refs/heads/windsor-merge-300 [created] 2c2f53ec3


Reset to default if max_dbs_open is deleted

max_dbs_opens is present in the stock default.ini so it appears we've
never deleted the value so far, just changed it up or down (mostly up
;))

When this property is deleted, reset to the default value.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/1caf4b68
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/1caf4b68
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/1caf4b68

Branch: refs/heads/windsor-merge-300
Commit: 1caf4b68bd3457a8593b294cf7dd24111a916c43
Parents: c68dc65
Author: Robert Newson <ro...@cloudant.com>
Authored: Mon Apr 15 21:46:22 2013 +0100
Committer: Robert Newson <rn...@apache.org>
Committed: Mon Aug 4 14:16:54 2014 +0100

----------------------------------------------------------------------
 src/couch_server.erl | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/1caf4b68/src/couch_server.erl
----------------------------------------------------------------------
diff --git a/src/couch_server.erl b/src/couch_server.erl
index ef7a6b6..e54efcc 100644
--- a/src/couch_server.erl
+++ b/src/couch_server.erl
@@ -26,10 +26,12 @@
 
 -include_lib("couch/include/couch_db.hrl").
 
+-define(MAX_DBS_OPEN, 100).
+
 -record(server,{
     root_dir = [],
     dbname_regexp,
-    max_dbs_open=100,
+    max_dbs_open=?MAX_DBS_OPEN,
     dbs_open=0,
     start_time="",
     lru = couch_lru:new()
@@ -187,7 +189,7 @@ init([]) ->
 
     RootDir = config:get("couchdb", "database_dir", "."),
     MaxDbsOpen = list_to_integer(
-            config:get("couchdb", "max_dbs_open")),
+            config:get("couchdb", "max_dbs_open", integer_to_list(?MAX_DBS_OPEN))),
     ok = config:listen_for_changes(?MODULE, nil),
     ok = couch_file:init_delete_dir(RootDir),
     hash_admin_passwords(),
@@ -210,8 +212,10 @@ terminate(_Reason, _Srv) ->
 handle_config_change("couchdb", "database_dir", _, _, _) ->
     exit(whereis(couch_server), config_change),
     remove_handler;
-handle_config_change("couchdb", "max_dbs_open", Max, _, _) ->
+handle_config_change("couchdb", "max_dbs_open", Max, _, _) when is_list(Max) ->
     {ok, gen_server:call(couch_server,{set_max_dbs_open,list_to_integer(Max)})};
+handle_config_change("couchdb", "max_dbs_open", _, _, _) ->
+    {ok, gen_server:call(couch_server,{set_max_dbs_open,?MAX_DBS_OPEN})};
 handle_config_change("admins", _, _, Persist, _) ->
     % spawn here so couch event manager doesn't deadlock
     {ok, spawn(fun() -> hash_admin_passwords(Persist) end)};


[34/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
fix code_change in couch_proc_manager


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/f2368da9
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/f2368da9
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/f2368da9

Branch: refs/heads/windsor-merge-300
Commit: f2368da9421531a70b1114a2497ee06c97be2d0f
Parents: 8e09de2
Author: Robert Newson <ro...@cloudant.com>
Authored: Thu Sep 5 11:45:22 2013 +0100
Committer: Robert Newson <rn...@apache.org>
Committed: Tue Aug 5 17:42:57 2014 +0100

----------------------------------------------------------------------
 src/couch_proc_manager.erl | 2 ++
 1 file changed, 2 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/f2368da9/src/couch_proc_manager.erl
----------------------------------------------------------------------
diff --git a/src/couch_proc_manager.erl b/src/couch_proc_manager.erl
index 6a64fa7..2744c88 100644
--- a/src/couch_proc_manager.erl
+++ b/src/couch_proc_manager.erl
@@ -231,6 +231,8 @@ terminate(_Reason, #state{tab=Tab}) ->
     ets:foldl(fun(#proc_int{pid=P}, _) -> couch_util:shutdown_sync(P) end, 0, Tab),
     ok.
 
+code_change(_OldVsn, #state{}=State, _Extra) ->
+    {ok, State};
 code_change(_OldVsn, {state, Tab}, _Extra) ->
     State = #state{tab = Tab, threshold_ts = {0,0,0}},
     ProcInts = lists:map(fun import_proc/1, ets:tab2list(Tab)),


[36/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Handle duplicate doc updates in single commit

Before this commit, there were bugs in couch_db and couch_db_updater
which caused couch to incorrectly report that all duplicate docs in a
group commit failed from conflicts. The changes in this commit cause
couch to correctly report that one of the duplicate updates in the
group commit succeeded. This commit also changes behavior slightly by
applying duplicate doc updates in the order they were supplied. This
change is for consistency with CouchDB.

This work is based on the patch by Bob Dionne.

BugzID: 12540


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/46607e03
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/46607e03
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/46607e03

Branch: refs/heads/windsor-merge-300
Commit: 46607e03d2a06a95ff38d6256e037450e886a837
Parents: fcce4b7
Author: Benjamin Bastian <be...@gmail.com>
Authored: Tue Aug 27 11:26:58 2013 -0700
Committer: Robert Newson <rn...@apache.org>
Committed: Wed Aug 6 10:34:17 2014 +0100

----------------------------------------------------------------------
 src/couch_db.erl         | 95 ++++++++++++++++++++++++++-----------------
 src/couch_db_updater.erl | 52 +++++++++++++++++------
 src/couch_doc.erl        | 12 +++---
 3 files changed, 103 insertions(+), 56 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/46607e03/src/couch_db.erl
----------------------------------------------------------------------
diff --git a/src/couch_db.erl b/src/couch_db.erl
index 0b9e490..b0c7894 100644
--- a/src/couch_db.erl
+++ b/src/couch_db.erl
@@ -500,11 +500,17 @@ update_docs(Db, Docs) ->
 % group_alike_docs groups the sorted documents into sublist buckets, by id.
 % ([DocA, DocA, DocB, DocC], []) -> [[DocA, DocA], [DocB], [DocC]]
 group_alike_docs(Docs) ->
-    Sorted = lists:sort(fun(#doc{id=A},#doc{id=B})-> A < B end, Docs),
-    group_alike_docs(Sorted, []).
+    % Here we're just asserting that our doc sort is stable so that
+    % if we have duplicate docids we don't have to worry about the
+    % behavior of lists:sort/2 which isn't documented anyhwere as
+    % being stable.
+    WithPos = lists:zip(Docs, lists:seq(1, length(Docs))),
+    SortFun = fun({D1, P1}, {D2, P2}) -> {D1#doc.id, P1} =< {D2#doc.id, P2} end,
+    SortedDocs = [D || {D, _} <- lists:sort(SortFun, WithPos)],
+    group_alike_docs(SortedDocs, []).
 
 group_alike_docs([], Buckets) ->
-    lists:reverse(Buckets);
+    lists:reverse(lists:map(fun lists:reverse/1, Buckets));
 group_alike_docs([Doc|Rest], []) ->
     group_alike_docs(Rest, [[Doc]]);
 group_alike_docs([Doc|Rest], [Bucket|RestBuckets]) ->
@@ -627,10 +633,10 @@ prep_and_validate_update(Db, #doc{id=Id,revs={RevStart, Revs}}=Doc,
 
 prep_and_validate_updates(_Db, [], [], _AllowConflict, AccPrepped,
         AccFatalErrors) ->
-   {AccPrepped, AccFatalErrors};
+    AccPrepped2 = lists:reverse(lists:map(fun lists:reverse/1, AccPrepped)),
+    {AccPrepped2, AccFatalErrors};
 prep_and_validate_updates(Db, [DocBucket|RestBuckets], [not_found|RestLookups],
         AllowConflict, AccPrepped, AccErrors) ->
-    [#doc{id=Id}|_]=DocBucket,
     % no existing revs are known,
     {PreppedBucket, AccErrors3} = lists:foldl(
         fun(#doc{revs=Revs}=Doc, {AccBucket, AccErrors2}) ->
@@ -645,11 +651,11 @@ prep_and_validate_updates(Db, [DocBucket|RestBuckets], [not_found|RestLookups],
                 ok ->
                     {[Doc | AccBucket], AccErrors2};
                 Error ->
-                    {AccBucket, [{{Id, {0, []}}, Error} | AccErrors2]}
+                    {AccBucket, [{doc_tag(Doc), Error} | AccErrors2]}
                 end;
             _ ->
                 % old revs specified but none exist, a conflict
-                {AccBucket, [{{Id, Revs}, conflict} | AccErrors2]}
+                {AccBucket, [{doc_tag(Doc), conflict} | AccErrors2]}
             end
         end,
         {[], AccErrors}, DocBucket),
@@ -670,9 +676,9 @@ prep_and_validate_updates(Db, [DocBucket|RestBuckets],
                     LeafRevsDict, AllowConflict) of
             {ok, Doc2} ->
                 {[Doc2 | Docs2Acc], AccErrors2};
-            {Error, #doc{id=Id,revs=Revs}} ->
+            {Error, _} ->
                 % Record the error
-                {Docs2Acc, [{{Id, Revs}, Error} |AccErrors2]}
+                {Docs2Acc, [{doc_tag(Doc), Error} |AccErrors2]}
             end
         end,
         {[], AccErrors}, DocBucket),
@@ -687,7 +693,8 @@ update_docs(Db, Docs, Options) ->
 prep_and_validate_replicated_updates(_Db, [], [], AccPrepped, AccErrors) ->
     Errors2 = [{{Id, {Pos, Rev}}, Error} ||
             {#doc{id=Id,revs={Pos,[Rev|_]}}, Error} <- AccErrors],
-    {lists:reverse(AccPrepped), lists:reverse(Errors2)};
+    AccPrepped2 = lists:reverse(lists:map(fun lists:reverse/1, AccPrepped)),
+    {AccPrepped2, lists:reverse(Errors2)};
 prep_and_validate_replicated_updates(Db, [Bucket|RestBuckets], [OldInfo|RestOldInfo], AccPrepped, AccErrors) ->
     case OldInfo of
     not_found ->
@@ -782,10 +789,10 @@ new_revs([], OutBuckets, IdRevsAcc) ->
     {lists:reverse(OutBuckets), IdRevsAcc};
 new_revs([Bucket|RestBuckets], OutBuckets, IdRevsAcc) ->
     {NewBucket, IdRevsAcc3} = lists:mapfoldl(
-        fun(#doc{id=Id,revs={Start, RevIds}}=Doc, IdRevsAcc2)->
+        fun(#doc{revs={Start, RevIds}}=Doc, IdRevsAcc2)->
         NewRevId = new_revid(Doc),
         {Doc#doc{revs={Start+1, [NewRevId | RevIds]}},
-            [{{Id, {Start, RevIds}}, {ok, {Start+1, NewRevId}}} | IdRevsAcc2]}
+            [{doc_tag(Doc), {ok, {Start+1, NewRevId}}} | IdRevsAcc2]}
     end, IdRevsAcc, Bucket),
     new_revs(RestBuckets, [NewBucket|OutBuckets], IdRevsAcc3).
 
@@ -802,9 +809,23 @@ check_dup_atts2(_) ->
     ok.
 
 
-update_docs(Db, Docs, Options, replicated_changes) ->
+tag_docs([]) ->
+    [];
+tag_docs([#doc{meta=Meta}=Doc | Rest]) ->
+    [Doc#doc{meta=[{ref, make_ref()} | Meta]} | tag_docs(Rest)].
+
+doc_tag(#doc{meta=Meta}) ->
+    case lists:keyfind(ref, 1, Meta) of
+        {ref, Ref} when is_reference(Ref) -> Ref;
+        false -> throw(doc_not_tagged);
+        Else -> throw({invalid_doc_tag, Else})
+    end.
+
+update_docs(Db, Docs0, Options, replicated_changes) ->
     increment_stat(Db, {couchdb, database_writes}),
+    Docs = tag_docs(Docs0),
     DocBuckets = before_docs_update(Db, group_alike_docs(Docs)),
+
     case (Db#db.validate_doc_funs /= []) orelse
         lists:any(
             fun(#doc{id= <<?DESIGN_DOC_PREFIX, _/binary>>}) -> true;
@@ -827,21 +848,17 @@ update_docs(Db, Docs, Options, replicated_changes) ->
     {ok, []} = write_and_commit(Db, DocBuckets4, [], [merge_conflicts | Options]),
     {ok, DocErrors};
 
-update_docs(Db, Docs, Options, interactive_edit) ->
+update_docs(Db, Docs0, Options, interactive_edit) ->
     increment_stat(Db, {couchdb, database_writes}),
     AllOrNothing = lists:member(all_or_nothing, Options),
-    % go ahead and generate the new revision ids for the documents.
-    % separate out the NonRep documents from the rest of the documents
-
-    {Docs2, NonRepDocs} = lists:foldl(
-         fun(#doc{id=Id}=Doc, {DocsAcc, NonRepDocsAcc}) ->
-            case Id of
-            <<?LOCAL_DOC_PREFIX, _/binary>> ->
-                {DocsAcc, [Doc | NonRepDocsAcc]};
-            Id->
-                {[Doc | DocsAcc], NonRepDocsAcc}
-            end
-        end, {[], []}, Docs),
+    Docs = tag_docs(Docs0),
+
+    % Separate _local docs from normal docs
+    IsLocal = fun
+        (#doc{id= <<?LOCAL_DOC_PREFIX, _/binary>>}) -> true;
+        (_) -> false
+    end,
+    {NonRepDocs, Docs2} = lists:partition(IsLocal, Docs),
 
     DocBuckets = before_docs_update(Db, group_alike_docs(Docs2)),
 
@@ -868,12 +885,14 @@ update_docs(Db, Docs, Options, interactive_edit) ->
     end,
 
     if (AllOrNothing) and (PreCommitFailures /= []) ->
-        {aborted, lists:map(
-            fun({{Id,{Pos, [RevId|_]}}, Error}) ->
-                {{Id, {Pos, RevId}}, Error};
-            ({{Id,{0, []}}, Error}) ->
-                {{Id, {0, <<>>}}, Error}
-            end, PreCommitFailures)};
+        RefErrorDict = dict:from_list([{doc_tag(Doc), Doc} || Doc <- Docs]),
+        {aborted, lists:map(fun({Ref, Error}) ->
+            #doc{id=Id,revs={Start,RevIds}} = dict:fetch(Ref, RefErrorDict),
+            case {Start, RevIds} of
+                {Pos, [RevId | _]} -> {{Id, {Pos, RevId}}, Error};
+                {0, []} -> {{Id, {0, <<>>}}, Error}
+            end
+        end, PreCommitFailures)};
     true ->
         Options2 = if AllOrNothing -> [merge_conflicts];
                 true -> [] end ++ Options,
@@ -885,12 +904,12 @@ update_docs(Db, Docs, Options, interactive_edit) ->
 
         {ok, CommitResults} = write_and_commit(Db, DocBuckets4, NonRepDocs, Options2),
 
-        ResultsDict = dict:from_list(IdRevs ++ CommitResults ++ PreCommitFailures),
-        {ok, lists:map(
-            fun(#doc{id=Id,revs={Pos, RevIds}}) ->
-                {ok, Result} = dict:find({Id, {Pos, RevIds}}, ResultsDict),
-                Result
-            end, Docs)}
+        ResultsDict = lists:foldl(fun({Key, Resp}, ResultsAcc) ->
+            dict:store(Key, Resp, ResultsAcc)
+        end, dict:from_list(IdRevs), CommitResults ++ PreCommitFailures),
+        {ok, lists:map(fun(Doc) ->
+            dict:fetch(doc_tag(Doc), ResultsDict)
+        end, Docs)}
     end.
 
 % Returns the first available document on disk. Input list is a full rev path

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/46607e03/src/couch_db_updater.erl
----------------------------------------------------------------------
diff --git a/src/couch_db_updater.erl b/src/couch_db_updater.erl
index 09028bd..7c2a43a 100644
--- a/src/couch_db_updater.erl
+++ b/src/couch_db_updater.erl
@@ -264,7 +264,7 @@ handle_cast(Msg, #db{name = Name} = Db) ->
 
 handle_info({update_docs, Client, GroupedDocs, NonRepDocs, MergeConflicts,
         FullCommit}, Db) ->
-    GroupedDocs2 = [[{Client, D} || D <- DocGroup] || DocGroup <- GroupedDocs],
+    GroupedDocs2 = maybe_tag_grouped_docs(Client, GroupedDocs),
     if NonRepDocs == [] ->
         {GroupedDocs3, Clients, FullCommit2} = collect_updates(GroupedDocs2,
                 [Client], MergeConflicts, FullCommit);
@@ -319,6 +319,20 @@ handle_info({'DOWN', Ref, _, _, Reason}, #db{fd_monitor=Ref, name=Name} = Db) ->
 code_change(_OldVsn, State, _Extra) ->
     {ok, State}.
 
+maybe_tag_grouped_docs(Client, GroupedDocs) ->
+    lists:map(fun(DocGroup) ->
+        [{Client, maybe_tag_doc(D)} || D <- DocGroup]
+    end, GroupedDocs).
+
+maybe_tag_doc(#doc{id=Id, revs={Pos,[_Rev|PrevRevs]}, meta=Meta0}=Doc) ->
+    case lists:keymember(ref, 1, Meta0) of
+        true ->
+            Doc;
+        false ->
+            Key = {Id, {Pos-1, PrevRevs}},
+            Doc#doc{meta=[{ref, Key} | Meta0]}
+    end.
+
 merge_updates([[{_,#doc{id=X}}|_]=A|RestA], [[{_,#doc{id=X}}|_]=B|RestB]) ->
     [A++B | merge_updates(RestA, RestB)];
 merge_updates([[{_,#doc{id=X}}|_]|_]=A, [[{_,#doc{id=Y}}|_]|_]=B) when X < Y ->
@@ -337,8 +351,7 @@ collect_updates(GroupedDocsAcc, ClientsAcc, MergeConflicts, FullCommit) ->
         % updaters than deal with their possible conflicts, and local docs
         % writes are relatively rare. Can be optmized later if really needed.
         {update_docs, Client, GroupedDocs, [], MergeConflicts, FullCommit2} ->
-            GroupedDocs2 = [[{Client, Doc} || Doc <- DocGroup]
-                    || DocGroup <- GroupedDocs],
+            GroupedDocs2 = maybe_tag_grouped_docs(Client, GroupedDocs),
             GroupedDocsAcc2 =
                 merge_updates(GroupedDocsAcc, GroupedDocs2),
             collect_updates(GroupedDocsAcc2, [Client | ClientsAcc],
@@ -597,9 +610,16 @@ flush_trees(#db{fd = Fd} = Db,
     flush_trees(Db, RestUnflushed, [InfoFlushed | AccFlushed]).
 
 
-send_result(Client, Id, OriginalRevs, NewResult) ->
+send_result(Client, Doc, NewResult) ->
     % used to send a result to the client
-    catch(Client ! {result, self(), {{Id, OriginalRevs}, NewResult}}).
+    catch(Client ! {result, self(), {doc_tag(Doc), NewResult}}).
+
+doc_tag(#doc{meta=Meta}) ->
+    case lists:keyfind(ref, 1, Meta) of
+        {ref, Ref} -> Ref;
+        false -> throw(no_doc_tag);
+        Else -> throw({invalid_doc_tag, Else})
+    end.
 
 merge_rev_trees(_Limit, _Merge, [], [], AccNewInfos, AccRemoveSeqs, AccSeq) ->
     {ok, lists:reverse(AccNewInfos), AccRemoveSeqs, AccSeq};
@@ -613,7 +633,7 @@ merge_rev_trees(Limit, MergeConflicts, [NewDocs|RestDocsList],
                 case couch_key_tree:merge(AccTree, couch_doc:to_path(NewDoc),
                     Limit) of
                 {_NewTree, conflicts} when (not OldDeleted) ->
-                    send_result(Client, Id, {Pos-1,PrevRevs}, conflict),
+                    send_result(Client, NewDoc, conflict),
                     {AccTree, OldDeleted};
                 {NewTree, conflicts} when PrevRevs /= [] ->
                     % Check to be sure if prev revision was specified, it's
@@ -625,7 +645,7 @@ merge_rev_trees(Limit, MergeConflicts, [NewDocs|RestDocsList],
                     if IsPrevLeaf ->
                         {NewTree, OldDeleted};
                     true ->
-                        send_result(Client, Id, {Pos-1,PrevRevs}, conflict),
+                        send_result(Client, NewDoc, conflict),
                         {AccTree, OldDeleted}
                     end;
                 {NewTree, no_conflicts} when  AccTree == NewTree ->
@@ -644,11 +664,11 @@ merge_rev_trees(Limit, MergeConflicts, [NewDocs|RestDocsList],
                         {NewTree2, _} = couch_key_tree:merge(AccTree,
                                 couch_doc:to_path(NewDoc2), Limit),
                         % we changed the rev id, this tells the caller we did
-                        send_result(Client, Id, {Pos-1,PrevRevs},
-                            {ok, {OldPos + 1, NewRevId}}),
+                        send_result(Client, NewDoc,
+                                {ok, {OldPos + 1, NewRevId}}),
                         {NewTree2, OldDeleted};
                     true ->
-                        send_result(Client, Id, {Pos-1,PrevRevs}, conflict),
+                        send_result(Client, NewDoc, conflict),
                         {AccTree, OldDeleted}
                     end;
                 {NewTree, _} ->
@@ -754,7 +774,13 @@ update_local_docs(#db{local_tree=Btree}=Db, Docs) ->
     Ids = [Id || {_Client, #doc{id=Id}} <- Docs],
     OldDocLookups = couch_btree:lookup(Btree, Ids),
     BtreeEntries = lists:zipwith(
-        fun({Client, #doc{id=Id,deleted=Delete,revs={0,PrevRevs},body=Body}}, _OldDocLookup) ->
+        fun({Client, NewDoc}, _OldDocLookup) ->
+            #doc{
+                id = Id,
+                deleted = Delete,
+                revs = {0, PrevRevs},
+                body = Body
+            } = NewDoc,
             case PrevRevs of
             [RevStr|_] ->
                 PrevRev = list_to_integer(?b2l(RevStr));
@@ -771,11 +797,11 @@ update_local_docs(#db{local_tree=Btree}=Db, Docs) ->
             % true ->
                 case Delete of
                     false ->
-                        send_result(Client, Id, {0, PrevRevs}, {ok,
+                        send_result(Client, NewDoc, {ok,
                                 {0, ?l2b(integer_to_list(PrevRev + 1))}}),
                         {update, {Id, {PrevRev + 1, Body}}};
                     true  ->
-                        send_result(Client, Id, {0, PrevRevs},
+                        send_result(Client, NewDoc,
                                 {ok, {0, <<"0">>}}),
                         {remove, Id}
                 end%;

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/46607e03/src/couch_doc.erl
----------------------------------------------------------------------
diff --git a/src/couch_doc.erl b/src/couch_doc.erl
index 05202f4..14f6a4f 100644
--- a/src/couch_doc.erl
+++ b/src/couch_doc.erl
@@ -70,7 +70,7 @@ revs_to_strs([{Pos, RevId}| Rest]) ->
     [rev_to_str({Pos, RevId}) | revs_to_strs(Rest)].
 
 to_json_meta(Meta) ->
-    lists:map(
+    lists:flatmap(
         fun({revs_info, Start, RevsInfo}) ->
             {JsonRevsInfo, _Pos}  = lists:mapfoldl(
                 fun({RevId, Status}, PosAcc) ->
@@ -78,13 +78,15 @@ to_json_meta(Meta) ->
                         {<<"status">>, ?l2b(atom_to_list(Status))}]},
                     {JsonObj, PosAcc - 1}
                 end, Start, RevsInfo),
-            {<<"_revs_info">>, JsonRevsInfo};
+            [{<<"_revs_info">>, JsonRevsInfo}];
         ({local_seq, Seq}) ->
-            {<<"_local_seq">>, Seq};
+            [{<<"_local_seq">>, Seq}];
         ({conflicts, Conflicts}) ->
-            {<<"_conflicts">>, revs_to_strs(Conflicts)};
+            [{<<"_conflicts">>, revs_to_strs(Conflicts)}];
         ({deleted_conflicts, DConflicts}) ->
-            {<<"_deleted_conflicts">>, revs_to_strs(DConflicts)}
+            [{<<"_deleted_conflicts">>, revs_to_strs(DConflicts)}];
+        (_) ->
+            []
         end, Meta).
 
 to_json_attachments(Attachments, Options) ->


[26/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Remember to protect field access on old headers

couch_db_header:get_field/2 is not guranteed to be called only on valid
db_header records so return undefined for any field beyond the given
tuple.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/d34d7960
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/d34d7960
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/d34d7960

Branch: refs/heads/windsor-merge-300
Commit: d34d7960d2d0669192d4d8a35197dc0d12e274a9
Parents: f77e694
Author: Paul J. Davis <pa...@gmail.com>
Authored: Fri Jun 14 14:55:34 2013 -0500
Committer: Robert Newson <rn...@apache.org>
Committed: Tue Aug 5 16:19:01 2014 +0100

----------------------------------------------------------------------
 src/couch_db_header.erl | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/d34d7960/src/couch_db_header.erl
----------------------------------------------------------------------
diff --git a/src/couch_db_header.erl b/src/couch_db_header.erl
index ff771d1..1d743ef 100644
--- a/src/couch_db_header.erl
+++ b/src/couch_db_header.erl
@@ -155,7 +155,11 @@ epochs(Header) ->
 
 
 get_field(Header, Field) ->
-    element(index(Field), Header).
+    Idx = index(Field),
+    case Idx > tuple_size(Header) of
+        true -> undefined;
+        false -> element(index(Field), Header)
+    end.
 
 
 set_field(Header, Field, Value) ->
@@ -343,4 +347,14 @@ upgrade_epochs_test() ->
     ?assertEqual(OwnedEpochs, epochs(ResetHeader)).
 
 
+get_uuid_from_old_header_test() ->
+    Vsn5Header = mk_header(5),
+    ?assertEqual(undefined, uuid(Vsn5Header)).
+
+
+get_epochs_from_old_header_test() ->
+    Vsn5Header = mk_header(5),
+    ?assertEqual(undefined, epochs(Vsn5Header)).
+
+
 -endif.


[09/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Update couch_auth_cache to use new couch_event app


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/be7b1a48
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/be7b1a48
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/be7b1a48

Branch: refs/heads/windsor-merge-300
Commit: be7b1a48fd7a9ba135915be7a76a5996b702bec7
Parents: 2b45f92
Author: Paul J. Davis <pa...@gmail.com>
Authored: Tue Apr 23 15:53:49 2013 -0500
Committer: Robert Newson <rn...@apache.org>
Committed: Mon Aug 4 15:19:30 2014 +0100

----------------------------------------------------------------------
 src/couch_auth_cache.erl | 44 ++++++++++++++++++++++++++-----------------
 1 file changed, 27 insertions(+), 17 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/be7b1a48/src/couch_auth_cache.erl
----------------------------------------------------------------------
diff --git a/src/couch_auth_cache.erl b/src/couch_auth_cache.erl
index 4f6c308..72e9278 100644
--- a/src/couch_auth_cache.erl
+++ b/src/couch_auth_cache.erl
@@ -22,6 +22,7 @@
 -export([code_change/3, terminate/2]).
 
 -export([handle_config_change/5]).
+-export([handle_db_event/3]).
 
 -include_lib("couch/include/couch_db.hrl").
 -include("couch_js_functions.hrl").
@@ -34,7 +35,8 @@
     max_cache_size = 0,
     cache_size = 0,
     db_notifier = nil,
-    db_mon_ref = nil
+    db_mon_ref = nil,
+    event_listener = nil
 }).
 
 
@@ -131,11 +133,14 @@ init(_) ->
     ?STATE = ets:new(?STATE, [set, protected, named_table]),
     ?BY_USER = ets:new(?BY_USER, [set, protected, named_table]),
     ?BY_ATIME = ets:new(?BY_ATIME, [ordered_set, private, named_table]),
+    AuthDbName = config:get("couch_httpd_auth", "authentication_db"),
     process_flag(trap_exit, true),
     ok = config:listen_for_changes(?MODULE, nil),
-    {ok, Notifier} = couch_db_update_notifier:start_link(fun handle_db_event/1),
+    {ok, Listener} = couch_event:link_listener(
+            ?MODULE, handle_db_event, nil, [{dbname, AuthDbName}]
+        ),
     State = #state{
-        db_notifier = Notifier,
+        event_listener = Listener,
         max_cache_size = list_to_integer(
             config:get("couch_httpd_auth", "auth_cache_size", "50")
         )
@@ -143,18 +148,14 @@ init(_) ->
     {ok, reinit_cache(State)}.
 
 
-handle_db_event({Event, DbName}) ->
-    [{auth_db_name, AuthDbName}] = ets:lookup(?STATE, auth_db_name),
-    case DbName =:= AuthDbName of
-    true ->
-        case Event of
-        created -> gen_server:call(?MODULE, reinit_cache, infinity);
-        compacted -> gen_server:call(?MODULE, auth_db_compacted, infinity);
-        _Else   -> ok
-        end;
-    false ->
-        ok
-    end.
+handle_db_event(_DbName, created, St) ->
+    gen_server:call(?MODULE, reinit_cache, infinity),
+    {ok, St};
+handle_db_event(_DbName, compacted, St) ->
+    gen_server:call(?MODULE, auth_db_compacted, infinity),
+    {ok, St};
+handle_db_event(_, _, St) ->
+    {ok, St}.
 
 
 handle_call(reinit_cache, _From, State) ->
@@ -206,6 +207,15 @@ handle_cast({cache_hit, UserName}, State) ->
     {noreply, State}.
 
 
+handle_info({'EXIT', LPid, _Reason}, #state{event_listener=LPid}=State) ->
+    erlang:send_after(5000, self(), restart_event_listener),
+    {noreply, State#state{event_listener=undefined}};
+handle_info(restart_event_listener, State) ->
+    [{auth_db_name, AuthDbName}] = ets:lookup(?STATE, auth_db_name),
+    {ok, NewListener} = couch_event:link_listener(
+            ?MODULE, handle_db_event, nil, [{dbanme, AuthDbName}]
+        ),
+    {noreply, State#state{event_listener=NewListener}};
 handle_info({gen_event_EXIT, {config_listener, ?MODULE}, _Reason}, State) ->
     erlang:send_after(5000, self(), restart_config_listener),
     {noreply, State};
@@ -216,8 +226,8 @@ handle_info({'DOWN', Ref, _, _, _Reason}, #state{db_mon_ref = Ref} = State) ->
     {noreply, reinit_cache(State)}.
 
 
-terminate(_Reason, #state{db_notifier = Notifier}) ->
-    couch_db_update_notifier:stop(Notifier),
+terminate(_Reason, #state{event_listener = Listener}) ->
+    couch_event:stop_listener(Listener),
     exec_if_auth_db(fun(AuthDb) -> catch couch_db:close(AuthDb) end),
     true = ets:delete(?BY_USER),
     true = ets:delete(?BY_ATIME),


[22/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Remove uuid and epochs from db records

The db record has become used too widely to make for an easy upgrade.
Rather than attempt and fix all the places its used we'll just remove
the new fields and store them in the header which isn't used nearly as
often.

To get the values there are two new API functions `couch_db:get_uuid/1`
and `couch_db:get_epochs/1` that take a db record as the single
argument.

There are also a couple slight behavior changes with this patch. We now
make sure that every db opened has initialized values for the uuid and
epochs. We also transmit those values during compaction so that every
header in a compacted file will have the correct values as well.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/e538ff7f
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/e538ff7f
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/e538ff7f

Branch: refs/heads/windsor-merge-300
Commit: e538ff7f9f9f41ba68159d83adae919ea7941c66
Parents: 9138d07
Author: Paul J. Davis <pa...@gmail.com>
Authored: Thu Jun 13 15:29:59 2013 -0500
Committer: Robert Newson <rn...@apache.org>
Committed: Tue Aug 5 15:21:40 2014 +0100

----------------------------------------------------------------------
 include/couch_db.hrl     |  4 +--
 src/couch_db.erl         | 14 ++++++++--
 src/couch_db_updater.erl | 62 ++++++++++++++++++++++++++++---------------
 3 files changed, 54 insertions(+), 26 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e538ff7f/include/couch_db.hrl
----------------------------------------------------------------------
diff --git a/include/couch_db.hrl b/include/couch_db.hrl
index 5c549ec..bbf74c3 100644
--- a/include/couch_db.hrl
+++ b/include/couch_db.hrl
@@ -180,9 +180,7 @@
     options = [],
     compression,
     before_doc_update = nil, % nil | fun(Doc, Db) -> NewDoc
-    after_doc_read = nil,    % nil | fun(Doc, Db) -> NewDoc
-    uuid = nil,
-    epochs = nil
+    after_doc_read = nil    % nil | fun(Doc, Db) -> NewDoc
     }).
 
 -record(view_fold_helper_funs, {

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e538ff7f/src/couch_db.erl
----------------------------------------------------------------------
diff --git a/src/couch_db.erl b/src/couch_db.erl
index aaab19b..ab5e3da 100644
--- a/src/couch_db.erl
+++ b/src/couch_db.erl
@@ -21,6 +21,7 @@
 -export([open_doc/2,open_doc/3,open_doc_revs/4]).
 -export([set_revs_limit/2,get_revs_limit/1]).
 -export([get_missing_revs/2,name/1,get_update_seq/1,get_committed_update_seq/1]).
+-export([get_uuid/1, get_epochs/1]).
 -export([enum_docs/4,enum_docs_since/5]).
 -export([enum_docs_since_reduce_to_count/1,enum_docs_reduce_to_count/1]).
 -export([increment_update_seq/1,get_purge_seq/1,purge_docs/2,get_last_purged/1]).
@@ -294,6 +295,12 @@ get_doc_count(Db) ->
     {ok, {Count, _, _}} = couch_btree:full_reduce(Db#db.id_tree),
     {ok, Count}.
 
+get_uuid(#db{header=Header}) ->
+    Header#header.uuid.
+
+get_epochs(#db{header=Header}) ->
+    Header#header.epochs.
+
 get_db_info(Db) ->
     #db{fd=Fd,
         header=#db_header{disk_version=DiskVersion},
@@ -304,11 +311,14 @@ get_db_info(Db) ->
         committed_update_seq=CommittedUpdateSeq,
         id_tree = IdBtree,
         seq_tree = SeqBtree,
-        local_tree = LocalBtree,
-        uuid = Uuid
+        local_tree = LocalBtree
     } = Db,
     {ok, Size} = couch_file:bytes(Fd),
     {ok, DbReduction} = couch_btree:full_reduce(IdBtree),
+    Uuid = case get_uuid(Db) of
+        undefined -> null;
+        Uuid0 -> Uuid0
+    end,
     InfoList = [
         {db_name, Name},
         {doc_count, element(1, DbReduction)},

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e538ff7f/src/couch_db_updater.erl
----------------------------------------------------------------------
diff --git a/src/couch_db_updater.erl b/src/couch_db_updater.erl
index f6276c5..eadd6c7 100644
--- a/src/couch_db_updater.erl
+++ b/src/couch_db_updater.erl
@@ -227,9 +227,7 @@ handle_cast({compact_done, CompactFilepath}, #db{filepath=Filepath}=Db) ->
             main_pid = self(),
             filepath = Filepath,
             instance_start_time = Db#db.instance_start_time,
-            revs_limit = Db#db.revs_limit,
-            uuid = Db#db.uuid,
-            epochs = Db#db.epochs
+            revs_limit = Db#db.revs_limit
         }),
 
         ?LOG_DEBUG("CouchDB swapping files ~s and ~s.",
@@ -459,7 +457,7 @@ simple_upgrade_record(Old, _New) ->
 
 init_db(DbName, Filepath, Fd, Header0, Options) ->
     Header1 = simple_upgrade_record(Header0, #db_header{}),
-    Header =
+    Header2 =
     case element(2, Header1) of
     1 -> throw({database_disk_version_error, ?OLD_DISK_VERSION_ERROR});
     2 -> throw({database_disk_version_error, ?OLD_DISK_VERSION_ERROR});
@@ -470,6 +468,11 @@ init_db(DbName, Filepath, Fd, Header0, Options) ->
     _ -> throw({database_disk_version_error, "Incorrect disk header version"})
     end,
 
+    Header = Header2#db_header{
+        uuid = initialize_uuid(Header2),
+        epochs = initialize_epochs(Header2)
+    },
+
     {ok, FsyncOptions} = couch_util:parse_term(
             config:get("couchdb", "fsync_options",
                     "[before_header, after_header, on_file_open]")),
@@ -505,7 +508,7 @@ init_db(DbName, Filepath, Fd, Header0, Options) ->
     StartTime = ?l2b(io_lib:format("~p",
             [(MegaSecs*1000000*1000000) + (Secs*1000000) + MicroSecs])),
     ok = couch_file:set_db_pid(Fd, self()),
-    #db{
+    Db = #db{
         fd=Fd,
         fd_monitor = erlang:monitor(process, Fd),
         header=Header,
@@ -524,10 +527,20 @@ init_db(DbName, Filepath, Fd, Header0, Options) ->
         options = Options,
         compression = Compression,
         before_doc_update = couch_util:get_value(before_doc_update, Options, nil),
-        after_doc_read = couch_util:get_value(after_doc_read, Options, nil),
-        uuid = initialize_uuid(Header),
-        epochs = initialize_epochs(Header)
-        }.
+        after_doc_read = couch_util:get_value(after_doc_read, Options, nil)
+        },
+
+    % If we just created a new UUID while upgrading a
+    % database then we want to flush that to disk or
+    % we risk sending out the uuid and having the db
+    % crash which would result in it generating a new
+    % uuid each time it was reopened.
+    case Header /= Header2 of
+        true ->
+            sync_header(Db, Header);
+        false ->
+            Db
+    end.
 
 
 close_db(#db{fd_monitor = Ref}) ->
@@ -808,9 +821,7 @@ db_to_header(Db, Header) ->
         id_tree_state = couch_btree:get_state(Db#db.id_tree),
         local_tree_state = couch_btree:get_state(Db#db.local_tree),
         security_ptr = Db#db.security_ptr,
-        revs_limit = Db#db.revs_limit,
-        uuid = Db#db.uuid,
-        epochs = update_epochs(Db)}.
+        revs_limit = Db#db.revs_limit}.
 
 commit_data(Db) ->
     commit_data(Db, false).
@@ -831,7 +842,7 @@ commit_data(Db, _) ->
         NewHeader -> sync_header(Db, NewHeader)
     end.
 
-sync_header(Db, NewHeader) ->
+sync_header(Db, NewHeader0) ->
     #db{
         fd = Fd,
         filepath = FilePath,
@@ -839,6 +850,8 @@ sync_header(Db, NewHeader) ->
         waiting_delayed_commit = Timer
     } = Db,
 
+    NewHeader = update_epochs(NewHeader0),
+
     if is_reference(Timer) -> erlang:cancel_timer(Timer); true -> ok end,
 
     Before = lists:member(before_header, FsyncOptions),
@@ -1034,11 +1047,11 @@ copy_compact(Db, NewDb0, Retry) ->
 
 
 start_copy_compact(#db{}=Db) ->
-    #db{name=Name, filepath=Filepath, options=Options} = Db,
+    #db{name=Name, filepath=Filepath, options=Options, header=Header} = Db,
     ?LOG_DEBUG("Compaction process spawned for db \"~s\"", [Name]),
 
     {ok, NewDb, DName, DFd, MFd, Retry} =
-        open_compaction_files(Name, Filepath, Options),
+        open_compaction_files(Name, Header, Filepath, Options),
     erlang:monitor(process, MFd),
 
     % This is a bit worrisome. init_db/4 will monitor the data fd
@@ -1059,7 +1072,7 @@ start_copy_compact(#db{}=Db) ->
     gen_server:cast(Db#db.main_pid, {compact_done, DName}).
 
 
-open_compaction_files(DbName, DbFilePath, Options) ->
+open_compaction_files(DbName, Header, DbFilePath, Options) ->
     DataFile = DbFilePath ++ ".compact.data",
     MetaFile = DbFilePath ++ ".compact.meta",
     {ok, DataFd, DataHdr} = open_compaction_file(DataFile),
@@ -1071,15 +1084,22 @@ open_compaction_files(DbName, DbFilePath, Options) ->
             Db1 = bind_emsort(Db0, MetaFd, A#comp_header.meta_state),
             {ok, Db1, DataFile, DataFd, MetaFd, Db0#db.id_tree};
         {#db_header{}, _} ->
-            ok = reset_compaction_file(MetaFd, #db_header{}),
+            NewHeader = #db_header{
+                uuid = DataHdr#db_header.uuid,
+                epochs = DataHdr#db_header.epochs
+            },
+            ok = reset_compaction_file(MetaFd, NewHeader),
             Db0 = init_db(DbName, DataFile, DataFd, DataHdr, Options),
             Db1 = bind_emsort(Db0, MetaFd, nil),
             {ok, Db1, DataFile, DataFd, MetaFd, Db0#db.id_tree};
         _ ->
-            Header = #db_header{},
-            ok = reset_compaction_file(DataFd, Header),
-            ok = reset_compaction_file(MetaFd, Header),
-            Db0 = init_db(DbName, DataFile, DataFd, Header, Options),
+            NewHeader = #db_header{
+                uuid = Header#db_header.uuid,
+                epochs = Header#db_header.epochs
+            },
+            ok = reset_compaction_file(DataFd, NewHeader),
+            ok = reset_compaction_file(MetaFd, NewHeader),
+            Db0 = init_db(DbName, DataFile, DataFd, NewHeader, Options),
             Db1 = bind_emsort(Db0, MetaFd, nil),
             {ok, Db1, DataFile, DataFd, MetaFd, nil}
     end.


[43/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Don't match on response from couch_stats_collector:decrement/1

Margaret's calls aren't always going to return `ok`, so we need to be
a bit more defensive here.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/c31b27a7
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/c31b27a7
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/c31b27a7

Branch: refs/heads/windsor-merge-300
Commit: c31b27a7db07243247db668055ac571932e7e692
Parents: 5686e73
Author: Benjamin Anderson <b...@banjiewen.net>
Authored: Wed Nov 20 19:02:27 2013 -0800
Committer: Robert Newson <rn...@apache.org>
Committed: Wed Aug 6 12:30:52 2014 +0100

----------------------------------------------------------------------
 src/couch_stats_collector.erl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/c31b27a7/src/couch_stats_collector.erl
----------------------------------------------------------------------
diff --git a/src/couch_stats_collector.erl b/src/couch_stats_collector.erl
index 99814de..2ed647c 100644
--- a/src/couch_stats_collector.erl
+++ b/src/couch_stats_collector.erl
@@ -106,7 +106,7 @@ handle_cast({track_process_count, Pid, Stat}, State) ->
 
 handle_info({'DOWN', Ref, _, _, _}, State) ->
     Stat = dict:fetch(Ref, State),
-    ok = couch_stats_collector:decrement(Stat),
+    couch_stats_collector:decrement(Stat),
     {noreply, dict:erase(Ref, State)}.
 
 code_change(_OldVersion, State, _Extra) when is_list(State) ->


[35/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Revert "Add references to docs to prevent dups from being collapsed"

Bulk docs requests may have duplicates or multiple docs with the same id.
These were being collapsed in a dictionary as messages are passed from
merge_rev_trees in couch_db_updater to collect_results in couch_db.
Attaching a reference allows each to be processed correctly.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/fcce4b72
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/fcce4b72
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/fcce4b72

Branch: refs/heads/windsor-merge-300
Commit: fcce4b723424b4da8b51a0b225db5de533fbd4aa
Parents: f2368da
Author: Robert Newson <rn...@apache.org>
Authored: Tue Aug 5 20:50:18 2014 +0100
Committer: Robert Newson <rn...@apache.org>
Committed: Wed Aug 6 10:34:11 2014 +0100

----------------------------------------------------------------------
 src/couch_db.erl         | 126 +++++++++++++++++++-----------------------
 src/couch_db_updater.erl |  31 ++++++-----
 2 files changed, 73 insertions(+), 84 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/fcce4b72/src/couch_db.erl
----------------------------------------------------------------------
diff --git a/src/couch_db.erl b/src/couch_db.erl
index 1487681..0b9e490 100644
--- a/src/couch_db.erl
+++ b/src/couch_db.erl
@@ -500,22 +500,22 @@ update_docs(Db, Docs) ->
 % group_alike_docs groups the sorted documents into sublist buckets, by id.
 % ([DocA, DocA, DocB, DocC], []) -> [[DocA, DocA], [DocB], [DocC]]
 group_alike_docs(Docs) ->
-    Sorted = lists:sort(fun({#doc{id=A},_},{#doc{id=B},_})-> A < B end, Docs),
+    Sorted = lists:sort(fun(#doc{id=A},#doc{id=B})-> A < B end, Docs),
     group_alike_docs(Sorted, []).
 
 group_alike_docs([], Buckets) ->
-    lists:reverse(lists:map(fun lists:reverse/1, Buckets));
+    lists:reverse(Buckets);
 group_alike_docs([Doc|Rest], []) ->
     group_alike_docs(Rest, [[Doc]]);
-group_alike_docs([{Doc,Ref}|Rest], [Bucket|RestBuckets]) ->
-    [{#doc{id=BucketId},_Ref}|_] = Bucket,
+group_alike_docs([Doc|Rest], [Bucket|RestBuckets]) ->
+    [#doc{id=BucketId}|_] = Bucket,
     case Doc#doc.id == BucketId of
     true ->
         % add to existing bucket
-        group_alike_docs(Rest, [[{Doc,Ref}|Bucket]|RestBuckets]);
+        group_alike_docs(Rest, [[Doc|Bucket]|RestBuckets]);
     false ->
         % add to new bucket
-       group_alike_docs(Rest, [[{Doc,Ref}]|[Bucket|RestBuckets]])
+       group_alike_docs(Rest, [[Doc]|[Bucket|RestBuckets]])
     end.
 
 validate_doc_update(#db{}=Db, #doc{id= <<"_design/",_/binary>>}=Doc, _GetDiskDocFun) ->
@@ -630,8 +630,10 @@ prep_and_validate_updates(_Db, [], [], _AllowConflict, AccPrepped,
    {AccPrepped, AccFatalErrors};
 prep_and_validate_updates(Db, [DocBucket|RestBuckets], [not_found|RestLookups],
         AllowConflict, AccPrepped, AccErrors) ->
+    [#doc{id=Id}|_]=DocBucket,
+    % no existing revs are known,
     {PreppedBucket, AccErrors3} = lists:foldl(
-        fun({#doc{revs=Revs}=Doc,Ref}, {AccBucket, AccErrors2}) ->
+        fun(#doc{revs=Revs}=Doc, {AccBucket, AccErrors2}) ->
             case couch_doc:has_stubs(Doc) of
             true ->
                 couch_doc:merge_stubs(Doc, #doc{}); % will throw exception
@@ -641,19 +643,19 @@ prep_and_validate_updates(Db, [DocBucket|RestBuckets], [not_found|RestLookups],
             {0, []} ->
                 case validate_doc_update(Db, Doc, fun() -> nil end) of
                 ok ->
-                    {[{Doc, Ref} | AccBucket], AccErrors2};
+                    {[Doc | AccBucket], AccErrors2};
                 Error ->
-                    {AccBucket, [{Ref, Error} | AccErrors2]}
+                    {AccBucket, [{{Id, {0, []}}, Error} | AccErrors2]}
                 end;
             _ ->
                 % old revs specified but none exist, a conflict
-                {AccBucket, [{Ref, conflict} | AccErrors2]}
+                {AccBucket, [{{Id, Revs}, conflict} | AccErrors2]}
             end
         end,
         {[], AccErrors}, DocBucket),
 
     prep_and_validate_updates(Db, RestBuckets, RestLookups, AllowConflict,
-            [lists:reverse(PreppedBucket) | AccPrepped], AccErrors3);
+            [PreppedBucket | AccPrepped], AccErrors3);
 prep_and_validate_updates(Db, [DocBucket|RestBuckets],
         [{ok, #full_doc_info{rev_tree=OldRevTree}=OldFullDocInfo}|RestLookups],
         AllowConflict, AccPrepped, AccErrors) ->
@@ -663,14 +665,14 @@ prep_and_validate_updates(Db, [DocBucket|RestBuckets],
         {Leaf, {Start, [RevId | _]} = Revs} <- Leafs
     ]),
     {PreppedBucket, AccErrors3} = lists:foldl(
-        fun({Doc, Ref}, {Docs2Acc, AccErrors2}) ->
+        fun(Doc, {Docs2Acc, AccErrors2}) ->
             case prep_and_validate_update(Db, Doc, OldFullDocInfo,
                     LeafRevsDict, AllowConflict) of
             {ok, Doc2} ->
-                {[{Doc2, Ref} | Docs2Acc], AccErrors2};
-            {Error, #doc{}} ->
+                {[Doc2 | Docs2Acc], AccErrors2};
+            {Error, #doc{id=Id,revs=Revs}} ->
                 % Record the error
-                {Docs2Acc, [{Ref, Error} |AccErrors2]}
+                {Docs2Acc, [{{Id, Revs}, Error} |AccErrors2]}
             end
         end,
         {[], AccErrors}, DocBucket),
@@ -690,7 +692,7 @@ prep_and_validate_replicated_updates(Db, [Bucket|RestBuckets], [OldInfo|RestOldI
     case OldInfo of
     not_found ->
         {ValidatedBucket, AccErrors3} = lists:foldl(
-            fun({Doc, Ref}, {AccPrepped2, AccErrors2}) ->
+            fun(Doc, {AccPrepped2, AccErrors2}) ->
                 case couch_doc:has_stubs(Doc) of
                 true ->
                     couch_doc:merge_stubs(Doc, #doc{}); % will throw exception
@@ -698,7 +700,7 @@ prep_and_validate_replicated_updates(Db, [Bucket|RestBuckets], [OldInfo|RestOldI
                 end,
                 case validate_doc_update(Db, Doc, fun() -> nil end) of
                 ok ->
-                    {[{Doc, Ref} | AccPrepped2], AccErrors2};
+                    {[Doc | AccPrepped2], AccErrors2};
                 Error ->
                     {AccPrepped2, [{Doc, Error} | AccErrors2]}
                 end
@@ -709,7 +711,7 @@ prep_and_validate_replicated_updates(Db, [Bucket|RestBuckets], [OldInfo|RestOldI
         OldLeafs = couch_key_tree:get_all_leafs_full(OldTree),
         OldLeafsLU = [{Start, RevId} || {Start, [{RevId, _}|_]} <- OldLeafs],
         NewRevTree = lists:foldl(
-            fun({NewDoc, _Ref}, AccTree) ->
+            fun(NewDoc, AccTree) ->
                 {NewTree, _} = couch_key_tree:merge(AccTree,
                     couch_doc:to_path(NewDoc), Db#db.revs_limit),
                 NewTree
@@ -719,7 +721,7 @@ prep_and_validate_replicated_updates(Db, [Bucket|RestBuckets], [OldInfo|RestOldI
         LeafRevsFullDict = dict:from_list( [{{Start, RevId}, FullPath} || {Start, [{RevId, _}|_]}=FullPath <- Leafs]),
         {ValidatedBucket, AccErrors3} =
         lists:foldl(
-            fun({#doc{id=Id,revs={Pos, [RevId|_]}}=Doc, Ref}, {AccValidated, AccErrors2}) ->
+            fun(#doc{id=Id,revs={Pos, [RevId|_]}}=Doc, {AccValidated, AccErrors2}) ->
                 IsOldLeaf = lists:member({Pos, RevId}, OldLeafsLU),
                 case dict:find({Pos, RevId}, LeafRevsFullDict) of
                 {ok, {Start, Path}} when not IsOldLeaf ->
@@ -748,7 +750,7 @@ prep_and_validate_replicated_updates(Db, [Bucket|RestBuckets], [OldInfo|RestOldI
 
                     case validate_doc_update(Db, Doc2, GetDiskDocFun) of
                     ok ->
-                        {[{Doc2, Ref} | AccValidated], AccErrors2};
+                        {[Doc2 | AccValidated], AccErrors2};
                     Error ->
                         {AccValidated, [{Doc, Error} | AccErrors2]}
                     end;
@@ -780,10 +782,10 @@ new_revs([], OutBuckets, IdRevsAcc) ->
     {lists:reverse(OutBuckets), IdRevsAcc};
 new_revs([Bucket|RestBuckets], OutBuckets, IdRevsAcc) ->
     {NewBucket, IdRevsAcc3} = lists:mapfoldl(
-        fun({#doc{revs={Start, RevIds}}=Doc, Ref}, IdRevsAcc2)->
+        fun(#doc{id=Id,revs={Start, RevIds}}=Doc, IdRevsAcc2)->
         NewRevId = new_revid(Doc),
-        {{Doc#doc{revs={Start+1, [NewRevId | RevIds]}}, Ref},
-            [{Ref, {ok, {Start+1, NewRevId}}} | IdRevsAcc2]}
+        {Doc#doc{revs={Start+1, [NewRevId | RevIds]}},
+            [{{Id, {Start, RevIds}}, {ok, {Start+1, NewRevId}}} | IdRevsAcc2]}
     end, IdRevsAcc, Bucket),
     new_revs(RestBuckets, [NewBucket|OutBuckets], IdRevsAcc3).
 
@@ -802,17 +804,15 @@ check_dup_atts2(_) ->
 
 update_docs(Db, Docs, Options, replicated_changes) ->
     increment_stat(Db, {couchdb, database_writes}),
-    % associate reference with each doc in order to track duplicates
-    Docs2 = lists:map(fun(Doc) -> {Doc, make_ref()} end, Docs),
-    DocBuckets = before_docs_update(Db, group_alike_docs(Docs2)),
+    DocBuckets = before_docs_update(Db, group_alike_docs(Docs)),
     case (Db#db.validate_doc_funs /= []) orelse
         lists:any(
-            fun({#doc{id= <<?DESIGN_DOC_PREFIX, _/binary>>}, _Ref}) -> true;
-            ({#doc{atts=Atts}, _Ref}) ->
+            fun(#doc{id= <<?DESIGN_DOC_PREFIX, _/binary>>}) -> true;
+            (#doc{atts=Atts}) ->
                 Atts /= []
-            end, Docs2) of
+            end, Docs) of
     true ->
-        Ids = [Id || [{#doc{id=Id}, _Ref}|_] <- DocBuckets],
+        Ids = [Id || [#doc{id=Id}|_] <- DocBuckets],
         ExistingDocs = get_full_doc_infos(Db, Ids),
 
         {DocBuckets2, DocErrors} =
@@ -822,8 +822,8 @@ update_docs(Db, Docs, Options, replicated_changes) ->
         DocErrors = [],
         DocBuckets3 = DocBuckets
     end,
-    DocBuckets4 = [[{doc_flush_atts(check_dup_atts(Doc), Db#db.fd), Ref}
-            || {Doc, Ref} <- Bucket] || Bucket <- DocBuckets3],
+    DocBuckets4 = [[doc_flush_atts(check_dup_atts(Doc), Db#db.fd)
+            || Doc <- Bucket] || Bucket <- DocBuckets3],
     {ok, []} = write_and_commit(Db, DocBuckets4, [], [merge_conflicts | Options]),
     {ok, DocErrors};
 
@@ -833,30 +833,28 @@ update_docs(Db, Docs, Options, interactive_edit) ->
     % go ahead and generate the new revision ids for the documents.
     % separate out the NonRep documents from the rest of the documents
 
-    % associate reference with each doc in order to track duplicates
-    Docs2 = lists:map(fun(Doc) -> {Doc, make_ref()} end,Docs),
-    {Docs3, NonRepDocs} = lists:foldl(
-         fun({#doc{id=Id},_Ref}=Doc, {DocsAcc, NonRepDocsAcc}) ->
+    {Docs2, NonRepDocs} = lists:foldl(
+         fun(#doc{id=Id}=Doc, {DocsAcc, NonRepDocsAcc}) ->
             case Id of
             <<?LOCAL_DOC_PREFIX, _/binary>> ->
                 {DocsAcc, [Doc | NonRepDocsAcc]};
             Id->
                 {[Doc | DocsAcc], NonRepDocsAcc}
             end
-        end, {[], []}, Docs2),
+        end, {[], []}, Docs),
 
-    DocBuckets = before_docs_update(Db, group_alike_docs(Docs3)),
+    DocBuckets = before_docs_update(Db, group_alike_docs(Docs2)),
 
     case (Db#db.validate_doc_funs /= []) orelse
         lists:any(
-            fun({#doc{id= <<?DESIGN_DOC_PREFIX, _/binary>>}, _Ref}) ->
+            fun(#doc{id= <<?DESIGN_DOC_PREFIX, _/binary>>}) ->
                 true;
-            ({#doc{atts=Atts}, _Ref}) ->
+            (#doc{atts=Atts}) ->
                 Atts /= []
-            end, Docs3) of
+            end, Docs2) of
     true ->
         % lookup the doc by id and get the most recent
-        Ids = [Id || [{#doc{id=Id}, _Ref}|_] <- DocBuckets],
+        Ids = [Id || [#doc{id=Id}|_] <- DocBuckets],
         ExistingDocInfos = get_full_doc_infos(Db, Ids),
 
         {DocBucketsPrepped, PreCommitFailures} = prep_and_validate_updates(Db,
@@ -870,38 +868,29 @@ update_docs(Db, Docs, Options, interactive_edit) ->
     end,
 
     if (AllOrNothing) and (PreCommitFailures /= []) ->
-        {aborted,
-         lists:foldl(fun({#doc{id=Id,revs=Revs}, Ref},Acc) ->
-                         case lists:keyfind(Ref,1,PreCommitFailures) of
-                         {Ref, Error} ->
-                             case Revs of
-                             {Pos, [RevId|_]} ->
-                                 [{{Id,{Pos, RevId}}, Error} | Acc];
-                             {0, []} ->
-                                 [{{Id,{0, <<>>}}, Error} | Acc]
-                             end;
-                         false ->
-                             Acc
-                         end
-                     end,[],Docs3)};
-
+        {aborted, lists:map(
+            fun({{Id,{Pos, [RevId|_]}}, Error}) ->
+                {{Id, {Pos, RevId}}, Error};
+            ({{Id,{0, []}}, Error}) ->
+                {{Id, {0, <<>>}}, Error}
+            end, PreCommitFailures)};
     true ->
         Options2 = if AllOrNothing -> [merge_conflicts];
                 true -> [] end ++ Options,
         DocBuckets3 = [[
-                {doc_flush_atts(set_new_att_revpos(
-                        check_dup_atts(Doc)), Db#db.fd), Ref}
-                || {Doc, Ref} <- B] || B <- DocBuckets2],
+                doc_flush_atts(set_new_att_revpos(
+                        check_dup_atts(Doc)), Db#db.fd)
+                || Doc <- B] || B <- DocBuckets2],
         {DocBuckets4, IdRevs} = new_revs(DocBuckets3, [], []),
 
         {ok, CommitResults} = write_and_commit(Db, DocBuckets4, NonRepDocs, Options2),
 
         ResultsDict = dict:from_list(IdRevs ++ CommitResults ++ PreCommitFailures),
         {ok, lists:map(
-            fun({#doc{}, Ref}) ->
-                {ok, Result} = dict:find(Ref, ResultsDict),
+            fun(#doc{id=Id,revs={Pos, RevIds}}) ->
+                {ok, Result} = dict:find({Id, {Pos, RevIds}}, ResultsDict),
                 Result
-            end, Docs2)}
+            end, Docs)}
     end.
 
 % Returns the first available document on disk. Input list is a full rev path
@@ -962,7 +951,7 @@ write_and_commit(#db{main_pid=Pid, user_ctx=Ctx}=Db, DocBuckets1,
             % compaction. Retry by reopening the db and writing to the current file
             {ok, Db2} = open(Db#db.name, [{user_ctx, Ctx}]),
             DocBuckets2 = [
-                [{doc_flush_atts(Doc, Db2#db.fd), Ref} || {Doc, Ref} <- Bucket] ||
+                [doc_flush_atts(Doc, Db2#db.fd) || Doc <- Bucket] ||
                 Bucket <- DocBuckets1
             ],
             % We only retry once
@@ -981,7 +970,7 @@ write_and_commit(#db{main_pid=Pid, user_ctx=Ctx}=Db, DocBuckets1,
 
 prepare_doc_summaries(Db, BucketList) ->
     [lists:map(
-        fun({#doc{body = Body, atts = Atts} = Doc, Ref}) ->
+        fun(#doc{body = Body, atts = Atts} = Doc) ->
             DiskAtts = [{N, T, P, AL, DL, R, M, E} ||
                 #att{name = N, type = T, data = {_, P}, md5 = M, revpos = R,
                     att_len = AL, disk_len = DL, encoding = E} <- Atts],
@@ -992,7 +981,7 @@ prepare_doc_summaries(Db, BucketList) ->
                 nil
             end,
             SummaryChunk = couch_db_updater:make_doc_summary(Db, {Body, DiskAtts}),
-            {Doc#doc{body = {summary, SummaryChunk, AttsFd}}, Ref}
+            Doc#doc{body = {summary, SummaryChunk, AttsFd}}
         end,
         Bucket) || Bucket <- BucketList].
 
@@ -1001,9 +990,8 @@ before_docs_update(#db{before_doc_update = nil}, BucketList) ->
     BucketList;
 before_docs_update(#db{before_doc_update = Fun} = Db, BucketList) ->
     [lists:map(
-        fun({Doc, Ref}) ->
-            NewDoc = Fun(couch_doc:with_ejson_body(Doc), Db),
-            {NewDoc, Ref}
+        fun(Doc) ->
+            Fun(couch_doc:with_ejson_body(Doc), Db)
         end,
         Bucket) || Bucket <- BucketList].
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/fcce4b72/src/couch_db_updater.erl
----------------------------------------------------------------------
diff --git a/src/couch_db_updater.erl b/src/couch_db_updater.erl
index 5cdb876..09028bd 100644
--- a/src/couch_db_updater.erl
+++ b/src/couch_db_updater.erl
@@ -319,11 +319,11 @@ handle_info({'DOWN', Ref, _, _, Reason}, #db{fd_monitor=Ref, name=Name} = Db) ->
 code_change(_OldVsn, State, _Extra) ->
     {ok, State}.
 
-merge_updates([[{_,{#doc{id=X},_}}|_]=A|RestA], [[{_,{#doc{id=X},_}}|_]=B|RestB]) ->
+merge_updates([[{_,#doc{id=X}}|_]=A|RestA], [[{_,#doc{id=X}}|_]=B|RestB]) ->
     [A++B | merge_updates(RestA, RestB)];
-merge_updates([[{_,{#doc{id=X},_}}|_]|_]=A, [[{_,{#doc{id=Y},_}}|_]|_]=B) when X < Y ->
+merge_updates([[{_,#doc{id=X}}|_]|_]=A, [[{_,#doc{id=Y}}|_]|_]=B) when X < Y ->
     [hd(A) | merge_updates(tl(A), B)];
-merge_updates([[{_,{#doc{id=X},_}}|_]|_]=A, [[{_,{#doc{id=Y},_}}|_]|_]=B) when X > Y ->
+merge_updates([[{_,#doc{id=X}}|_]|_]=A, [[{_,#doc{id=Y}}|_]|_]=B) when X > Y ->
     [hd(B) | merge_updates(A, tl(B))];
 merge_updates([], RestB) ->
     RestB;
@@ -597,9 +597,9 @@ flush_trees(#db{fd = Fd} = Db,
     flush_trees(Db, RestUnflushed, [InfoFlushed | AccFlushed]).
 
 
-send_result(Client, Ref, NewResult) ->
+send_result(Client, Id, OriginalRevs, NewResult) ->
     % used to send a result to the client
-    catch(Client ! {result, self(), {Ref, NewResult}}).
+    catch(Client ! {result, self(), {{Id, OriginalRevs}, NewResult}}).
 
 merge_rev_trees(_Limit, _Merge, [], [], AccNewInfos, AccRemoveSeqs, AccSeq) ->
     {ok, lists:reverse(AccNewInfos), AccRemoveSeqs, AccSeq};
@@ -608,12 +608,12 @@ merge_rev_trees(Limit, MergeConflicts, [NewDocs|RestDocsList],
     #full_doc_info{id=Id,rev_tree=OldTree,deleted=OldDeleted0,update_seq=OldSeq}
             = OldDocInfo,
     {NewRevTree, _} = lists:foldl(
-        fun({Client, {#doc{revs={Pos,[_Rev|PrevRevs]}}=NewDoc, Ref}}, {AccTree, OldDeleted}) ->
+        fun({Client, #doc{revs={Pos,[_Rev|PrevRevs]}}=NewDoc}, {AccTree, OldDeleted}) ->
             if not MergeConflicts ->
                 case couch_key_tree:merge(AccTree, couch_doc:to_path(NewDoc),
                     Limit) of
                 {_NewTree, conflicts} when (not OldDeleted) ->
-                    send_result(Client, Ref, conflict),
+                    send_result(Client, Id, {Pos-1,PrevRevs}, conflict),
                     {AccTree, OldDeleted};
                 {NewTree, conflicts} when PrevRevs /= [] ->
                     % Check to be sure if prev revision was specified, it's
@@ -625,7 +625,7 @@ merge_rev_trees(Limit, MergeConflicts, [NewDocs|RestDocsList],
                     if IsPrevLeaf ->
                         {NewTree, OldDeleted};
                     true ->
-                        send_result(Client, Ref, conflict),
+                        send_result(Client, Id, {Pos-1,PrevRevs}, conflict),
                         {AccTree, OldDeleted}
                     end;
                 {NewTree, no_conflicts} when  AccTree == NewTree ->
@@ -644,10 +644,11 @@ merge_rev_trees(Limit, MergeConflicts, [NewDocs|RestDocsList],
                         {NewTree2, _} = couch_key_tree:merge(AccTree,
                                 couch_doc:to_path(NewDoc2), Limit),
                         % we changed the rev id, this tells the caller we did
-                        send_result(Client, Ref, {ok, {OldPos + 1, NewRevId}}),
+                        send_result(Client, Id, {Pos-1,PrevRevs},
+                            {ok, {OldPos + 1, NewRevId}}),
                         {NewTree2, OldDeleted};
                     true ->
-                        send_result(Client, Ref, conflict),
+                        send_result(Client, Id, {Pos-1,PrevRevs}, conflict),
                         {AccTree, OldDeleted}
                     end;
                 {NewTree, _} ->
@@ -700,7 +701,7 @@ update_docs_int(Db, DocsList, NonRepDocs, MergeConflicts, FullCommit) ->
         update_seq = LastSeq,
         revs_limit = RevsLimit
         } = Db,
-    Ids = [Id || [{_Client, {#doc{id=Id}, _Ref}}|_] <- DocsList],
+    Ids = [Id || [{_Client, #doc{id=Id}}|_] <- DocsList],
     % lookup up the old documents, if they exist.
     OldDocLookups = couch_btree:lookup(DocInfoByIdBTree, Ids),
     OldDocInfos = lists:zipwith(
@@ -750,10 +751,10 @@ update_docs_int(Db, DocsList, NonRepDocs, MergeConflicts, FullCommit) ->
 update_local_docs(Db, []) ->
     {ok, Db};
 update_local_docs(#db{local_tree=Btree}=Db, Docs) ->
-    Ids = [Id || {_Client, {#doc{id=Id}, _Ref}} <- Docs],
+    Ids = [Id || {_Client, #doc{id=Id}} <- Docs],
     OldDocLookups = couch_btree:lookup(Btree, Ids),
     BtreeEntries = lists:zipwith(
-        fun({Client, {#doc{id=Id,deleted=Delete,revs={0,PrevRevs},body=Body}, Ref}}, _OldDocLookup) ->
+        fun({Client, #doc{id=Id,deleted=Delete,revs={0,PrevRevs},body=Body}}, _OldDocLookup) ->
             case PrevRevs of
             [RevStr|_] ->
                 PrevRev = list_to_integer(?b2l(RevStr));
@@ -770,11 +771,11 @@ update_local_docs(#db{local_tree=Btree}=Db, Docs) ->
             % true ->
                 case Delete of
                     false ->
-                        send_result(Client, Ref, {ok,
+                        send_result(Client, Id, {0, PrevRevs}, {ok,
                                 {0, ?l2b(integer_to_list(PrevRev + 1))}}),
                         {update, {Id, {PrevRev + 1, Body}}};
                     true  ->
-                        send_result(Client, Ref,
+                        send_result(Client, Id, {0, PrevRevs},
                                 {ok, {0, <<"0">>}}),
                         {remove, Id}
                 end%;


[48/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Fix indentation in couch_db.hrl

This commit contains only cosmetic changes.

BugzID: 26211


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/2c2f53ec
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/2c2f53ec
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/2c2f53ec

Branch: refs/heads/windsor-merge-300
Commit: 2c2f53ec3eb173829e493af9823cf6d09c8fbdd2
Parents: 371ae11
Author: Benjamin Bastian <be...@gmail.com>
Authored: Mon Dec 16 14:00:07 2013 -0800
Committer: Robert Newson <rn...@apache.org>
Committed: Wed Aug 6 12:52:46 2014 +0100

----------------------------------------------------------------------
 include/couch_db.hrl | 49 +++++++++++++++++++++--------------------------
 1 file changed, 22 insertions(+), 27 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/2c2f53ec/include/couch_db.hrl
----------------------------------------------------------------------
diff --git a/include/couch_db.hrl b/include/couch_db.hrl
index d77a2aa..c57a583 100644
--- a/include/couch_db.hrl
+++ b/include/couch_db.hrl
@@ -47,31 +47,29 @@
 -type path() :: {Start::pos_integer(), branch()}.
 -type tree() :: [branch()]. % sorted by key
 
--record(rev_info,
-    {
+-record(rev_info, {
     rev,
     seq = 0,
     deleted = false,
     body_sp = nil % stream pointer
-    }).
+}).
 
--record(doc_info,
-    {
+-record(doc_info, {
     id = <<"">>,
     high_seq = 0,
     revs = [] % rev_info
-    }).
+}).
 
--record(full_doc_info,
-    {id = <<"">>,
+-record(full_doc_info, {
+    id = <<"">>,
     update_seq = 0,
     deleted = false,
     rev_tree = [],
     leafs_size = 0
-    }).
+}).
 
--record(httpd,
-    {mochi_req,
+-record(httpd, {
+    mochi_req,
     peer,
     method,
     requested_path_parts,
@@ -83,11 +81,10 @@
     auth,
     default_fun,
     url_handlers
-    }).
+}).
 
 
--record(doc,
-    {
+-record(doc, {
     id = <<"">>,
     revs = {0, []},
 
@@ -101,11 +98,10 @@
     % key/value tuple of meta information, provided when using special options:
     % couch_db:open_doc(Db, Id, Options).
     meta = []
-    }).
+}).
 
 
--record(att,
-    {
+-record(att, {
     name,
     type,
     att_len,
@@ -119,18 +115,17 @@
                       %     identity, gzip
                       % additional values to support in the future:
                       %     deflate, compress
-    }).
+}).
 
 
--record(user_ctx,
-    {
+-record(user_ctx, {
     name=null,
     roles=[],
     handler
-    }).
+}).
 
--record(db,
-    {main_pid = nil,
+-record(db, {
+    main_pid = nil,
     compactor_pid = nil,
     instance_start_time, % number of microsecs since jan 1 1970 as a binary string
     fd,
@@ -154,7 +149,7 @@
     compression,
     before_doc_update = nil, % nil | fun(Doc, Db) -> NewDoc
     after_doc_read = nil    % nil | fun(Doc, Db) -> NewDoc
-    }).
+}).
 
 -record(view_fold_helper_funs, {
     reduce_count,
@@ -177,12 +172,12 @@
     json = nil
 }).
 
--record(index_header,
-    {seq=0,
+-record(index_header, {
+    seq=0,
     purge_seq=0,
     id_btree_state=nil,
     view_states=nil
-    }).
+}).
 
 % small value used in revision trees to indicate the revision isn't stored
 -define(REV_MISSING, []).


[06/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Ignore all casts, not just foo


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/ff3b88bc
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/ff3b88bc
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/ff3b88bc

Branch: refs/heads/windsor-merge-300
Commit: ff3b88bc0c26fa92e67ae4ce9735ea45c006145c
Parents: 1caf4b6
Author: Robert Newson <ro...@cloudant.com>
Authored: Tue Apr 30 18:17:44 2013 +0100
Committer: Robert Newson <rn...@apache.org>
Committed: Mon Aug 4 14:16:55 2014 +0100

----------------------------------------------------------------------
 src/couch_native_process.erl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/ff3b88bc/src/couch_native_process.erl
----------------------------------------------------------------------
diff --git a/src/couch_native_process.erl b/src/couch_native_process.erl
index d4b2deb..e59a223 100644
--- a/src/couch_native_process.erl
+++ b/src/couch_native_process.erl
@@ -97,7 +97,7 @@ handle_call({prompt, Data}, _From, State) ->
 handle_cast(garbage_collect, State) ->
     erlang:garbage_collect(),
     {noreply, State};
-handle_cast(foo, State) -> {noreply, State}.
+handle_cast(_, State) -> {noreply, State}.
 
 handle_info({'EXIT',_,normal}, State) -> {noreply, State};
 handle_info({'EXIT',_,Reason}, State) ->


[46/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Fix JSON encoding for missing revisions

This is a super old bug from when we switched to Jiffy. Jiffy doesn't
support Erlang strings as object keys due to Unicode issues. This just
changes the "missing" string to a <<"missing">> binary.

BugzId: 26050


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/ff82e5f5
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/ff82e5f5
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/ff82e5f5

Branch: refs/heads/windsor-merge-300
Commit: ff82e5f55bf6b832196597cd595f86beebcfa042
Parents: aa35d4f
Author: Paul J. Davis <pa...@gmail.com>
Authored: Wed Dec 11 11:09:37 2013 -0600
Committer: Robert Newson <rn...@apache.org>
Committed: Wed Aug 6 12:49:04 2014 +0100

----------------------------------------------------------------------
 src/couch_httpd_db.erl | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/ff82e5f5/src/couch_httpd_db.erl
----------------------------------------------------------------------
diff --git a/src/couch_httpd_db.erl b/src/couch_httpd_db.erl
index 7360dc0..8689944 100644
--- a/src/couch_httpd_db.erl
+++ b/src/couch_httpd_db.erl
@@ -520,7 +520,7 @@ db_doc_req(#httpd{method = 'GET', mochi_req = MochiReq} = Req, Db, DocId) ->
                         send_chunk(Resp, AccSeparator ++ Json);
                     {{not_found, missing}, RevId} ->
                         RevStr = couch_doc:rev_to_str(RevId),
-                        Json = ?JSON_ENCODE({[{"missing", RevStr}]}),
+                        Json = ?JSON_ENCODE({[{<<"missing">>, RevStr}]}),
                         send_chunk(Resp, AccSeparator ++ Json)
                     end,
                     "," % AccSeparator now has a comma
@@ -663,7 +663,7 @@ send_docs_multipart(Req, Results, Options1) ->
              couch_httpd:send_chunk(Resp, <<"\r\n--", OuterBoundary/binary>>);
         ({{not_found, missing}, RevId}) ->
              RevStr = couch_doc:rev_to_str(RevId),
-             Json = ?JSON_ENCODE({[{"missing", RevStr}]}),
+             Json = ?JSON_ENCODE({[{<<"missing">>, RevStr}]}),
              couch_httpd:send_chunk(Resp,
                 [<<"\r\nContent-Type: application/json; error=\"true\"\r\n\r\n">>,
                 Json,


[39/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Dedupe sequence ownership in epochs

We don't increase the update_seq when a node claims ownership of a
shard. This means that if a shard moves between nodes without a write we
can end up with two nodes claiming the same sequence. This just replaces
any duplicates with the more recent claim.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/13132219
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/13132219
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/13132219

Branch: refs/heads/windsor-merge-300
Commit: 131322194d0809705be7f8d5a1a51e20ab6f9e07
Parents: 2ca323c
Author: Paul J. Davis <pa...@gmail.com>
Authored: Thu Oct 3 12:14:45 2013 -0500
Committer: Robert Newson <rn...@apache.org>
Committed: Wed Aug 6 11:53:34 2014 +0100

----------------------------------------------------------------------
 src/couch_db_header.erl | 30 +++++++++++++++++++++++++++++-
 1 file changed, 29 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/13132219/src/couch_db_header.erl
----------------------------------------------------------------------
diff --git a/src/couch_db_header.erl b/src/couch_db_header.erl
index 1d743ef..c23cf38 100644
--- a/src/couch_db_header.erl
+++ b/src/couch_db_header.erl
@@ -236,7 +236,35 @@ upgrade_epochs(#db_header{}=Header) ->
             % and marking the update sequence where it happened.
             [{node(), Header#db_header.update_seq} | Epochs1]
     end,
-    Header#db_header{epochs=NewEpochs}.
+    % Its possible for a node to open a db and claim
+    % ownership but never make a write to the db. This
+    % removes nodes that claimed ownership but never
+    % changed the database.
+    DedupedEpochs = remove_dup_epochs(NewEpochs),
+    Header#db_header{epochs=DedupedEpochs}.
+
+
+% This is slightly relying on the udpate_seq's being sorted
+% in epochs due to how we only ever push things onto the
+% front. Although if we ever had a case where the update_seq
+% is not monotonically increasing I don't know that we'd
+% want to remove dupes (by calling a sort on the input to this
+% function). So for now we don't sort but are relying on the
+% idea that epochs is always sorted.
+remove_dup_epochs([_]=Epochs) ->
+    Epochs;
+remove_dup_epochs([{N1, S}, {_N2, S}]) ->
+    % Seqs match, keep the most recent owner
+    remove_dup_epochs([{N1, S}]);
+remove_dup_epochs([_, _]=Epochs) ->
+    % Seqs don't match.
+    Epochs;
+remove_dup_epochs([{N1, S}, {_N2, S} | Rest]) ->
+    % Seqs match, keep the most recent owner
+    remove_dup_epochs([{N1, S} | Rest]);
+remove_dup_epochs([{N1, S1}, {N2, S2} | Rest]) ->
+    % Seqs don't match, recurse to check others
+    remove_dup_epochs([{N1, S1} | remove_dup_epochs([{N2, S2} | Rest])]).
 
 
 -ifdef(TEST).


[16/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Allow graceful reloading of OS processes

This patch adds a reload/0 command to couch_proc_manager that will cause
all OS processes to be reclaimed at the next earliest convenience (i.e.,
when the current client is done with the process).  It also adds a
get_stale_proc_count/0 function to report the number of processes that
have yet to be reclaimied.

BugzID: 19529


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/1f5673fa
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/1f5673fa
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/1f5673fa

Branch: refs/heads/windsor-merge-300
Commit: 1f5673facc451087edf7622ce337a40a7cb5e996
Parents: 974d85e
Author: Adam Kocoloski <ad...@cloudant.com>
Authored: Mon Jun 3 12:26:35 2013 -0400
Committer: Robert Newson <rn...@apache.org>
Committed: Tue Aug 5 12:03:14 2014 +0100

----------------------------------------------------------------------
 src/couch_proc_manager.erl | 62 +++++++++++++++++++++++++++++++++--------
 1 file changed, 51 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/1f5673fa/src/couch_proc_manager.erl
----------------------------------------------------------------------
diff --git a/src/couch_proc_manager.erl b/src/couch_proc_manager.erl
index e2a4657..8288b13 100644
--- a/src/couch_proc_manager.erl
+++ b/src/couch_proc_manager.erl
@@ -17,7 +17,13 @@
 -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, 
     code_change/3]).
 
--export([start_link/0, get_proc_count/0, new_proc/1]).
+-export([
+    start_link/0,
+    get_proc_count/0,
+    get_stale_proc_count/0,
+    new_proc/1,
+    reload/0
+]).
 
 % config_listener api
 -export([handle_config_change/5]).
@@ -28,7 +34,8 @@
     tab,
     config,
     proc_counts,
-    waiting
+    waiting,
+    threshold_ts
 }).
 
 -record(client, {
@@ -46,7 +53,8 @@
     ddoc_keys = [],
     prompt_fun,
     set_timeout_fun,
-    stop_fun
+    stop_fun,
+    t0 = os:timestamp()
 }).
 
 start_link() ->
@@ -55,6 +63,12 @@ start_link() ->
 get_proc_count() ->
     gen_server:call(?MODULE, get_proc_count).
 
+get_stale_proc_count() ->
+    gen_server:call(?MODULE, get_stale_proc_count).
+
+reload() ->
+    gen_server:call(?MODULE, bump_threshold_ts).
+
 init([]) ->
     process_flag(trap_exit, true),
     ok = config:listen_for_changes(?MODULE, nil),
@@ -72,6 +86,11 @@ handle_call(get_table, _From, State) ->
 handle_call(get_proc_count, _From, State) ->
     {reply, ets:info(State#state.tab, size), State};
 
+handle_call(get_stale_proc_count, _From, State) ->
+    #state{tab = Tab, threshold_ts = T0} = State,
+    MatchSpec = [{#proc_int{t0='$1', _='_'}, [{'<', '$1', T0}], [true]}],
+    {reply, ets:select_count(Tab, MatchSpec), State};
+
 handle_call({get_proc, #doc{body={Props}}=DDoc, DDocKey}, From, State) ->
     {ClientPid, _} = From,
     Lang = couch_util:to_binary(
@@ -112,6 +131,16 @@ handle_call({ret_proc, #proc{client=Ref, lang=Lang0} = Proc}, _From, State) ->
     % table before the insert.  Don't know which approach is cheaper.
     {reply, true, return_proc(State, Proc#proc{lang=Lang})};
 
+handle_call(bump_threshold_ts, _From, #state{tab = Tab} = State) ->
+    FoldFun = fun(#proc_int{client = nil, pid = Pid}, _) ->
+        gen_server:cast(Pid, stop),
+        ets:delete(Tab, Pid);
+    (_, _) ->
+        ok
+    end,
+    ets:foldl(FoldFun, nil, Tab),
+    {reply, ok, State#state{threshold_ts = os:timestamp()}};
+
 handle_call(_Call, _From, State) ->
     {reply, ignored, State}.
 
@@ -199,11 +228,17 @@ terminate(_Reason, #state{tab=Tab}) ->
     ets:foldl(fun(#proc_int{pid=P}, _) -> couch_util:shutdown_sync(P) end, 0, Tab),
     ok.
 
-code_change(_OldVsn, #state{tab = Tab} = State, _Extra) ->
-    NewTab = ets:new(procs, [ordered_set, {keypos, #proc_int.pid}]),
-    true = ets:insert(NewTab, ets:tab2list(Tab)),
-    true = ets:delete(Tab),
-    {ok, State#state{tab = NewTab}}.
+code_change(_OldVsn, {state, Tab}, _Extra) ->
+    State = #state{tab = Tab, threshold_ts = {0,0,0}},
+    ProcInts = lists:map(
+        fun(#proc{} = P) ->
+            setelement(1, erlang:append_element(P, os:timestamp()), proc_int)
+        end,
+        ets:tab2list(Tab)
+    ),
+    ets:delete_all_objects(Tab),
+    ets:insert(Tab, ProcInts),
+    {ok, State}.
 
 handle_config_change("query_server_config", _, _, _, _) ->
     gen_server:cast(?MODULE, reload_config),
@@ -353,13 +388,18 @@ return_proc(#state{} = State, #proc{} = Proc) ->
             ok
     end;
 return_proc(#state{} = State, #proc_int{} = ProcInt) ->
-    #state{tab = Tab, waiting = Waiting} = State,
+    #state{tab = Tab, waiting = Waiting, threshold_ts = T0} = State,
     #proc_int{pid = Pid, lang = Lang} = ProcInt,
     case is_process_alive(Pid) of true ->
         case get_waiting_client(Waiting, Lang) of
             nil ->
-                gen_server:cast(Pid, garbage_collect),
-                ets:insert(Tab, ProcInt#proc_int{client=nil}),
+                if ProcInt#proc_int.t0 < T0 ->
+                    gen_server:cast(Pid, stop),
+                    ets:delete(Tab, Pid);
+                true ->
+                    gen_server:cast(Pid, garbage_collect),
+                    ets:insert(Tab, ProcInt#proc_int{client=nil})
+                end,
                 State;
             #client{}=Client ->
                 From = Client#client.from,


[21/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Export btree configured functions


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/9138d07b
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/9138d07b
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/9138d07b

Branch: refs/heads/windsor-merge-300
Commit: 9138d07bcb2df0cac20e7eba5334bc5957516739
Parents: ea2b1ee
Author: Paul J. Davis <pa...@gmail.com>
Authored: Fri Jun 7 15:17:39 2013 -0500
Committer: Robert Newson <rn...@apache.org>
Committed: Tue Aug 5 12:04:50 2014 +0100

----------------------------------------------------------------------
 src/couch_btree.erl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/9138d07b/src/couch_btree.erl
----------------------------------------------------------------------
diff --git a/src/couch_btree.erl b/src/couch_btree.erl
index e9d1ea8..09c3d6a 100644
--- a/src/couch_btree.erl
+++ b/src/couch_btree.erl
@@ -15,7 +15,7 @@
 -export([open/2, open/3, query_modify/4, add/2, add_remove/3]).
 -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]).
--export([less/3]).
+-export([extract/2, assemble/3, less/3]).
 
 -include_lib("couch/include/couch_db.hrl").
 


[12/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Remove ets table scan on process exit

This commit adds a new ets table for doing O(1) pid-to-db-name lookup.
This is to remove the necessity for doing an ets table scan on the exit
of a monitored process.

BugzID: 19132


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/caffc58f
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/caffc58f
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/caffc58f

Branch: refs/heads/windsor-merge-300
Commit: caffc58f3cc62211294b39eda0e32b2ff230bde2
Parents: b7b20c9
Author: Benjamin Bastian <be...@gmail.com>
Authored: Tue May 28 15:03:03 2013 -0700
Committer: Robert Newson <rn...@apache.org>
Committed: Mon Aug 4 16:40:35 2014 +0100

----------------------------------------------------------------------
 src/couch_lru.erl    |  1 +
 src/couch_server.erl | 18 ++++++++++++++----
 2 files changed, 15 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/caffc58f/src/couch_lru.erl
----------------------------------------------------------------------
diff --git a/src/couch_lru.erl b/src/couch_lru.erl
index 97f27f8..ad432ec 100644
--- a/src/couch_lru.erl
+++ b/src/couch_lru.erl
@@ -47,6 +47,7 @@ close_int({Lru, DbName, Iter}, {Tree, Dict} = Cache) ->
         [#db{main_pid = Pid} = Db] = ets:lookup(couch_dbs, DbName),
         case couch_db:is_idle(Db) of true ->
             true = ets:delete(couch_dbs, DbName),
+            true = ets:delete(couch_dbs_pid_to_name, Pid),
             exit(Pid, kill),
             {gb_trees:delete(Lru, Tree), dict:erase(DbName, Dict)};
         false ->

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/caffc58f/src/couch_server.erl
----------------------------------------------------------------------
diff --git a/src/couch_server.erl b/src/couch_server.erl
index 5a73877..a10fcd0 100644
--- a/src/couch_server.erl
+++ b/src/couch_server.erl
@@ -198,6 +198,7 @@ init([]) ->
         "(\\.[0-9]{10,})?$" % but allow an optional shard timestamp at the end
     ),
     ets:new(couch_dbs, [set, protected, named_table, {keypos, #db.name}]),
+    ets:new(couch_dbs_pid_to_name, [set, protected, named_table]),
     process_flag(trap_exit, true),
     {ok, #server{root_dir=RootDir,
                 dbname_regexp=RegExp,
@@ -312,6 +313,7 @@ open_async(Server, From, DbName, Filepath, Options) ->
         fd_monitor = locked,
         options = Options
     }),
+    true = ets:insert(couch_dbs_pid_to_name, {Opener, DbName}),
     db_opened(Server, Options).
 
 handle_call(close_lru, _From, #server{lru=Lru} = Server) ->
@@ -326,8 +328,9 @@ handle_call({set_max_dbs_open, Max}, _From, Server) ->
     {reply, ok, Server#server{max_dbs_open=Max}};
 handle_call(get_server, _From, Server) ->
     {reply, {ok, Server}, Server};
-handle_call({open_result, DbName, {ok, Db}}, _From, Server) ->
+handle_call({open_result, DbName, {ok, Db}}, {FromPid, _Tag}, Server) ->
     link(Db#db.main_pid),
+    true = ets:delete(couch_dbs_pid_to_name, FromPid),
     case erase({async_open, DbName}) of undefined -> ok; T0 ->
         ?LOG_INFO("needed ~p ms to open new ~s", [timer:now_diff(os:timestamp(),T0)/1000,
             DbName])
@@ -344,6 +347,7 @@ handle_call({open_result, DbName, {ok, Db}}, _From, Server) ->
             ok
     end,
     true = ets:insert(couch_dbs, Db),
+    true = ets:insert(couch_dbs_pid_to_name, {Db#db.main_pid, DbName}),
     Lru = case couch_db:is_system_db(Db) of
         false ->
             Stat = {couchdb, open_databases},
@@ -355,12 +359,13 @@ handle_call({open_result, DbName, {ok, Db}}, _From, Server) ->
     {reply, ok, Server#server{lru = Lru}};
 handle_call({open_result, DbName, {error, eexist}}, From, Server) ->
     handle_call({open_result, DbName, file_exists}, From, Server);
-handle_call({open_result, DbName, Error}, _From, Server) ->
+handle_call({open_result, DbName, Error}, {FromPid, _Tag}, Server) ->
     % icky hack of field values - compactor_pid used to store clients
     [#db{fd=ReqType, compactor_pid=Froms}=Db] = ets:lookup(couch_dbs, DbName),
     [gen_server:reply(From, Error) || From <- Froms],
     ?LOG_INFO("open_result error ~p for ~s", [Error, DbName]),
     true = ets:delete(couch_dbs, DbName),
+    true = ets:delete(couch_dbs_pid_to_name, FromPid),
     NewServer = case ReqType of
         {create, DbName, Filepath, Options, CrFrom} ->
             open_async(Server, CrFrom, DbName, Filepath, Options);
@@ -435,11 +440,13 @@ handle_call({delete, DbName, Options}, _From, Server) ->
         [#db{main_pid=Pid, compactor_pid=Froms} = Db] when is_list(Froms) ->
             % icky hack of field values - compactor_pid used to store clients
             true = ets:delete(couch_dbs, DbName),
+            true = ets:delete(couch_dbs_pid_to_name, Pid),
             exit(Pid, kill),
             [gen_server:reply(F, not_found) || F <- Froms],
             db_closed(Server, Db#db.options);
         [#db{main_pid=Pid} = Db] ->
             true = ets:delete(couch_dbs, DbName),
+            true = ets:delete(couch_dbs_pid_to_name, Pid),
             exit(Pid, kill),
             db_closed(Server, Db#db.options)
         end,
@@ -485,8 +492,10 @@ code_change(_, State, _) ->
 handle_info({'EXIT', _Pid, config_change}, Server) ->
     {stop, config_change, Server};
 handle_info({'EXIT', Pid, Reason}, Server) ->
-    case ets:match_object(couch_dbs, #db{main_pid=Pid, _='_'}) of
-    [#db{name = DbName, compactor_pid=Froms} = Db] ->
+    case ets:lookup(couch_dbs_pid_to_name, Pid) of
+    [DbName] ->
+        [#db{compactor_pid=Froms}=Db] =
+            ets:match_object(couch_dbs, #db{name=DbName, _='_'}),
         if Reason /= snappy_nif_not_loaded -> ok; true ->
             Msg = io_lib:format("To open the database `~s`, Apache CouchDB "
                 "must be built with Erlang OTP R13B04 or higher.", [DbName]),
@@ -500,6 +509,7 @@ handle_info({'EXIT', Pid, Reason}, Server) ->
             ok
         end,
         true = ets:delete(couch_dbs, DbName),
+        true = ets:delete(couch_dbs_pid_to_name, Pid),
         {noreply, db_closed(Server, Db#db.options)};
     [] ->
         {noreply, Server}


[42/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Avoid infinite loops in remove_dup_epochs

Oops.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/5686e732
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/5686e732
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/5686e732

Branch: refs/heads/windsor-merge-300
Commit: 5686e7327c1fa1a039896d0e34567c0ee8125e41
Parents: a9f5c83
Author: Adam Kocoloski <ad...@cloudant.com>
Authored: Thu Oct 24 16:51:00 2013 -0400
Committer: Robert Newson <rn...@apache.org>
Committed: Wed Aug 6 12:20:08 2014 +0100

----------------------------------------------------------------------
 src/couch_db_header.erl | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/5686e732/src/couch_db_header.erl
----------------------------------------------------------------------
diff --git a/src/couch_db_header.erl b/src/couch_db_header.erl
index b7c93d9..03f95ea 100644
--- a/src/couch_db_header.erl
+++ b/src/couch_db_header.erl
@@ -263,7 +263,7 @@ remove_dup_epochs([_]=Epochs) ->
     Epochs;
 remove_dup_epochs([{N1, S}, {_N2, S}]) ->
     % Seqs match, keep the most recent owner
-    remove_dup_epochs([{N1, S}]);
+    [{N1, S}];
 remove_dup_epochs([_, _]=Epochs) ->
     % Seqs don't match.
     Epochs;
@@ -272,7 +272,7 @@ remove_dup_epochs([{N1, S}, {_N2, S} | Rest]) ->
     remove_dup_epochs([{N1, S} | Rest]);
 remove_dup_epochs([{N1, S1}, {N2, S2} | Rest]) ->
     % Seqs don't match, recurse to check others
-    remove_dup_epochs([{N1, S1} | remove_dup_epochs([{N2, S2} | Rest])]).
+    [{N1, S1} | remove_dup_epochs([{N2, S2} | Rest])].
 
 
 upgrade_compacted_seq(#db_header{}=Header) ->


[27/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Fix typo in auth cache event listener config


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/f1e1638e
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/f1e1638e
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/f1e1638e

Branch: refs/heads/windsor-merge-300
Commit: f1e1638ebd713c1b630034dfe03e6de6da1bd83c
Parents: d34d796
Author: Paul J. Davis <pa...@gmail.com>
Authored: Fri Jun 14 18:47:02 2013 -0500
Committer: Robert Newson <rn...@apache.org>
Committed: Tue Aug 5 16:19:11 2014 +0100

----------------------------------------------------------------------
 src/couch_auth_cache.erl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/f1e1638e/src/couch_auth_cache.erl
----------------------------------------------------------------------
diff --git a/src/couch_auth_cache.erl b/src/couch_auth_cache.erl
index 72e9278..82fb095 100644
--- a/src/couch_auth_cache.erl
+++ b/src/couch_auth_cache.erl
@@ -213,7 +213,7 @@ handle_info({'EXIT', LPid, _Reason}, #state{event_listener=LPid}=State) ->
 handle_info(restart_event_listener, State) ->
     [{auth_db_name, AuthDbName}] = ets:lookup(?STATE, auth_db_name),
     {ok, NewListener} = couch_event:link_listener(
-            ?MODULE, handle_db_event, nil, [{dbanme, AuthDbName}]
+            ?MODULE, handle_db_event, nil, [{dbname, AuthDbName}]
         ),
     {noreply, State#state{event_listener=NewListener}};
 handle_info({gen_event_EXIT, {config_listener, ?MODULE}, _Reason}, State) ->


[05/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Add couch_event dependency


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/c4ce4906
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/c4ce4906
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/c4ce4906

Branch: refs/heads/windsor-merge-300
Commit: c4ce4906797006337a691af956d358a05c38d5a2
Parents: ff3b88b
Author: Robert Newson <rn...@apache.org>
Authored: Tue Jul 22 17:12:58 2014 +0100
Committer: Robert Newson <rn...@apache.org>
Committed: Mon Aug 4 14:16:55 2014 +0100

----------------------------------------------------------------------
 src/couch.app.src | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/c4ce4906/src/couch.app.src
----------------------------------------------------------------------
diff --git a/src/couch.app.src b/src/couch.app.src
index 48b089b..8007278 100644
--- a/src/couch.app.src
+++ b/src/couch.app.src
@@ -29,5 +29,5 @@
     ]},
     {mod, {couch_app, []}},
     {applications, [kernel, stdlib, crypto, sasl, inets, oauth, ibrowse,
-        mochiweb, ssl, couch_log]}
+        mochiweb, ssl, couch_log, couch_event]}
 ]}.


[08/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Update couch_changes to use new couch_event app


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/c24e6d68
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/c24e6d68
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/c24e6d68

Branch: refs/heads/windsor-merge-300
Commit: c24e6d6805d638879832fcf338bb42f2f201d104
Parents: be7b1a4
Author: Paul J. Davis <pa...@gmail.com>
Authored: Tue Apr 23 16:00:01 2013 -0500
Committer: Robert Newson <rn...@apache.org>
Committed: Mon Aug 4 15:19:30 2014 +0100

----------------------------------------------------------------------
 src/couch_changes.erl | 23 ++++++++++++++---------
 1 file changed, 14 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/c24e6d68/src/couch_changes.erl
----------------------------------------------------------------------
diff --git a/src/couch_changes.erl b/src/couch_changes.erl
index fd14f3d..637a6ba 100644
--- a/src/couch_changes.erl
+++ b/src/couch_changes.erl
@@ -19,7 +19,8 @@
     wait_db_updated/3,
     get_rest_db_updated/1,
     configure_filter/4,
-    filter/3
+    filter/3,
+    handle_db_event/3
 ]).
 
 -export([changes_enumerator/2]).
@@ -77,13 +78,8 @@ handle_changes(Args1, Req, Db0) ->
     true ->
         fun(CallbackAcc) ->
             {Callback, UserAcc} = get_callback_acc(CallbackAcc),
-            Self = self(),
-            {ok, Notify} = couch_db_update_notifier:start_link(
-                fun({updated, DbName}) when Db0#db.name == DbName ->
-                    Self ! db_updated;
-                (_) ->
-                    ok
-                end
+            {ok, Listener} = couch_event:link_listener(
+                 ?MODULE, handle_db_event, self(), [{dbname, Db0#db.name}]
             ),
             {Db, StartSeq} = Start(),
             UserAcc2 = start_sending_changes(Callback, UserAcc, Feed),
@@ -96,7 +92,7 @@ handle_changes(Args1, Req, Db0) ->
                     Acc0,
                     true)
             after
-                couch_db_update_notifier:stop(Notify),
+                couch_event:stop_listener(Listener),
                 get_rest_db_updated(ok) % clean out any remaining update messages
             end
         end;
@@ -117,6 +113,15 @@ handle_changes(Args1, Req, Db0) ->
         end
     end.
 
+
+handle_db_event(_DbName, updated, Parent) ->
+    Parent ! db_updated,
+    {ok, Parent};
+
+handle_db_event(_DbName, _Event, Parent) ->
+    {ok, Parent}.
+
+
 get_callback_acc({Callback, _UserAcc} = Pair) when is_function(Callback, 3) ->
     Pair;
 get_callback_acc(Callback) when is_function(Callback, 2) ->


[44/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Remove old code_change, set module version to 1


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/dfa15da2
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/dfa15da2
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/dfa15da2

Branch: refs/heads/windsor-merge-300
Commit: dfa15da29bbc7c8aff80bd6a1ad85ee6e47bc083
Parents: c31b27a
Author: Robert Newson <ro...@cloudant.com>
Authored: Fri Nov 22 16:30:53 2013 +0000
Committer: Robert Newson <rn...@apache.org>
Committed: Wed Aug 6 12:48:47 2014 +0100

----------------------------------------------------------------------
 src/couch_auth_cache.erl       | 1 +
 src/couch_db_updater.erl       | 1 +
 src/couch_drv.erl              | 1 +
 src/couch_event_sup.erl        | 1 +
 src/couch_external_manager.erl | 1 +
 src/couch_external_server.erl  | 1 +
 src/couch_file.erl             | 1 +
 src/couch_native_process.erl   | 1 +
 src/couch_os_process.erl       | 1 +
 src/couch_proc_manager.erl     | 7 +------
 src/couch_server.erl           | 3 ++-
 src/couch_stats_collector.erl  | 5 ++---
 src/couch_stream.erl           | 1 +
 src/couch_task_status.erl      | 1 +
 src/couch_uuids.erl            | 1 +
 src/couch_work_queue.erl       | 1 +
 16 files changed, 18 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/dfa15da2/src/couch_auth_cache.erl
----------------------------------------------------------------------
diff --git a/src/couch_auth_cache.erl b/src/couch_auth_cache.erl
index 82fb095..d53cff0 100644
--- a/src/couch_auth_cache.erl
+++ b/src/couch_auth_cache.erl
@@ -12,6 +12,7 @@
 
 -module(couch_auth_cache).
 -behaviour(gen_server).
+-vsn(1).
 -behaviour(config_listener).
 
 % public API

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/dfa15da2/src/couch_db_updater.erl
----------------------------------------------------------------------
diff --git a/src/couch_db_updater.erl b/src/couch_db_updater.erl
index 6979256..3ff5963 100644
--- a/src/couch_db_updater.erl
+++ b/src/couch_db_updater.erl
@@ -12,6 +12,7 @@
 
 -module(couch_db_updater).
 -behaviour(gen_server).
+-vsn(1).
 
 -export([btree_by_id_split/1, btree_by_id_join/2, btree_by_id_reduce/2]).
 -export([btree_by_seq_split/1, btree_by_seq_join/2, btree_by_seq_reduce/2]).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/dfa15da2/src/couch_drv.erl
----------------------------------------------------------------------
diff --git a/src/couch_drv.erl b/src/couch_drv.erl
index 7fe119a..4424114 100644
--- a/src/couch_drv.erl
+++ b/src/couch_drv.erl
@@ -12,6 +12,7 @@
 
 -module(couch_drv).
 -behaviour(gen_server).
+-vsn(1).
 -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
     code_change/3]).
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/dfa15da2/src/couch_event_sup.erl
----------------------------------------------------------------------
diff --git a/src/couch_event_sup.erl b/src/couch_event_sup.erl
index e9e7214..39e728c 100644
--- a/src/couch_event_sup.erl
+++ b/src/couch_event_sup.erl
@@ -16,6 +16,7 @@
 
 -module(couch_event_sup).
 -behaviour(gen_server).
+-vsn(1).
 
 -include_lib("couch/include/couch_db.hrl").
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/dfa15da2/src/couch_external_manager.erl
----------------------------------------------------------------------
diff --git a/src/couch_external_manager.erl b/src/couch_external_manager.erl
index a8cad63..d2fa1ea 100644
--- a/src/couch_external_manager.erl
+++ b/src/couch_external_manager.erl
@@ -12,6 +12,7 @@
 
 -module(couch_external_manager).
 -behaviour(gen_server).
+-vsn(1).
 -behaviour(config_listener).
 
 -export([start_link/0, execute/2]).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/dfa15da2/src/couch_external_server.erl
----------------------------------------------------------------------
diff --git a/src/couch_external_server.erl b/src/couch_external_server.erl
index 123e2e1..40d9113 100644
--- a/src/couch_external_server.erl
+++ b/src/couch_external_server.erl
@@ -12,6 +12,7 @@
 
 -module(couch_external_server).
 -behaviour(gen_server).
+-vsn(1).
 -behaviour(config_listener).
 
 -export([start_link/2, stop/1, execute/2]).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/dfa15da2/src/couch_file.erl
----------------------------------------------------------------------
diff --git a/src/couch_file.erl b/src/couch_file.erl
index a5bcbd1..4fda603 100644
--- a/src/couch_file.erl
+++ b/src/couch_file.erl
@@ -12,6 +12,7 @@
 
 -module(couch_file).
 -behaviour(gen_server).
+-vsn(1).
 
 -include_lib("couch/include/couch_db.hrl").
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/dfa15da2/src/couch_native_process.erl
----------------------------------------------------------------------
diff --git a/src/couch_native_process.erl b/src/couch_native_process.erl
index e59a223..b09716e 100644
--- a/src/couch_native_process.erl
+++ b/src/couch_native_process.erl
@@ -39,6 +39,7 @@
 % to help simplify your view code.
 -module(couch_native_process).
 -behaviour(gen_server).
+-vsn(1).
 
 -export([start_link/0,init/1,terminate/2,handle_call/3,handle_cast/2,code_change/3,
          handle_info/2]).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/dfa15da2/src/couch_os_process.erl
----------------------------------------------------------------------
diff --git a/src/couch_os_process.erl b/src/couch_os_process.erl
index f4d65e9..676d540 100644
--- a/src/couch_os_process.erl
+++ b/src/couch_os_process.erl
@@ -12,6 +12,7 @@
 
 -module(couch_os_process).
 -behaviour(gen_server).
+-vsn(1).
 
 -export([start_link/1, start_link/2, start_link/3, stop/1]).
 -export([set_timeout/2, prompt/2, killer/1]).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/dfa15da2/src/couch_proc_manager.erl
----------------------------------------------------------------------
diff --git a/src/couch_proc_manager.erl b/src/couch_proc_manager.erl
index 2744c88..18c82e6 100644
--- a/src/couch_proc_manager.erl
+++ b/src/couch_proc_manager.erl
@@ -13,6 +13,7 @@
 -module(couch_proc_manager).
 -behaviour(gen_server).
 -behaviour(config_listener).
+-vsn(1).
 
 -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, 
     code_change/3]).
@@ -232,12 +233,6 @@ terminate(_Reason, #state{tab=Tab}) ->
     ok.
 
 code_change(_OldVsn, #state{}=State, _Extra) ->
-    {ok, State};
-code_change(_OldVsn, {state, Tab}, _Extra) ->
-    State = #state{tab = Tab, threshold_ts = {0,0,0}},
-    ProcInts = lists:map(fun import_proc/1, ets:tab2list(Tab)),
-    ets:delete_all_objects(Tab),
-    ets:insert(Tab, ProcInts),
     {ok, State}.
 
 handle_config_change("query_server_config", _, _, _, _) ->

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/dfa15da2/src/couch_server.erl
----------------------------------------------------------------------
diff --git a/src/couch_server.erl b/src/couch_server.erl
index fd4691c..28dfc79 100644
--- a/src/couch_server.erl
+++ b/src/couch_server.erl
@@ -13,6 +13,7 @@
 -module(couch_server).
 -behaviour(gen_server).
 -behaviour(config_listener).
+-vsn(1).
 
 -export([open/2,create/2,delete/2,get_version/0,get_version/1,get_uuid/0]).
 -export([all_databases/0, all_databases/2]).
@@ -507,7 +508,7 @@ handle_cast({update_lru, _DbName}, Server) ->
 handle_cast(Msg, Server) ->
     {stop, {unknown_cast_message, Msg}, Server}.
 
-code_change(_, State, _) ->
+code_change(_OldVsn, #server{}=State, _Extra) ->
     {ok, State}.
 
 handle_info({'EXIT', _Pid, config_change}, Server) ->

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/dfa15da2/src/couch_stats_collector.erl
----------------------------------------------------------------------
diff --git a/src/couch_stats_collector.erl b/src/couch_stats_collector.erl
index 2ed647c..5bf4864 100644
--- a/src/couch_stats_collector.erl
+++ b/src/couch_stats_collector.erl
@@ -17,6 +17,7 @@
 -module(couch_stats_collector).
 
 -behaviour(gen_server).
+-vsn(1).
 
 -export([start/0, stop/0]).
 -export([all/0, all/1, get/1, increment/1, decrement/1, record/2, clear/1]).
@@ -109,9 +110,7 @@ handle_info({'DOWN', Ref, _, _, _}, State) ->
     couch_stats_collector:decrement(Stat),
     {noreply, dict:erase(Ref, State)}.
 
-code_change(_OldVersion, State, _Extra) when is_list(State) ->
-    {ok, dict:from_list(State)};
-code_change(_OldVersion, State, _Extra) ->
+code_change(_, State, _Extra) ->
     {ok, State}.
 
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/dfa15da2/src/couch_stream.erl
----------------------------------------------------------------------
diff --git a/src/couch_stream.erl b/src/couch_stream.erl
index 6e7213b..e71542c 100644
--- a/src/couch_stream.erl
+++ b/src/couch_stream.erl
@@ -12,6 +12,7 @@
 
 -module(couch_stream).
 -behaviour(gen_server).
+-vsn(1).
 
 % public API
 -export([open/1, open/2, close/1]).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/dfa15da2/src/couch_task_status.erl
----------------------------------------------------------------------
diff --git a/src/couch_task_status.erl b/src/couch_task_status.erl
index 8dc91b4..8a8dbf2 100644
--- a/src/couch_task_status.erl
+++ b/src/couch_task_status.erl
@@ -12,6 +12,7 @@
 
 -module(couch_task_status).
 -behaviour(gen_server).
+-vsn(1).
 
 % This module is used to track the status of long running tasks.
 % Long running tasks register themselves, via a call to add_task/1, and then

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/dfa15da2/src/couch_uuids.erl
----------------------------------------------------------------------
diff --git a/src/couch_uuids.erl b/src/couch_uuids.erl
index e891abb..2cee58b 100644
--- a/src/couch_uuids.erl
+++ b/src/couch_uuids.erl
@@ -13,6 +13,7 @@
 -include_lib("couch/include/couch_db.hrl").
 
 -behaviour(gen_server).
+-vsn(1).
 -behaviour(config_listener).
 
 -export([start/0, stop/0]).

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/dfa15da2/src/couch_work_queue.erl
----------------------------------------------------------------------
diff --git a/src/couch_work_queue.erl b/src/couch_work_queue.erl
index ea871e2..5d747de 100644
--- a/src/couch_work_queue.erl
+++ b/src/couch_work_queue.erl
@@ -12,6 +12,7 @@
 
 -module(couch_work_queue).
 -behaviour(gen_server).
+-vsn(1).
 
 -include_lib("couch/include/couch_db.hrl").
 


[25/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Fix match_spec's in couch_proc_manager

When using a list or tuple in a match_spec condition clause we have
to wrap them in a tuple or it throws a badarg error.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/f77e6946
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/f77e6946
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/f77e6946

Branch: refs/heads/windsor-merge-300
Commit: f77e6946afdd547c25c4cfbcac3d794b663174bd
Parents: 2f3a1fd
Author: Paul J. Davis <pa...@gmail.com>
Authored: Fri Jun 14 14:51:33 2013 -0500
Committer: Robert Newson <rn...@apache.org>
Committed: Tue Aug 5 16:10:36 2014 +0100

----------------------------------------------------------------------
 src/couch_proc_manager.erl | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/f77e6946/src/couch_proc_manager.erl
----------------------------------------------------------------------
diff --git a/src/couch_proc_manager.erl b/src/couch_proc_manager.erl
index 252de2c..6a64fa7 100644
--- a/src/couch_proc_manager.erl
+++ b/src/couch_proc_manager.erl
@@ -92,7 +92,7 @@ handle_call(get_proc_count, _From, State) ->
 
 handle_call(get_stale_proc_count, _From, State) ->
     #state{tab = Tab, threshold_ts = T0} = State,
-    MatchSpec = [{#proc_int{t0='$1', _='_'}, [{'<', '$1', T0}], [true]}],
+    MatchSpec = [{#proc_int{t0='$1', _='_'}, [{'<', '$1', {T0}}], [true]}],
     {reply, ets:select_count(Tab, MatchSpec), State};
 
 handle_call({get_proc, #doc{body={Props}}=DDoc, DDocKey}, From, State) ->
@@ -147,7 +147,7 @@ handle_call(bump_threshold_ts, _From, #state{tab = Tab} = State) ->
 handle_call(terminate_stale_procs, _From, State) ->
     #state{tab = Tab, threshold_ts = T0} = State,
     MatchHead = #proc_int{pid = '$1', t0 = '$2', _ = '_'},
-    MatchSpec = [{MatchHead, [{'<', '$2', T0}], ['$1']}],
+    MatchSpec = [{MatchHead, [{'<', '$2', {T0}}], ['$1']}],
     lists:foreach(fun(P) -> remove_proc(Tab,P) end, ets:select(Tab, MatchSpec)),
     {reply, ok, State};
 


[10/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Remove old couch_db_update_notifier code


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/a2addb5f
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/a2addb5f
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/a2addb5f

Branch: refs/heads/windsor-merge-300
Commit: a2addb5fc8eabbe29ca5fad22e1fa8d7c1e70ecf
Parents: c24e6d6
Author: Paul J. Davis <pa...@gmail.com>
Authored: Wed Apr 24 12:59:59 2013 -0500
Committer: Robert Newson <rn...@apache.org>
Committed: Mon Aug 4 15:24:37 2014 +0100

----------------------------------------------------------------------
 src/couch_db_update_notifier.erl     | 82 -------------------------------
 src/couch_db_update_notifier_sup.erl | 68 -------------------------
 src/couch_os_process.erl             |  2 +-
 src/couch_primary_sup.erl            |  6 ---
 src/couch_secondary_sup.erl          |  7 ---
 5 files changed, 1 insertion(+), 164 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a2addb5f/src/couch_db_update_notifier.erl
----------------------------------------------------------------------
diff --git a/src/couch_db_update_notifier.erl b/src/couch_db_update_notifier.erl
deleted file mode 100644
index 3958917..0000000
--- a/src/couch_db_update_notifier.erl
+++ /dev/null
@@ -1,82 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
-%
-% This causes an OS process to spawned and it is notified every time a database
-% is updated.
-%
-% The notifications are in the form of a the database name sent as a line of
-% text to the OS processes stdout.
-%
-
--module(couch_db_update_notifier).
-
--behaviour(gen_event).
-
--export([start_link/1, notify/1]).
--export([init/1, terminate/2, handle_event/2, handle_call/2, handle_info/2, code_change/3,stop/1]).
-
--include_lib("couch/include/couch_db.hrl").
-
-start_link(Exec) ->
-    couch_event_sup:start_link(couch_db_update, {couch_db_update_notifier, make_ref()}, Exec).
-
-notify(Event) ->
-    gen_event:notify(couch_db_update, Event).
-
-stop(Pid) ->
-    couch_event_sup:stop(Pid).
-
-init(Exec) when is_list(Exec) -> % an exe
-    couch_os_process:start_link(Exec, []);
-init(Else) ->
-    {ok, Else}.
-
-terminate(_Reason, Pid) when is_pid(Pid) ->
-    couch_os_process:stop(Pid),
-    ok;
-terminate(_Reason, _State) ->
-    ok.
-
-handle_event(Event, Fun) when is_function(Fun, 1) ->
-    Fun(Event),
-    {ok, Fun};
-handle_event(Event, {Fun, FunAcc}) ->
-    FunAcc2 = Fun(Event, FunAcc),
-    {ok, {Fun, FunAcc2}};
-handle_event({EventType, EventDesc}, Pid) ->
-    Obj = encode_event(EventType, EventDesc),
-    ok = couch_os_process:send(Pid, Obj),
-    {ok, Pid}.
-
-handle_call(_Request, State) ->
-    {reply, ok, State}.
-
-handle_info({'EXIT', Pid, Reason}, Pid) ->
-    ?LOG_ERROR("Update notification process ~p died: ~p", [Pid, Reason]),
-    remove_handler;
-handle_info({'EXIT', _, _}, Pid) ->
-    %% the db_update event manager traps exits and forwards this message to all
-    %% its handlers. Just ignore as it wasn't our os_process that exited.
-    {ok, Pid}.
-
-code_change(_OldVsn, State, _Extra) ->
-    {ok, State}.
-
-encode_event(EventType, EventDesc) when is_atom(EventType) ->
-    encode_event(atom_to_list(EventType), EventDesc);
-encode_event(EventType, EventDesc) when is_list(EventType) ->
-    encode_event(?l2b(EventType), EventDesc);
-encode_event(EventType, {DbName, DocId}) ->
-    {[{type, EventType}, {db, DbName}, {id, DocId}]};
-encode_event(EventType, DbName) ->
-    {[{type, EventType}, {db, DbName}]}.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a2addb5f/src/couch_db_update_notifier_sup.erl
----------------------------------------------------------------------
diff --git a/src/couch_db_update_notifier_sup.erl b/src/couch_db_update_notifier_sup.erl
deleted file mode 100644
index 9eb943a..0000000
--- a/src/couch_db_update_notifier_sup.erl
+++ /dev/null
@@ -1,68 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
-%
-% This causes an OS process to spawned and it is notified every time a database
-% is updated.
-%
-% The notifications are in the form of a the database name sent as a line of
-% text to the OS processes stdout.
-%
-
--module(couch_db_update_notifier_sup).
-
--behaviour(supervisor).
--behaviour(config_listener).
-
--export([start_link/0, init/1]).
-
-% config_listener api
--export([handle_config_change/5]).
-
-
-start_link() ->
-    supervisor:start_link({local, couch_db_update_notifier_sup},
-        couch_db_update_notifier_sup, []).
-
-init([]) ->
-    ok = config:listen_for_changes(?MODULE, nil),
-
-    UpdateNotifierExes = config:get("update_notification"),
-
-    {ok,
-        {{one_for_one, 10, 3600},
-            lists:map(fun({Name, UpdateNotifierExe}) ->
-                {Name,
-                {couch_db_update_notifier, start_link, [UpdateNotifierExe]},
-                    permanent,
-                    1000,
-                    supervisor,
-                    [couch_db_update_notifier]}
-                end, UpdateNotifierExes)}}.
-
-%% @doc when update_notification configuration changes, terminate the process
-%%      for that notifier and start a new one with the updated config
-handle_config_change("update_notification", Id, Exe, _, _) ->
-    ChildSpec = {
-        Id,
-        {couch_db_update_notifier, start_link, [Exe]},
-        permanent,
-        1000,
-        supervisor,
-        [couch_db_update_notifier]
-    },
-    supervisor:terminate_child(couch_db_update_notifier_sup, Id),
-    supervisor:delete_child(couch_db_update_notifier_sup, Id),
-    supervisor:start_child(couch_db_update_notifier_sup, ChildSpec),
-    {ok, nil};
-handle_config_change(_, _, _, _, _) ->
-    {ok, nil}.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a2addb5f/src/couch_os_process.erl
----------------------------------------------------------------------
diff --git a/src/couch_os_process.erl b/src/couch_os_process.erl
index 6756dd3..f4d65e9 100644
--- a/src/couch_os_process.erl
+++ b/src/couch_os_process.erl
@@ -45,7 +45,7 @@ stop(Pid) ->
 set_timeout(Pid, TimeOut) when is_integer(TimeOut) ->
     ok = gen_server:call(Pid, {set_timeout, TimeOut}, infinity).
 
-% Used by couch_db_update_notifier.erl
+% Used by couch_event_os_process.erl
 send(Pid, Data) ->
     gen_server:cast(Pid, {send, Data}).
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a2addb5f/src/couch_primary_sup.erl
----------------------------------------------------------------------
diff --git a/src/couch_primary_sup.erl b/src/couch_primary_sup.erl
index 3ce8827..cbd576d 100644
--- a/src/couch_primary_sup.erl
+++ b/src/couch_primary_sup.erl
@@ -37,12 +37,6 @@ init([]) ->
             brutal_kill,
             worker,
             [couch_server]},
-        {couch_db_update_event,
-            {gen_event, start_link, [{local, couch_db_update}]},
-            permanent,
-            brutal_kill,
-            worker,
-            dynamic},
         {couch_replication_event,
             {gen_event, start_link, [{local, couch_replication}]},
             permanent,

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a2addb5f/src/couch_secondary_sup.erl
----------------------------------------------------------------------
diff --git a/src/couch_secondary_sup.erl b/src/couch_secondary_sup.erl
index 91e87cb..09e77b7 100644
--- a/src/couch_secondary_sup.erl
+++ b/src/couch_secondary_sup.erl
@@ -19,13 +19,6 @@ start_link() ->
 
 init([]) ->
     SecondarySupervisors = [
-        {couch_db_update_notifier_sup,
-            {couch_db_update_notifier_sup, start_link, []},
-            permanent,
-            infinity,
-            supervisor,
-            [couch_db_update_notifier_sup]},
-
         {couch_plugin_event,
             {gen_event, start_link, [{local, couch_plugin}]},
             permanent,


[13/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Add backwards compatible key_group_level option

This adds a new btree reduce fold option called `key_group_level` that
can have the value `exact`, `0`, or `N` where `N` is any positive
integer. The reason for adding this is so that calling code can pass a
group level option instead of a function for key grouping. The important
point is that the new option uses the btree's defined `less` operator to
test equality which means that ICU collation is used for key grouping on
most views.

BugzId: 19776


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/1d6c18e5
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/1d6c18e5
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/1d6c18e5

Branch: refs/heads/windsor-merge-300
Commit: 1d6c18e54478765ba4cda4379f0504a441bf5c9e
Parents: caffc58
Author: Paul J. Davis <pa...@gmail.com>
Authored: Wed May 29 13:19:32 2013 -0500
Committer: Robert Newson <rn...@apache.org>
Committed: Mon Aug 4 16:40:39 2014 +0100

----------------------------------------------------------------------
 src/couch_btree.erl | 58 +++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 57 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/1d6c18e5/src/couch_btree.erl
----------------------------------------------------------------------
diff --git a/src/couch_btree.erl b/src/couch_btree.erl
index 9caceb8..e9d1ea8 100644
--- a/src/couch_btree.erl
+++ b/src/couch_btree.erl
@@ -73,7 +73,7 @@ fold_reduce(#btree{root=Root}=Bt, Fun, Acc, Options) ->
     Dir = couch_util:get_value(dir, Options, fwd),
     StartKey = couch_util:get_value(start_key, Options),
     InEndRangeFun = make_key_in_end_range_function(Bt, Dir, Options),
-    KeyGroupFun = couch_util:get_value(key_group_fun, Options, fun(_,_) -> true end),
+    KeyGroupFun = get_group_fun(Bt, Options),
     try
         {ok, Acc2, GroupedRedsAcc2, GroupedKVsAcc2, GroupedKey2} =
             reduce_stream_node(Bt, Dir, Root, StartKey, InEndRangeFun, undefined, [], [],
@@ -103,6 +103,62 @@ size(#btree{root = {_P, _Red}}) ->
 size(#btree{root = {_P, _Red, Size}}) ->
     Size.
 
+get_group_fun(Bt, Options) ->
+    case couch_util:get_value(key_group_level, Options) of
+        exact ->
+            make_group_fun(Bt, exact);
+        0 ->
+            fun(_, _) -> true end;
+        N when is_integer(N), N > 0 ->
+            make_group_fun(Bt, N);
+        undefined ->
+            couch_util:get_value(key_group_fun, Options, fun(_,_) -> true end)
+    end.
+
+make_group_fun(Bt, exact) ->
+    fun({Key1, _}, {Key2, _}) ->
+        case less(Bt, {Key1, nil}, {Key2, nil}) of
+            false ->
+                case less(Bt, {Key2, nil}, {Key1, nil}) of
+                    false ->
+                        true;
+                    _ ->
+                        false
+                end;
+            _ ->
+                false
+        end
+    end;
+make_group_fun(Bt, GroupLevel) when is_integer(GroupLevel), GroupLevel > 0 ->
+    fun
+        ({[_|_] = Key1, _}, {[_|_] = Key2, _}) ->
+            SL1 = lists:sublist(Key1, GroupLevel),
+            SL2 = lists:sublist(Key2, GroupLevel),
+            case less(Bt, {SL1, nil}, {SL2, nil}) of
+                false ->
+                    case less(Bt, {SL2, nil}, {SL1, nil}) of
+                        false ->
+                            true;
+                        _ ->
+                            false
+                    end;
+                _ ->
+                    false
+            end;
+        ({Key1, _}, {Key2, _}) ->
+            case less(Bt, {Key1, nil}, {Key2, nil}) of
+                false ->
+                    case less(Bt, {Key2, nil}, {Key1, nil}) of
+                        false ->
+                            true;
+                        _ ->
+                            false
+                    end;
+                _ ->
+                    false
+            end
+    end.
+
 % wraps a 2 arity function with the proper 3 arity function
 convert_fun_arity(Fun) when is_function(Fun, 2) ->
     fun


[31/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Set update_lru_on_read in couch_server:init/1


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/9f16b07f
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/9f16b07f
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/9f16b07f

Branch: refs/heads/windsor-merge-300
Commit: 9f16b07f0ee85a2b7d670009d56619f0455c21b8
Parents: 7070a15
Author: Robert Newson <ro...@cloudant.com>
Authored: Sat Jul 20 00:19:39 2013 +0100
Committer: Robert Newson <rn...@apache.org>
Committed: Tue Aug 5 17:00:53 2014 +0100

----------------------------------------------------------------------
 src/couch_server.erl | 3 +++
 1 file changed, 3 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/9f16b07f/src/couch_server.erl
----------------------------------------------------------------------
diff --git a/src/couch_server.erl b/src/couch_server.erl
index 2199ce9..e741720 100644
--- a/src/couch_server.erl
+++ b/src/couch_server.erl
@@ -191,6 +191,8 @@ init([]) ->
     RootDir = config:get("couchdb", "database_dir", "."),
     MaxDbsOpen = list_to_integer(
             config:get("couchdb", "max_dbs_open", integer_to_list(?MAX_DBS_OPEN))),
+    UpdateLruOnRead =
+        config:get("couchdb", "update_lru_on_read", "true") =:= "true",
     ok = config:listen_for_changes(?MODULE, nil),
     ok = couch_file:init_delete_dir(RootDir),
     hash_admin_passwords(),
@@ -204,6 +206,7 @@ init([]) ->
     {ok, #server{root_dir=RootDir,
                 dbname_regexp=RegExp,
                 max_dbs_open=MaxDbsOpen,
+                update_lru_on_read=UpdateLruOnRead,
                 start_time=couch_util:rfc1123_date()}}.
 
 terminate(_Reason, _Srv) ->


[11/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Add uuid and epochs to couchdb database header

All .couch files now have a uuid value that is preserved during
compaction. This value can be used to distinguish one .couch file from
another. In addition, a list of {node(), non_neg_integer()} tuples is
recorded showing the highest update sequence used since the last time
that the value of node() changed. This allows us to know if the
database file has moved and received writes since the last time we saw it.

BugzID: 19133

Conflicts:
	include/couch_db.hrl
	src/couch_db.erl
	src/couch_db_updater.erl


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/b7b20c90
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/b7b20c90
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/b7b20c90

Branch: refs/heads/windsor-merge-300
Commit: b7b20c906e6041816ad1caf66e747c5be4882d0c
Parents: a2addb5
Author: Robert Newson <ro...@cloudant.com>
Authored: Mon Apr 29 22:39:52 2013 +0100
Committer: Robert Newson <rn...@apache.org>
Committed: Mon Aug 4 15:32:10 2014 +0100

----------------------------------------------------------------------
 include/couch_db.hrl     |  8 ++++--
 src/couch_db.erl         |  6 +++--
 src/couch_db_updater.erl | 57 ++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 64 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/b7b20c90/include/couch_db.hrl
----------------------------------------------------------------------
diff --git a/include/couch_db.hrl b/include/couch_db.hrl
index 90cc263..5c549ec 100644
--- a/include/couch_db.hrl
+++ b/include/couch_db.hrl
@@ -151,7 +151,9 @@
      purge_seq = 0,
      purged_docs = nil,
      security_ptr = nil,
-     revs_limit = 1000
+     revs_limit = 1000,
+     uuid = nil,
+     epochs = nil
     }).
 
 -record(db,
@@ -178,7 +180,9 @@
     options = [],
     compression,
     before_doc_update = nil, % nil | fun(Doc, Db) -> NewDoc
-    after_doc_read = nil     % nil | fun(Doc, Db) -> NewDoc
+    after_doc_read = nil,    % nil | fun(Doc, Db) -> NewDoc
+    uuid = nil,
+    epochs = nil
     }).
 
 -record(view_fold_helper_funs, {

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/b7b20c90/src/couch_db.erl
----------------------------------------------------------------------
diff --git a/src/couch_db.erl b/src/couch_db.erl
index 9d480a9..aaab19b 100644
--- a/src/couch_db.erl
+++ b/src/couch_db.erl
@@ -304,7 +304,8 @@ get_db_info(Db) ->
         committed_update_seq=CommittedUpdateSeq,
         id_tree = IdBtree,
         seq_tree = SeqBtree,
-        local_tree = LocalBtree
+        local_tree = LocalBtree,
+        uuid = Uuid
     } = Db,
     {ok, Size} = couch_file:bytes(Fd),
     {ok, DbReduction} = couch_btree:full_reduce(IdBtree),
@@ -319,7 +320,8 @@ get_db_info(Db) ->
         {data_size, db_data_size(DbReduction, [SeqBtree, IdBtree, LocalBtree])},
         {instance_start_time, StartTime},
         {disk_format_version, DiskVersion},
-        {committed_update_seq, CommittedUpdateSeq}
+        {committed_update_seq, CommittedUpdateSeq},
+        {uuid, Uuid}
         ],
     {ok, InfoList}.
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/b7b20c90/src/couch_db_updater.erl
----------------------------------------------------------------------
diff --git a/src/couch_db_updater.erl b/src/couch_db_updater.erl
index 4e72dac..f6276c5 100644
--- a/src/couch_db_updater.erl
+++ b/src/couch_db_updater.erl
@@ -227,7 +227,9 @@ handle_cast({compact_done, CompactFilepath}, #db{filepath=Filepath}=Db) ->
             main_pid = self(),
             filepath = Filepath,
             instance_start_time = Db#db.instance_start_time,
-            revs_limit = Db#db.revs_limit
+            revs_limit = Db#db.revs_limit,
+            uuid = Db#db.uuid,
+            epochs = Db#db.epochs
         }),
 
         ?LOG_DEBUG("CouchDB swapping files ~s and ~s.",
@@ -522,7 +524,9 @@ init_db(DbName, Filepath, Fd, Header0, Options) ->
         options = Options,
         compression = Compression,
         before_doc_update = couch_util:get_value(before_doc_update, Options, nil),
-        after_doc_read = couch_util:get_value(after_doc_read, Options, nil)
+        after_doc_read = couch_util:get_value(after_doc_read, Options, nil),
+        uuid = initialize_uuid(Header),
+        epochs = initialize_epochs(Header)
         }.
 
 
@@ -804,7 +808,9 @@ db_to_header(Db, Header) ->
         id_tree_state = couch_btree:get_state(Db#db.id_tree),
         local_tree_state = couch_btree:get_state(Db#db.local_tree),
         security_ptr = Db#db.security_ptr,
-        revs_limit = Db#db.revs_limit}.
+        revs_limit = Db#db.revs_limit,
+        uuid = Db#db.uuid,
+        epochs = update_epochs(Db)}.
 
 commit_data(Db) ->
     commit_data(Db, false).
@@ -1273,3 +1279,48 @@ make_doc_summary(#db{compression = Comp}, {Body0, Atts0}) ->
     end,
     SummaryBin = ?term_to_bin({Body, Atts}),
     couch_file:assemble_file_chunk(SummaryBin, couch_util:md5(SummaryBin)).
+
+initialize_uuid(#db_header{uuid=nil}) ->
+    couch_uuids:random();
+initialize_uuid(#db_header{uuid=Uuid}) ->
+    Uuid.
+
+initialize_epochs(#db_header{epochs=nil}=Db) ->
+    [{node(), Db#db_header.update_seq}];
+initialize_epochs(#db_header{epochs=Epochs}) ->
+    Epochs.
+
+update_epochs(Db) ->
+    case Db#db.epochs of
+        [{Node, _} | _] when Node =:= node() ->
+            Db#db.epochs;
+        Epochs when is_list(Epochs) ->
+            %% Mark the sequence where this node took over.
+            [{node(), Db#db.update_seq} | Epochs]
+    end.
+
+-ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
+
+initialize_uuid_is_stable_test() ->
+    %% UUID was initialized.
+    ?assertMatch(<<_:32/binary>>, initialize_uuid(#db_header{})),
+    %% UUID was preserved.
+    ?assertEqual(foo, initialize_uuid(#db_header{uuid=foo})).
+
+initialize_epochs_is_stable_test() ->
+    %% Epochs were initialized.
+    ?assertMatch([{'nonode@nohost', 0}], initialize_epochs(#db_header{})),
+    %% Epochs are preserved.
+    ?assertMatch(foo, initialize_epochs(#db_header{epochs=foo})).
+
+update_epochs_test() ->
+    %% Epochs are not extended if node stays the same.
+    ?assertMatch([{'nonode@nohost', 0}],
+                 update_epochs(#db{epochs=[{'nonode@nohost', 0}]})),
+
+    %% Epochs are extended if node changes.
+    ?assertMatch([{'nonode@nohost', 1}, {foo, 0}],
+                 update_epochs(#db{update_seq=1, epochs=[{foo, 0}]})).
+
+-endif.


[37/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Remove unnecessary btree lookup

This commit removes an unnecessary btree lookup in couch_db_updater. As
far as I can tell, its reason for existence was removed in 2010, but the
btree lookup wasn't.

BugzID: 23737


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/13b4e435
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/13b4e435
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/13b4e435

Branch: refs/heads/windsor-merge-300
Commit: 13b4e4352f8b4b2d5839de7f444ad04153064094
Parents: 46607e0
Author: Benjamin Bastian <be...@gmail.com>
Authored: Thu Sep 26 15:40:40 2013 -0700
Committer: Robert Newson <rn...@apache.org>
Committed: Wed Aug 6 11:02:12 2014 +0100

----------------------------------------------------------------------
 src/couch_db_updater.erl | 40 +++++++++++++---------------------------
 1 file changed, 13 insertions(+), 27 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/13b4e435/src/couch_db_updater.erl
----------------------------------------------------------------------
diff --git a/src/couch_db_updater.erl b/src/couch_db_updater.erl
index 7c2a43a..e01df83 100644
--- a/src/couch_db_updater.erl
+++ b/src/couch_db_updater.erl
@@ -771,10 +771,8 @@ update_docs_int(Db, DocsList, NonRepDocs, MergeConflicts, FullCommit) ->
 update_local_docs(Db, []) ->
     {ok, Db};
 update_local_docs(#db{local_tree=Btree}=Db, Docs) ->
-    Ids = [Id || {_Client, #doc{id=Id}} <- Docs],
-    OldDocLookups = couch_btree:lookup(Btree, Ids),
-    BtreeEntries = lists:zipwith(
-        fun({Client, NewDoc}, _OldDocLookup) ->
+    BtreeEntries = lists:map(
+        fun({Client, NewDoc}) ->
             #doc{
                 id = Id,
                 deleted = Delete,
@@ -787,29 +785,17 @@ update_local_docs(#db{local_tree=Btree}=Db, Docs) ->
             [] ->
                 PrevRev = 0
             end,
-            %% disabled conflict checking for local docs -- APK 16 June 2010
-            % OldRev =
-            % case OldDocLookup of
-            %     {ok, {_, {OldRev0, _}}} -> OldRev0;
-            %     not_found -> 0
-            % end,
-            % case OldRev == PrevRev of
-            % true ->
-                case Delete of
-                    false ->
-                        send_result(Client, NewDoc, {ok,
-                                {0, ?l2b(integer_to_list(PrevRev + 1))}}),
-                        {update, {Id, {PrevRev + 1, Body}}};
-                    true  ->
-                        send_result(Client, NewDoc,
-                                {ok, {0, <<"0">>}}),
-                        {remove, Id}
-                end%;
-            % false ->
-            %     send_result(Client, Ref, conflict),
-            %     ignore
-            % end
-        end, Docs, OldDocLookups),
+            case Delete of
+                false ->
+                    send_result(Client, NewDoc, {ok,
+                        {0, ?l2b(integer_to_list(PrevRev + 1))}}),
+                    {update, {Id, {PrevRev + 1, Body}}};
+                true  ->
+                    send_result(Client, NewDoc,
+                        {ok, {0, <<"0">>}}),
+                    {remove, Id}
+            end
+        end, Docs),
 
     BtreeIdsRemove = [Id || {remove, Id} <- BtreeEntries],
     BtreeIdsUpdate = [{Key, Val} || {update, {Key, Val}} <- BtreeEntries],


[18/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Improve code_change and #proc{} imports

Going forward we should define default values for all fields of a
proc_int that are not shared by a proc.  This allows us to implement the
general import_proc/1 function that I've added (and used in code_change)
here.

BugzID: 19529


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/08a25ad7
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/08a25ad7
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/08a25ad7

Branch: refs/heads/windsor-merge-300
Commit: 08a25ad7d43c83169120ccce64256a5028718490
Parents: acb80ab
Author: Adam Kocoloski <ad...@cloudant.com>
Authored: Tue Jun 4 16:36:22 2013 -0400
Committer: Robert Newson <rn...@apache.org>
Committed: Tue Aug 5 12:03:15 2014 +0100

----------------------------------------------------------------------
 src/couch_proc_manager.erl | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/08a25ad7/src/couch_proc_manager.erl
----------------------------------------------------------------------
diff --git a/src/couch_proc_manager.erl b/src/couch_proc_manager.erl
index 8cb53cb..f84c06a 100644
--- a/src/couch_proc_manager.erl
+++ b/src/couch_proc_manager.erl
@@ -233,12 +233,7 @@ terminate(_Reason, #state{tab=Tab}) ->
 
 code_change(_OldVsn, {state, Tab}, _Extra) ->
     State = #state{tab = Tab, threshold_ts = {0,0,0}},
-    ProcInts = lists:map(
-        fun(#proc{} = P) ->
-            setelement(1, erlang:append_element(P, os:timestamp()), proc_int)
-        end,
-        ets:tab2list(Tab)
-    ),
+    ProcInts = lists:map(fun import_proc/1, ets:tab2list(Tab)),
     ets:delete_all_objects(Tab),
     ets:insert(Tab, ProcInts),
     {ok, State}.
@@ -433,6 +428,11 @@ export_proc(#proc_int{} = ProcInt) ->
     [_ | Data] = lists:sublist(record_info(size, proc), tuple_to_list(ProcInt)),
     list_to_tuple([proc | Data]).
 
+import_proc(#proc{} = P) ->
+    lists:foldl(fun(Idx, ProcInt) ->
+        setelement(Idx, ProcInt, element(Idx, P))
+    end, #proc_int{}, lists:seq(2, tuple_size(P))).
+
 maybe_spawn_proc(State, Client) ->
     #state{proc_counts=Counts, waiting=Waiting} = State,
     #client{lang=Lang} = Client,


[15/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Use a #proc_int record internally instead of #proc

This allows us to add more information to an OS process without
disturbing the proc manager API.

BugzID: 19529

Slightly modified to include #proc{} and #proc_int{} handling in return_proc.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/974d85e6
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/974d85e6
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/974d85e6

Branch: refs/heads/windsor-merge-300
Commit: 974d85e614900a79e7c4f8d9c6ba922acae18a8e
Parents: f2b3162
Author: Adam Kocoloski <ad...@cloudant.com>
Authored: Mon Jun 3 11:04:59 2013 -0400
Committer: Robert Newson <rn...@apache.org>
Committed: Tue Aug 5 12:02:37 2014 +0100

----------------------------------------------------------------------
 src/couch_proc_manager.erl | 77 ++++++++++++++++++++++++++++-------------
 1 file changed, 52 insertions(+), 25 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/974d85e6/src/couch_proc_manager.erl
----------------------------------------------------------------------
diff --git a/src/couch_proc_manager.erl b/src/couch_proc_manager.erl
index 9aca645..e2a4657 100644
--- a/src/couch_proc_manager.erl
+++ b/src/couch_proc_manager.erl
@@ -39,6 +39,16 @@
     ddoc_key
 }).
 
+-record(proc_int, {
+    pid,
+    lang,
+    client = nil,
+    ddoc_keys = [],
+    prompt_fun,
+    set_timeout_fun,
+    stop_fun
+}).
+
 start_link() ->
     gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
 
@@ -49,7 +59,7 @@ init([]) ->
     process_flag(trap_exit, true),
     ok = config:listen_for_changes(?MODULE, nil),
     {ok, #state{
-        tab = ets:new(procs, [ordered_set, {keypos, #proc.pid}]),
+        tab = ets:new(procs, [ordered_set, {keypos, #proc_int.pid}]),
         config = get_proc_config(),
         proc_counts = dict:new(),
         waiting = ets:new(couch_proc_manage_waiting,
@@ -67,7 +77,7 @@ handle_call({get_proc, #doc{body={Props}}=DDoc, DDocKey}, From, State) ->
     Lang = couch_util:to_binary(
             couch_util:get_value(<<"language">>, Props, <<"javascript">>)),
     IterFun = fun(Proc, Acc) ->
-        case lists:member(DDocKey, Proc#proc.ddoc_keys) of
+        case lists:member(DDocKey, Proc#proc_int.ddoc_keys) of
             true ->
                 {stop, assign_proc(State#state.tab, ClientPid, Proc)};
             false ->
@@ -98,7 +108,7 @@ handle_call({ret_proc, #proc{client=Ref, lang=Lang0} = Proc}, _From, State) ->
     Lang = couch_util:to_binary(Lang0),
     % We need to check if the process is alive here, as the client could be
     % handing us a #proc{} with a dead one.  We would have already removed the
-    % #proc{} from our own table, so the alternative is to do a lookup in the
+    % #proc_int{} from our own table, so the alternative is to do a lookup in the
     % table before the insert.  Don't know which approach is cheaper.
     {reply, true, return_proc(State, Proc#proc{lang=Lang})};
 
@@ -109,7 +119,7 @@ handle_cast({os_proc_idle, Pid}, #state{tab=Tab, proc_counts=Counts}=State0) ->
     Limit = list_to_integer(
             config:get("query_server_config", "os_process_soft_limit", "100")),
     State = case ets:lookup(Tab, Pid) of
-        [#proc{client=nil, lang=Lang}] ->
+        [#proc_int{client=nil, lang=Lang}] ->
             case dict:find(Lang, Counts) of
                 {ok, Count} when Count > Limit ->
                     ?LOG_INFO("Closing idle OS Process: ~p", [Pid]),
@@ -141,7 +151,7 @@ handle_info(shutdown, State) ->
     {stop, shutdown, State};
 
 handle_info({'EXIT', _, {ok, Proc0, {ClientPid,_} = From}}, State) ->
-    link(Proc0#proc.pid),
+    link(Proc0#proc_int.pid),
     Proc = assign_proc(State#state.tab, ClientPid, Proc0),
     gen_server:reply(From, {ok, Proc, State#state.config}),
     {noreply, State};
@@ -152,7 +162,7 @@ handle_info({'EXIT', Pid, Reason}, State) ->
     MaybeProc = ets:lookup(State#state.tab, Pid),
     ets:delete(State#state.tab, Pid),
     case MaybeProc of
-        [#proc{lang=Lang}] ->
+        [#proc_int{lang=Lang}] ->
             case get_waiting_client(Waiting, Lang) of
                 nil ->
                     {noreply, State#state{
@@ -167,10 +177,10 @@ handle_info({'EXIT', Pid, Reason}, State) ->
     end;
 
 handle_info({'DOWN', Ref, _, _, _Reason}, State0) ->
-    case ets:match_object(State0#state.tab, #proc{client=Ref, _='_'}) of
+    case ets:match_object(State0#state.tab, #proc_int{client=Ref, _='_'}) of
     [] ->
         {noreply, State0};
-    [#proc{} = Proc] ->
+    [#proc_int{} = Proc] ->
         {noreply, return_proc(State0, Proc)}
     end;
 
@@ -186,11 +196,11 @@ handle_info(_Msg, State) ->
     {noreply, State}.
 
 terminate(_Reason, #state{tab=Tab}) ->
-    ets:foldl(fun(#proc{pid=P}, _) -> couch_util:shutdown_sync(P) end, 0, Tab),
+    ets:foldl(fun(#proc_int{pid=P}, _) -> couch_util:shutdown_sync(P) end, 0, Tab),
     ok.
 
 code_change(_OldVsn, #state{tab = Tab} = State, _Extra) ->
-    NewTab = ets:new(procs, [ordered_set, {keypos, #proc.pid}]),
+    NewTab = ets:new(procs, [ordered_set, {keypos, #proc_int.pid}]),
     true = ets:insert(NewTab, ets:tab2list(Tab)),
     true = ets:delete(Tab),
     {ok, State#state{tab = NewTab}}.
@@ -217,7 +227,7 @@ find_proc(State, Client, []) ->
 iter_procs(Tab, Lang, Fun, Acc) when is_list(Lang) ->
     iter_procs(Tab, list_to_binary(Lang), Fun, Acc);
 iter_procs(Tab, Lang, Fun, Acc) ->
-    Pattern = #proc{lang=Lang, client=nil, _='_'},
+    Pattern = #proc_int{lang=Lang, client=nil, _='_'},
     MSpec = [{Pattern, [], ['$_']}],
     case ets:select_reverse(Tab, MSpec, 25) of
         '$end_of_table' ->
@@ -283,7 +293,7 @@ new_proc_int(From, Lang) when is_list(Lang) ->
     end.
 
 proc_with_ddoc(DDoc, DDocKey, Procs) ->
-    Filter = fun(#proc{ddoc_keys=Keys}) -> not lists:member(DDocKey, Keys) end,
+    Filter = fun(#proc_int{ddoc_keys=Keys}) -> not lists:member(DDocKey, Keys) end,
     case lists:dropwhile(Filter, Procs) of
     [DDocProc|_] ->
         {ok, DDocProc};
@@ -300,12 +310,13 @@ teach_any_proc(DDoc, DDocKey, [Proc|Rest]) ->
 teach_any_proc(_, _, []) ->
     {error, noproc}.
 
-teach_ddoc(DDoc, {DDocId, _Rev}=DDocKey, #proc{ddoc_keys=Keys}=Proc) ->
+teach_ddoc(DDoc, {DDocId, _Rev}=DDocKey, #proc_int{ddoc_keys=Keys}=Proc) ->
     % send ddoc over the wire
     % we only share the rev with the client we know to update code
     % but it only keeps the latest copy, per each ddoc, around.
-    true = couch_query_servers:proc_prompt(Proc, [<<"ddoc">>, <<"new">>,
-        DDocId, couch_doc:to_json_obj(DDoc, [])]),
+    true = couch_query_servers:proc_prompt(
+        export_proc(Proc),
+        [<<"ddoc">>, <<"new">>, DDocId, couch_doc:to_json_obj(DDoc, [])]),
     % we should remove any other ddocs keys for this docid
     % because the query server overwrites without the rev
     Keys2 = [{D,R} || {D,R} <- Keys, D /= DDocId],
@@ -313,7 +324,7 @@ teach_ddoc(DDoc, {DDocId, _Rev}=DDocKey, #proc{ddoc_keys=Keys}=Proc) ->
     {ok, Proc#proc{ddoc_keys=[DDocKey|Keys2]}}.
 
 make_proc(Pid, Lang, Mod) ->
-    Proc = #proc{
+    Proc = #proc_int{
         lang = Lang,
         pid = Pid,
         prompt_fun = {Mod, prompt},
@@ -323,26 +334,37 @@ make_proc(Pid, Lang, Mod) ->
     unlink(Pid),
     {ok, Proc}.
 
-assign_proc(Tab, ClientPid, #proc{client=nil}=Proc0) when is_pid(ClientPid) ->
-    Proc = Proc0#proc{client = erlang:monitor(process, ClientPid)},
+assign_proc(Tab, ClientPid, #proc_int{client=nil}=Proc0) when is_pid(ClientPid) ->
+    Proc = Proc0#proc_int{client = erlang:monitor(process, ClientPid)},
     ets:insert(Tab, Proc),
-    Proc;
-assign_proc(Tab, #client{}=Client, #proc{client=nil}=Proc) ->
+    export_proc(Proc);
+assign_proc(Tab, #client{}=Client, #proc_int{client=nil}=Proc) ->
     {Pid, _} = Client#client.from,
     assign_proc(Tab, Pid, Proc).
 
-return_proc(State, #proc{pid=Pid, lang=Lang} = Proc) ->
-    #state{tab=Tab, waiting=Waiting} = State,
+return_proc(#state{} = State, #proc{} = Proc) ->
+    case ets:lookup(State#state.tab, Proc#proc.pid) of
+        [#proc_int{}=ProcInt] ->
+            return_proc(State, ProcInt);
+        [] ->
+            % Proc must've died and we already
+            % cleared it out of the table in
+            % the handle_info clause.
+            ok
+    end;
+return_proc(#state{} = State, #proc_int{} = ProcInt) ->
+    #state{tab = Tab, waiting = Waiting} = State,
+    #proc_int{pid = Pid, lang = Lang} = ProcInt,
     case is_process_alive(Pid) of true ->
         case get_waiting_client(Waiting, Lang) of
             nil ->
                 gen_server:cast(Pid, garbage_collect),
-                ets:insert(Tab, Proc#proc{client=nil}),
+                ets:insert(Tab, ProcInt#proc_int{client=nil}),
                 State;
             #client{}=Client ->
                 From = Client#client.from,
-                assign_proc(Tab, Client, Proc#proc{client=nil}),
-                gen_server:reply(From, {ok, Proc, State#state.config}),
+                assign_proc(Tab, Client, ProcInt#proc_int{client=nil}),
+                gen_server:reply(From, {ok, ProcInt, State#state.config}),
                 State
         end;
     false ->
@@ -355,6 +377,11 @@ return_proc(State, #proc{pid=Pid, lang=Lang} = Proc) ->
         end
     end.
 
+-spec export_proc(#proc_int{}) -> #proc{}.
+export_proc(#proc_int{} = ProcInt) ->
+    [_ | Data] = lists:sublist(record_info(size, proc), tuple_to_list(ProcInt)),
+    list_to_tuple([proc | Data]).
+
 maybe_spawn_proc(State, Client) ->
     #state{proc_counts=Counts, waiting=Waiting} = State,
     #client{lang=Lang} = Client,


[38/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Gracefully handle design document updates with malformatted structure

The existing code will break with a badmatch when a user attempts to
insert a design document with the following structure:

    {"_id": "_design/test",
     "views": "a string"}

This badmatch bubbles up to the user as an HTTP 500 with a nasty function
clause stacktrace[1].

With this patch, the user sees a clean HTTP 400 with a description of the
error[2].

[1]: https://gist.github.com/banjiewen/8dd395f152eef9269d77
[2]: https://gist.github.com/banjiewen/ae665af65171fb7c6572

BugzID: 23081


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/2ca323c0
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/2ca323c0
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/2ca323c0

Branch: refs/heads/windsor-merge-300
Commit: 2ca323c0d480127f8bd10365a0bc8cfe16cb03d3
Parents: 13b4e43
Author: Benjamin Anderson <b...@banjiewen.net>
Authored: Mon Sep 30 16:18:55 2013 -0700
Committer: Robert Newson <rn...@apache.org>
Committed: Wed Aug 6 11:12:59 2014 +0100

----------------------------------------------------------------------
 src/couch_db.erl | 2 ++
 1 file changed, 2 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/2ca323c0/src/couch_db.erl
----------------------------------------------------------------------
diff --git a/src/couch_db.erl b/src/couch_db.erl
index b0c7894..4adb128 100644
--- a/src/couch_db.erl
+++ b/src/couch_db.erl
@@ -548,6 +548,8 @@ validate_ddoc(DbName, DDoc) ->
     try
         couch_index_server:validate(DbName, couch_doc:with_ejson_body(DDoc))
     catch
+        throw:{invalid_design_doc, Reason} ->
+            {bad_request, invalid_design_doc, Reason};
         throw:Error ->
             Error
     end.


[07/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Switch notifications to the new couch_event app


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/2b45f92b
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/2b45f92b
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/2b45f92b

Branch: refs/heads/windsor-merge-300
Commit: 2b45f92ba0f5c5de6eef1c65ee6252abb9f0ffb9
Parents: 088ded6
Author: Paul J. Davis <pa...@gmail.com>
Authored: Wed Apr 24 12:42:14 2013 -0500
Committer: Robert Newson <rn...@apache.org>
Committed: Mon Aug 4 15:19:26 2014 +0100

----------------------------------------------------------------------
 src/couch_db_updater.erl | 24 ++++++++++++++----------
 src/couch_server.erl     |  4 ++--
 2 files changed, 16 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/2b45f92b/src/couch_db_updater.erl
----------------------------------------------------------------------
diff --git a/src/couch_db_updater.erl b/src/couch_db_updater.erl
index 1b20544..4e72dac 100644
--- a/src/couch_db_updater.erl
+++ b/src/couch_db_updater.erl
@@ -107,7 +107,7 @@ handle_call(cancel_compact, _From, #db{compactor_pid = Pid} = Db) ->
 handle_call(increment_update_seq, _From, Db) ->
     Db2 = commit_data(Db#db{update_seq=Db#db.update_seq+1}),
     ok = gen_server:call(couch_server, {db_updated, Db2}, infinity),
-    couch_db_update_notifier:notify({updated, Db#db.name}),
+    couch_event:notify(Db#db.name, updated),
     {reply, {ok, Db2#db.update_seq}, Db2};
 
 handle_call({set_security, NewSec}, _From, #db{compression = Comp} = Db) ->
@@ -189,7 +189,7 @@ handle_call({purge_docs, IdRevs}, _From, Db) ->
             header=Header#db_header{purge_seq=PurgeSeq+1, purged_docs=Pointer}}),
 
     ok = gen_server:call(couch_server, {db_updated, Db2}, infinity),
-    couch_db_update_notifier:notify({updated, Db#db.name}),
+    couch_event:notify(Db#db.name, updated),
     {reply, {ok, (Db2#db.header)#db_header.purge_seq, IdRevsPurged}, Db2}.
 
 
@@ -242,7 +242,7 @@ handle_cast({compact_done, CompactFilepath}, #db{filepath=Filepath}=Db) ->
         close_db(Db),
         NewDb3 = refresh_validate_doc_funs(NewDb2),
         ok = gen_server:call(couch_server, {db_updated, NewDb3}, infinity),
-        couch_db_update_notifier:notify({compacted, NewDb3#db.name}),
+        couch_event:notify(NewDb3#db.name, compacted),
         ?LOG_INFO("Compaction for db \"~s\" completed.", [Db#db.name]),
         {noreply, NewDb3#db{compactor_pid=nil}};
     false ->
@@ -278,14 +278,19 @@ handle_info({update_docs, Client, GroupedDocs, NonRepDocs, MergeConflicts,
     {ok, Db2, UpdatedDDocIds} ->
         ok = gen_server:call(couch_server, {db_updated, Db2}, infinity),
         if Db2#db.update_seq /= Db#db.update_seq ->
-            couch_db_update_notifier:notify({updated, Db2#db.name});
+            couch_event:notify(Db2#db.name, updated);
         true -> ok
         end,
         [catch(ClientPid ! {done, self()}) || ClientPid <- Clients],
-        lists:foreach(fun(DDocId) ->
-            couch_db_update_notifier:notify({ddoc_updated, {Db#db.name, DDocId}})
-        end, UpdatedDDocIds),
-        {noreply, Db2, hibernate}
+        Db3 = case length(UpdatedDDocIds) > 0 of
+            true ->
+                couch_event:notify(Db2#db.name, ddoc_updated),
+                ddoc_cache:evict(Db2#db.name, UpdatedDDocIds),
+                refresh_validate_doc_funs(Db2);
+            false ->
+                Db2
+        end,
+        {noreply, Db3, hibernate}
     catch
         throw: retry ->
             [catch(ClientPid ! {retry, self()}) || ClientPid <- Clients],
@@ -738,8 +743,7 @@ update_docs_int(Db, DocsList, NonRepDocs, MergeConflicts, FullCommit) ->
     % funs if we did.
     Db4 = case length(UpdatedDDocIds) > 0 of
         true ->
-            couch_db_update_notifier:notify(
-                {ddoc_updated, Db3#db.name}),
+            couch_event:notify(Db3#db.name, ddoc_updated),
             ddoc_cache:evict(Db3#db.name, UpdatedDDocIds),
             refresh_validate_doc_funs(Db3);
         false ->

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/2b45f92b/src/couch_server.erl
----------------------------------------------------------------------
diff --git a/src/couch_server.erl b/src/couch_server.erl
index e54efcc..5a73877 100644
--- a/src/couch_server.erl
+++ b/src/couch_server.erl
@@ -291,7 +291,7 @@ open_async(Server, From, DbName, Filepath, Options) ->
         Res = couch_db:start_link(DbName, Filepath, Options),
         case {Res, lists:member(create, Options)} of
             {{ok, _Db}, true} ->
-                couch_db_update_notifier:notify({created, DbName});
+                couch_event:notify(DbName, created);
             _ ->
                 ok
         end,
@@ -456,7 +456,7 @@ handle_call({delete, DbName, Options}, _From, Server) ->
 
         case couch_file:delete(Server#server.root_dir, FullFilepath, Async) of
         ok ->
-            couch_db_update_notifier:notify({deleted, DbName}),
+            couch_event:notify(DbName, deleted),
             {reply, ok, Server2};
         {error, enoent} ->
             {reply, not_found, Server2};


[23/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Use a module for access to db_header records

While this isn't as bad as sharing group records between nodes it is a
bit complicated because db_header records are shared amongst a large
number of processes that don't have access to a code_upgrade clause.

This module provides access to all db_header fields in a backwards
compatible manner so that records floating around during an upgrade
don't suddenly start causing bad match errors.

There's also an API for setting field values that will be used by
couch_db_updater that automatically upgrades records to the latest
version.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/f704acf4
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/f704acf4
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/f704acf4

Branch: refs/heads/windsor-merge-300
Commit: f704acf46ec0d31f27f940edd3c0dade4a5c79c0
Parents: e538ff7
Author: Paul J. Davis <pa...@gmail.com>
Authored: Thu Jun 13 15:08:54 2013 -0500
Committer: Robert Newson <rn...@apache.org>
Committed: Tue Aug 5 15:25:45 2014 +0100

----------------------------------------------------------------------
 src/couch_db_header.erl | 344 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 344 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/f704acf4/src/couch_db_header.erl
----------------------------------------------------------------------
diff --git a/src/couch_db_header.erl b/src/couch_db_header.erl
new file mode 100644
index 0000000..e0d7f4b
--- /dev/null
+++ b/src/couch_db_header.erl
@@ -0,0 +1,344 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_db_header).
+
+
+-export([
+    new/0,
+    from/1,
+    is_header/1,
+    upgrade/1,
+    set/2
+]).
+
+-export([
+    disk_version/1,
+    update_seq/1,
+    id_tree_state/1,
+    seq_tree_state/1,
+    local_tree_state/1,
+    purge_seq/1,
+    purged_docs/1,
+    security_ptr/1,
+    revs_limit/1,
+    uuid/1,
+    epochs/1
+]).
+
+
+% This should be updated anytime a header change happens that requires more
+% than filling in new defaults.
+%
+% As long the changes are limited to new header fields (with inline
+% defaults) added to the end of the record, then there is no need to increment
+% the disk revision number.
+%
+% 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, 6).
+
+-record(db_header, {
+    disk_version = ?LATEST_DISK_VERSION,
+    update_seq = 0,
+    unused = 0,
+    id_tree_state = nil,
+    seq_tree_state = nil,
+    local_tree_state = nil,
+    purge_seq = 0,
+    purged_docs = nil,
+    security_ptr = nil,
+    revs_limit = 1000,
+    uuid,
+    epochs
+}).
+
+
+new() ->
+    #db_header{
+        uuid = couch_uuids:random(),
+        epochs = [{node(), 0}]
+    }.
+
+
+from(Header0) ->
+    Header = upgrade(Header0),
+    #db_header{
+        uuid = Header#db_header.uuid,
+        epochs = Header#db_header.epochs
+    }.
+
+
+is_header(Header) ->
+    try
+        upgrade(Header),
+        true
+    catch _:_ ->
+        false
+    end.
+
+
+upgrade(Header) ->
+    Funs = [
+        fun upgrade_tuple/1,
+        fun upgrade_disk_version/1,
+        fun upgrade_uuid/1,
+        fun upgrade_epochs/1
+    ],
+    lists:foldl(fun(F, HdrAcc) ->
+        F(HdrAcc)
+    end, Header, Funs).
+
+
+set(Header0, Fields) ->
+    % A subtlety here is that if a database was open during
+    % the release upgrade that updates to uuids and epochs then
+    % this dynamic upgrade also assigns a uuid and epoch.
+    Header = upgrade(Header0),
+    lists:foldl(fun({Field, Value}, HdrAcc) ->
+        set_field(HdrAcc, Field, Value)
+    end, Header, Fields).
+
+
+disk_version(Header) ->
+    get_field(Header, disk_version).
+
+
+update_seq(Header) ->
+    get_field(Header, update_seq).
+
+
+id_tree_state(Header) ->
+    get_field(Header, id_tree_state).
+
+
+seq_tree_state(Header) ->
+    get_field(Header, seq_tree_state).
+
+
+local_tree_state(Header) ->
+    get_field(Header, local_tree_state).
+
+
+purge_seq(Header) ->
+    get_field(Header, purge_seq).
+
+
+purged_docs(Header) ->
+    get_field(Header, purged_docs).
+
+
+security_ptr(Header) ->
+    get_field(Header, security_ptr).
+
+
+revs_limit(Header) ->
+    get_field(Header, revs_limit).
+
+
+uuid(Header) ->
+    get_field(Header, uuid).
+
+
+epochs(Header) ->
+    get_field(Header, epochs).
+
+
+get_field(Header, Field) ->
+    element(index(Field), Header).
+
+
+set_field(Header, Field, Value) ->
+    setelement(index(Field), Header, Value).
+
+
+index(Field) ->
+    couch_util:get_value(Field, indexes()).
+
+
+indexes() ->
+    Fields = record_info(fields, db_header),
+    Indexes = lists:seq(2, record_info(size, db_header)),
+    lists:zip(Fields, Indexes).
+
+
+upgrade_tuple(Old) when is_record(Old, db_header) ->
+    Old;
+upgrade_tuple(Old) when is_tuple(Old) ->
+    NewSize = record_info(size, db_header),
+    if tuple_size(Old) < NewSize -> ok; true ->
+        erlang:error({invalid_header_size, Old})
+    end,
+    {_, New} = lists:foldl(fun(Val, {Idx, Hdr}) ->
+        {Idx+1, setelement(Idx, Hdr, Val)}
+    end, {1, #db_header{}}, tuple_to_list(Old)),
+    if is_record(New, db_header) -> ok; true ->
+        erlang:error({invalid_header_extension, {Old, New}})
+    end,
+    New.
+
+
+upgrade_disk_version(#db_header{}=Header) ->
+    case element(2, Header) of
+        1 -> Header#db_header{unused = 0, security_ptr = nil}; % 0.9]
+        2 -> Header#db_header{unused = 0, security_ptr = nil}; % (0.9 - 0.10)
+        3 -> Header#db_header{security_ptr = nil}; % (0.9 - 0.10)
+        4 -> Header#db_header{security_ptr = nil}; % [0.10 - 0.11)
+        5 -> Header; % pre 1.2
+        ?LATEST_DISK_VERSION -> Header;
+        _ ->
+            Reason = "Incorrect disk header version",
+            throw({database_disk_version_error, Reason})
+    end.
+
+
+upgrade_uuid(#db_header{}=Header) ->
+    case Header#db_header.uuid of
+        undefined ->
+            % Upgrading this old db file to a newer
+            % on disk format that includes a UUID.
+            Header#db_header{uuid=couch_uuids:random()};
+        _ ->
+            Header
+    end.
+
+
+upgrade_epochs(#db_header{}=Header) ->
+    NewEpochs = case Header#db_header.epochs of
+        undefined ->
+            % This node is taking over ownership of shard with
+            % and old version of couch file. Before epochs there
+            % was always an implicit assumption that a file was
+            % owned since eternity by the node it was on. This
+            % just codifies that assumption.
+            [{node(), 0}];
+        [{Node, _} | _] = Epochs0 when Node == node() ->
+            % Current node is the current owner of this db
+            Epochs0;
+        Epochs1 ->
+            % This node is taking over ownership of this db
+            % and marking the update sequence where it happened.
+            [{node(), Header#db_header.update_seq} | Epochs1]
+    end,
+    Header#db_header{epochs=NewEpochs}.
+
+
+-ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
+
+mk_header(Vsn) ->
+    {
+        db_header, % record name
+        Vsn, % disk version
+        100, % update_seq
+        0, % unused
+        foo, % id_tree_state
+        bar, % seq_tree_state
+        bam, % local_tree_state
+        1, % purge_seq
+        baz, % purged_docs
+        bang, % security_ptr
+        999 % revs_limit
+    }.
+
+
+upgrade_v3_test() ->
+    Vsn3Header = mk_header(3),
+    NewHeader = upgrade_tuple(Vsn3Header),
+
+    % Tuple upgrades don't change
+    ?assert(is_record(NewHeader, db_header)),
+    ?assertEqual(3, disk_version(NewHeader)),
+    ?assertEqual(100, update_seq(NewHeader)),
+    ?assertEqual(foo, id_tree_state(NewHeader)),
+    ?assertEqual(bar, seq_tree_state(NewHeader)),
+    ?assertEqual(bam, local_tree_state(NewHeader)),
+    ?assertEqual(1, purge_seq(NewHeader)),
+    ?assertEqual(baz, purged_docs(NewHeader)),
+    ?assertEqual(bang, security_ptr(NewHeader)),
+    ?assertEqual(999, revs_limit(NewHeader)),
+    ?assertEqual(undefined, uuid(NewHeader)),
+    ?assertEqual(undefined, epochs(NewHeader)),
+
+    % Security ptr isn't changed until upgrade_disk_version/1
+    NewNewHeader = upgrade_disk_version(NewHeader),
+    ?assert(is_record(NewNewHeader, db_header)),
+    ?assertEqual(nil, security_ptr(NewNewHeader)),
+
+    % Assert upgrade works on really old headers
+    NewestHeader = upgrade(Vsn3Header),
+    ?assertMatch(<<_:32/binary>>, uuid(NewestHeader)),
+    ?assertEqual([{node(), 0}], epochs(NewestHeader)).
+
+
+upgrade_v5_test() ->
+    Vsn5Header = mk_header(5),
+    NewHeader = upgrade_disk_version(upgrade_tuple(Vsn5Header)),
+
+    ?assert(is_record(NewHeader, db_header)),
+    ?assertEqual(5, disk_version(NewHeader)),
+
+    % Security ptr isn't changed for v5 headers
+    ?assertEqual(bang, security_ptr(NewHeader)).
+
+
+upgrade_uuid_test() ->
+    Vsn5Header = mk_header(5),
+
+    % Upgraded headers get a new UUID
+    NewHeader = upgrade_uuid(upgrade_disk_version(upgrade_tuple(Vsn5Header))),
+    ?assertMatch(<<_:32/binary>>, uuid(NewHeader)),
+
+    % Headers with a UUID don't have their UUID changed
+    NewNewHeader = upgrade_uuid(upgrade_disk_version(upgrade_tuple(NewHeader))),
+    ?assertEqual(uuid(NewHeader), uuid(NewNewHeader)),
+
+    % Derived empty headers maintain the same UUID
+    ResetHeader = from(NewNewHeader),
+    ?assertEqual(uuid(NewHeader), uuid(ResetHeader)).
+
+
+upgrade_epochs_test() ->
+    Vsn5Header = mk_header(5),
+
+    % Upgraded headers get a default epochs set
+    NewHeader = upgrade(Vsn5Header),
+    ?assertEqual([{node(), 0}], epochs(NewHeader)),
+
+    % Fake an old entry in epochs
+    FakeFields = [
+        {update_seq, 20},
+        {epochs, [{'someothernode@someotherhost', 0}]}
+    ],
+    NotOwnedHeader = set(NewHeader, FakeFields),
+
+    OwnedEpochs = [
+        {node(), 20},
+        {'someothernode@someotherhost', 0}
+    ],
+
+    % Upgrading a header not owned by the local node updates
+    % the epochs appropriately.
+    NowOwnedHeader = upgrade(NotOwnedHeader),
+    ?assertEqual(OwnedEpochs, epochs(NowOwnedHeader)),
+
+    % Headers with epochs stay the same after upgrades
+    NewNewHeader = upgrade(NowOwnedHeader),
+    ?assertEqual(OwnedEpochs, epochs(NewNewHeader)),
+
+    % Getting a reset header maintains the epoch data
+    ResetHeader = from(NewNewHeader),
+    ?assertEqual(OwnedEpochs, epochs(ResetHeader)).
+
+
+-endif.


[19/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Fix use of lists:sublist/2

The lists module has pretty terrible consistency on which argument is
the list. This one happens to take the list first.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/ea2b1ee0
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/ea2b1ee0
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/ea2b1ee0

Branch: refs/heads/windsor-merge-300
Commit: ea2b1ee09242974991360d313705387ed727ec85
Parents: 08a25ad
Author: Paul J. Davis <pa...@gmail.com>
Authored: Fri Jun 7 14:42:11 2013 -0500
Committer: Robert Newson <rn...@apache.org>
Committed: Tue Aug 5 12:03:15 2014 +0100

----------------------------------------------------------------------
 src/couch_proc_manager.erl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/ea2b1ee0/src/couch_proc_manager.erl
----------------------------------------------------------------------
diff --git a/src/couch_proc_manager.erl b/src/couch_proc_manager.erl
index f84c06a..252de2c 100644
--- a/src/couch_proc_manager.erl
+++ b/src/couch_proc_manager.erl
@@ -425,7 +425,7 @@ remove_proc(Tab, Pid) ->
 
 -spec export_proc(#proc_int{}) -> #proc{}.
 export_proc(#proc_int{} = ProcInt) ->
-    [_ | Data] = lists:sublist(record_info(size, proc), tuple_to_list(ProcInt)),
+    [_ | Data] = lists:sublist(tuple_to_list(ProcInt), record_info(size, proc)),
     list_to_tuple([proc | Data]).
 
 import_proc(#proc{} = P) ->


[33/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Add stop clause for couch_btree

This is part of "Implement couch_view:get_map_keys/4", the rest was in
couch_view:get_map_keys, a module which no longer exists.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/8e09de27
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/8e09de27
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/8e09de27

Branch: refs/heads/windsor-merge-300
Commit: 8e09de27be8517147b27902482cf3dcf5d8f5851
Parents: 41b61b9
Author: Paul J. Davis <pa...@gmail.com>
Authored: Sat Jul 20 17:57:36 2013 -0500
Committer: Robert Newson <rn...@apache.org>
Committed: Tue Aug 5 17:38:00 2014 +0100

----------------------------------------------------------------------
 src/couch_btree.erl | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/8e09de27/src/couch_btree.erl
----------------------------------------------------------------------
diff --git a/src/couch_btree.erl b/src/couch_btree.erl
index 09c3d6a..e9ff7f5 100644
--- a/src/couch_btree.erl
+++ b/src/couch_btree.erl
@@ -714,7 +714,9 @@ stream_kp_node(Bt, Reds, [{Key, Node} | Rest], InRange, Dir, Fun, Acc) ->
             {stop, LastReds, Acc3}
         end;
     {skip, Acc2} ->
-        stream_kp_node(Bt, [Red | Reds], Rest, InRange, Dir, Fun, Acc2)
+        stream_kp_node(Bt, [Red | Reds], Rest, InRange, Dir, Fun, Acc2);
+    {stop, Acc2} ->
+        {stop, Reds, Acc2}
     end.
 
 drop_nodes(_Bt, Reds, _StartKey, []) ->


[03/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Export parse_copy_destination_header/1


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/a74e7a0f
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/a74e7a0f
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/a74e7a0f

Branch: refs/heads/windsor-merge-300
Commit: a74e7a0fdc8c20af06033573665bdc99780e27c5
Parents: 7e8dca7
Author: Robert Newson <rn...@apache.org>
Authored: Mon Aug 4 13:44:52 2014 +0100
Committer: Robert Newson <rn...@apache.org>
Committed: Mon Aug 4 14:16:55 2014 +0100

----------------------------------------------------------------------
 src/couch_httpd_db.erl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a74e7a0f/src/couch_httpd_db.erl
----------------------------------------------------------------------
diff --git a/src/couch_httpd_db.erl b/src/couch_httpd_db.erl
index 4c113e5..7360dc0 100644
--- a/src/couch_httpd_db.erl
+++ b/src/couch_httpd_db.erl
@@ -16,7 +16,7 @@
 -export([handle_request/1, handle_compact_req/2, handle_design_req/2,
     db_req/2, couch_doc_open/4,handle_changes_req/2,
     update_doc_result_to_json/1, update_doc_result_to_json/2,
-    handle_design_info_req/3]).
+    handle_design_info_req/3, parse_copy_destination_header/1]).
 
 -import(couch_httpd,
     [send_json/2,send_json/3,send_json/4,send_method_not_allowed/2,


[20/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Fix typo in pattern match


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/3bf3cbd2
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/3bf3cbd2
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/3bf3cbd2

Branch: refs/heads/windsor-merge-300
Commit: 3bf3cbd2f78f6c030e796d7034a69b08df224bac
Parents: 1f5673f
Author: Robert Newson <rn...@apache.org>
Authored: Tue Aug 5 11:49:04 2014 +0100
Committer: Robert Newson <rn...@apache.org>
Committed: Tue Aug 5 12:03:15 2014 +0100

----------------------------------------------------------------------
 src/couch_proc_manager.erl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/3bf3cbd2/src/couch_proc_manager.erl
----------------------------------------------------------------------
diff --git a/src/couch_proc_manager.erl b/src/couch_proc_manager.erl
index 8288b13..d06e7b9 100644
--- a/src/couch_proc_manager.erl
+++ b/src/couch_proc_manager.erl
@@ -217,7 +217,7 @@ handle_info({gen_event_EXIT, {config_listener, ?MODULE}, _Reason}, State) ->
     erlang:send_after(5000, self(), restart_config_listener),
     {noreply, State};
 
-handle_info(restart_config_lister, State) ->
+handle_info(restart_config_listener, State) ->
     ok = config:listen_for_changes(?MODULE, nil),
     {noreply, State};
 


[32/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Verify that DB is in couch_dbs before updating

This patch fixes a bug where there could be a db_updated message in the
couch_server message queue, but the updater process could be dead and
removed from the couch_dbs ets table. While this patch doesn't change
the possibility of that state, it makes the couch_server db_updated
function clause to verify that 1) the DB is in the couch_dbs ets table
and 2) that the instance_start_time of the updated DB matches the
instance_start_time of the couch_dbs entry. If the DB isn't in the
couch_dbs ets table it means that it was removed from the LRU. If the
two instance_start_times don't match, it means that the old couch_dbs
entry was expired from the LRU and re-inserted before the monitor exit
message was processed.

BugzID: 21179


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/41b61b9a
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/41b61b9a
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/41b61b9a

Branch: refs/heads/windsor-merge-300
Commit: 41b61b9a2f07e62dc20a84be85f328dd225f89c9
Parents: 9f16b07
Author: Benjamin Bastian <be...@gmail.com>
Authored: Mon Jul 22 15:41:54 2013 -0700
Committer: Robert Newson <rn...@apache.org>
Committed: Tue Aug 5 17:35:48 2014 +0100

----------------------------------------------------------------------
 src/couch_server.erl | 21 +++++++++++++++------
 1 file changed, 15 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/41b61b9a/src/couch_server.erl
----------------------------------------------------------------------
diff --git a/src/couch_server.erl b/src/couch_server.erl
index e741720..fd4691c 100644
--- a/src/couch_server.erl
+++ b/src/couch_server.erl
@@ -483,13 +483,22 @@ handle_call({delete, DbName, Options}, _From, Server) ->
     Error ->
         {reply, Error, Server}
     end;
-handle_call({db_updated, #db{name = DbName} = Db}, _From, Server) ->
-    true = ets:insert(couch_dbs, Db),
-    Lru = case couch_db:is_system_db(Db) of
-        false -> couch_lru:update(DbName, Server#server.lru);
-        true -> Server#server.lru
+handle_call({db_updated, #db{}=Db}, _From, Server0) ->
+    #db{name = DbName, instance_start_time = StartTime} = Db,
+    Server = try ets:lookup_element(couch_dbs, DbName, #db.instance_start_time) of
+        StartTime ->
+            true = ets:insert(couch_dbs, Db),
+            Lru = case couch_db:is_system_db(Db) of
+                false -> couch_lru:update(DbName, Server0#server.lru);
+                true -> Server0#server.lru
+            end,
+            Server0#server{lru = Lru};
+        _ ->
+            Server0
+    catch _:_ ->
+        Server0
     end,
-    {reply, ok, Server#server{lru = Lru}}.
+    {reply, ok, Server}.
 
 handle_cast({update_lru, DbName}, #server{lru = Lru, update_lru_on_read=true} = Server) ->
     {noreply, Server#server{lru = couch_lru:update(DbName, Lru)}};


[17/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Allow operator to forcibly terminate stale procs

This patch adds a new terminate_stale_procs/0 command which kills off
any OS processes with spawn times older than the current threshold time,
regardless of whether the process has a client.  It also refactors the
internals to use the same logic to terminate a process in every case.

BugzID: 19529


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/acb80abc
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/acb80abc
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/acb80abc

Branch: refs/heads/windsor-merge-300
Commit: acb80abc56eb85aa910736d913e7dcb4f8b05682
Parents: 3bf3cbd
Author: Adam Kocoloski <ad...@cloudant.com>
Authored: Mon Jun 3 13:24:02 2013 -0400
Committer: Robert Newson <rn...@apache.org>
Committed: Tue Aug 5 12:03:15 2014 +0100

----------------------------------------------------------------------
 src/couch_proc_manager.erl | 37 ++++++++++++++++++++++++-------------
 1 file changed, 24 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/acb80abc/src/couch_proc_manager.erl
----------------------------------------------------------------------
diff --git a/src/couch_proc_manager.erl b/src/couch_proc_manager.erl
index d06e7b9..8cb53cb 100644
--- a/src/couch_proc_manager.erl
+++ b/src/couch_proc_manager.erl
@@ -22,7 +22,8 @@
     get_proc_count/0,
     get_stale_proc_count/0,
     new_proc/1,
-    reload/0
+    reload/0,
+    terminate_stale_procs/0
 ]).
 
 % config_listener api
@@ -69,6 +70,9 @@ get_stale_proc_count() ->
 reload() ->
     gen_server:call(?MODULE, bump_threshold_ts).
 
+terminate_stale_procs() ->
+    gen_server:call(?MODULE, terminate_stale_procs).
+
 init([]) ->
     process_flag(trap_exit, true),
     ok = config:listen_for_changes(?MODULE, nil),
@@ -133,14 +137,20 @@ handle_call({ret_proc, #proc{client=Ref, lang=Lang0} = Proc}, _From, State) ->
 
 handle_call(bump_threshold_ts, _From, #state{tab = Tab} = State) ->
     FoldFun = fun(#proc_int{client = nil, pid = Pid}, _) ->
-        gen_server:cast(Pid, stop),
-        ets:delete(Tab, Pid);
+        remove_proc(Tab, Pid);
     (_, _) ->
         ok
     end,
     ets:foldl(FoldFun, nil, Tab),
     {reply, ok, State#state{threshold_ts = os:timestamp()}};
 
+handle_call(terminate_stale_procs, _From, State) ->
+    #state{tab = Tab, threshold_ts = T0} = State,
+    MatchHead = #proc_int{pid = '$1', t0 = '$2', _ = '_'},
+    MatchSpec = [{MatchHead, [{'<', '$2', T0}], ['$1']}],
+    lists:foreach(fun(P) -> remove_proc(Tab,P) end, ets:select(Tab, MatchSpec)),
+    {reply, ok, State};
+
 handle_call(_Call, _From, State) ->
     {reply, ignored, State}.
 
@@ -152,14 +162,7 @@ handle_cast({os_proc_idle, Pid}, #state{tab=Tab, proc_counts=Counts}=State0) ->
             case dict:find(Lang, Counts) of
                 {ok, Count} when Count > Limit ->
                     ?LOG_INFO("Closing idle OS Process: ~p", [Pid]),
-                    ets:delete(Tab, Pid),
-                    case is_process_alive(Pid) of
-                        true ->
-                            unlink(Pid),
-                            gen_server:cast(Pid, stop);
-                        _ ->
-                            ok
-                    end,
+                    remove_proc(Tab, Pid),
                     State0#state{
                         proc_counts=dict:update_counter(Lang, -1, Counts)
                     };
@@ -394,8 +397,7 @@ return_proc(#state{} = State, #proc_int{} = ProcInt) ->
         case get_waiting_client(Waiting, Lang) of
             nil ->
                 if ProcInt#proc_int.t0 < T0 ->
-                    gen_server:cast(Pid, stop),
-                    ets:delete(Tab, Pid);
+                    remove_proc(Tab, Pid);
                 true ->
                     gen_server:cast(Pid, garbage_collect),
                     ets:insert(Tab, ProcInt#proc_int{client=nil})
@@ -417,6 +419,15 @@ return_proc(#state{} = State, #proc_int{} = ProcInt) ->
         end
     end.
 
+remove_proc(Tab, Pid) ->
+    ets:delete(Tab, Pid),
+    case is_process_alive(Pid) of true ->
+        unlink(Pid),
+        gen_server:cast(Pid, stop);
+    false ->
+        ok
+    end.
+
 -spec export_proc(#proc_int{}) -> #proc{}.
 export_proc(#proc_int{} = ProcInt) ->
     [_ | Data] = lists:sublist(record_info(size, proc), tuple_to_list(ProcInt)),


[47/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Minor cosmetic change

1) Export indentation
2) Comment typo


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/371ae11c
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/371ae11c
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/371ae11c

Branch: refs/heads/windsor-merge-300
Commit: 371ae11c070a5ff5be8e56658e39411e9179560e
Parents: ff82e5f
Author: Paul J. Davis <pa...@gmail.com>
Authored: Fri Nov 15 12:07:18 2013 -0600
Committer: Robert Newson <rn...@apache.org>
Committed: Wed Aug 6 12:49:04 2014 +0100

----------------------------------------------------------------------
 src/couch_key_tree.erl | 21 +++++++++++++++++----
 1 file changed, 17 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/371ae11c/src/couch_key_tree.erl
----------------------------------------------------------------------
diff --git a/src/couch_key_tree.erl b/src/couch_key_tree.erl
index a7f6bb2..bf7e61c 100644
--- a/src/couch_key_tree.erl
+++ b/src/couch_key_tree.erl
@@ -37,7 +37,7 @@
 %% this limit is exceeded only the last 1000 are kept. This comes in to play
 %% when branches are merged. The comparison has to begin at the same place in
 %% the branches. A revision id is of the form N-XXXXXXX where N is the current
-%% revision. So each path will have a start number, calculated in
+%% revision depth. So each path will have a start number, calculated in
 %% couch_doc:to_path using the formula N - length(RevIds) + 1 So, .eg. if a doc
 %% was edit 1003 times this start number would be 4, indicating that 3
 %% revisions were truncated.
@@ -47,9 +47,22 @@
 
 -module(couch_key_tree).
 
--export([merge/3, find_missing/2, get_key_leafs/2, get_full_key_paths/2, get/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, fold/3]).
+-export([
+count_leafs/1,
+find_missing/2,
+fold/3,
+get/2,
+get_all_leafs/1,
+get_all_leafs_full/1,
+get_full_key_paths/2,
+get_key_leafs/2,
+map/2,
+map_leafs/2,
+mapfold/3,
+merge/3,
+remove_leafs/2,
+stem/2
+]).
 
 -include_lib("couch/include/couch_db.hrl").
 


[30/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Fix arity of listen_for_changes calls


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/7070a151
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/7070a151
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/7070a151

Branch: refs/heads/windsor-merge-300
Commit: 7070a151b2d97cfd748c8cb19d4409a222added4
Parents: ef1af37
Author: Robert Newson <ro...@cloudant.com>
Authored: Sat Jul 13 00:38:18 2013 +0100
Committer: Robert Newson <rn...@apache.org>
Committed: Tue Aug 5 16:52:07 2014 +0100

----------------------------------------------------------------------
 src/couch_external_server.erl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/7070a151/src/couch_external_server.erl
----------------------------------------------------------------------
diff --git a/src/couch_external_server.erl b/src/couch_external_server.erl
index 56406bb..123e2e1 100644
--- a/src/couch_external_server.erl
+++ b/src/couch_external_server.erl
@@ -43,7 +43,7 @@ init([Name, Command]) ->
     Timeout = list_to_integer(config:get("couchdb", "os_process_timeout",
         "5000")),
     {ok, Pid} = couch_os_process:start_link(Command, [{timeout, Timeout}]),
-    ok = config:listen_for_changes(?MODULE, {Name, Command, Pid}, Pid),
+    ok = config:listen_for_changes(?MODULE, Pid),
     {ok, {Name, Command, Pid}}.
 
 terminate(_Reason, {_Name, _Command, Pid}) ->


[41/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Improve responses for bad multipart/mime requests

This changes a number of possible errors in multipart/mime requests to
correctly report a "400 Bad Request" instead of crashing and giving the
client a "500 Internal Server Error". This works by monitoring the
parser process and checking for a host of new exit reasons.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/a9f5c830
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/a9f5c830
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/a9f5c830

Branch: refs/heads/windsor-merge-300
Commit: a9f5c8303e6efd0b1302a9135740610a6b3ef156
Parents: 5be71f7
Author: Mike Wallace <mi...@googlemail.com>
Authored: Thu Aug 29 15:33:16 2013 +0100
Committer: Robert Newson <rn...@apache.org>
Committed: Wed Aug 6 12:19:11 2014 +0100

----------------------------------------------------------------------
 src/couch_db.erl    |  8 +++++++-
 src/couch_doc.erl   | 19 +++++++++++++++----
 src/couch_httpd.erl | 10 ++++++++--
 3 files changed, 30 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a9f5c830/src/couch_db.erl
----------------------------------------------------------------------
diff --git a/src/couch_db.erl b/src/couch_db.erl
index 836b468..c9dc8e4 100644
--- a/src/couch_db.erl
+++ b/src/couch_db.erl
@@ -1190,8 +1190,14 @@ with_stream(Fd, #att{md5=InMd5,type=Type,encoding=Enc}=Att, Fun) ->
 
 write_streamed_attachment(_Stream, _F, 0) ->
     ok;
+write_streamed_attachment(_Stream, _F, LenLeft) when LenLeft < 0 ->
+    throw({bad_request, <<"attachment longer than expected">>});
 write_streamed_attachment(Stream, F, LenLeft) when LenLeft > 0 ->
-    Bin = read_next_chunk(F, LenLeft),
+    Bin = try read_next_chunk(F, LenLeft)
+    catch
+        {mp_parser_died, normal} ->
+            throw({bad_request, <<"attachment shorter than expected">>})
+    end,
     ok = couch_stream:write(Stream, Bin),
     write_streamed_attachment(Stream, F, LenLeft - size(Bin)).
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a9f5c830/src/couch_doc.erl
----------------------------------------------------------------------
diff --git a/src/couch_doc.erl b/src/couch_doc.erl
index 14f6a4f..9263ba3 100644
--- a/src/couch_doc.erl
+++ b/src/couch_doc.erl
@@ -595,7 +595,7 @@ doc_from_multi_part_stream(ContentType, DataFun) ->
 doc_from_multi_part_stream(ContentType, DataFun, Ref) ->
     Parent = self(),
     NumMpWriters = num_mp_writers(),
-    Parser = spawn_link(fun() ->
+    {Parser, ParserRef} = spawn_monitor(fun() ->
         ParentRef = erlang:monitor(process, Parent),
         put(mp_parent_ref, ParentRef),
         put(num_mp_writers, NumMpWriters),
@@ -604,7 +604,6 @@ doc_from_multi_part_stream(ContentType, DataFun, Ref) ->
             fun(Next) -> mp_parse_doc(Next, []) end),
         unlink(Parent)
         end),
-    ParserRef = erlang:monitor(process, Parser),
     Parser ! {get_doc_bytes, Ref, self()},
     receive
     {started_open_doc_revs, NewRef} ->
@@ -623,7 +622,17 @@ doc_from_multi_part_stream(ContentType, DataFun, Ref) ->
             receive {'DOWN', ParserRef, _, _, _} -> ok end,
             erlang:put(mochiweb_request_recv, true)
         end,
-        {ok, Doc#doc{atts=Atts2}, WaitFun, Parser}
+        {ok, Doc#doc{atts=Atts2}, WaitFun, Parser};
+    {'DOWN', ParserRef, _, _, normal} ->
+        ok;
+    {'DOWN', ParserRef, process, Parser, {{nocatch, {Error, Msg}}, _}} ->
+        ?LOG_ERROR("Multipart streamer ~p died with reason ~p",
+                    [ParserRef, Msg]),
+        throw({Error, Msg});
+    {'DOWN', ParserRef, _, _, Reason} ->
+        ?LOG_ERROR("Multipart streamer ~p died with reason ~p",
+                    [ParserRef, Reason]),
+        throw({error, Reason})
     end.
 
 
@@ -632,7 +641,9 @@ mp_parse_doc({headers, H}, []) ->
     {"application/json", _} ->
         fun (Next) ->
             mp_parse_doc(Next, [])
-        end
+        end;
+    _ ->
+        throw({bad_ctype, <<"Content-Type must be application/json">>})
     end;
 mp_parse_doc({body, Bytes}, AccBytes) ->
     fun (Next) ->

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/a9f5c830/src/couch_httpd.erl
----------------------------------------------------------------------
diff --git a/src/couch_httpd.erl b/src/couch_httpd.erl
index 437ef08..8022a02 100644
--- a/src/couch_httpd.erl
+++ b/src/couch_httpd.erl
@@ -990,8 +990,14 @@ get_boundary(ContentType) ->
 split_header(<<>>) ->
     [];
 split_header(Line) ->
-    {Name, [$: | Value]} = lists:splitwith(fun (C) -> C =/= $: end,
-                                           binary_to_list(Line)),
+    {Name, Rest} = lists:splitwith(fun (C) -> C =/= $: end,
+                                   binary_to_list(Line)),
+    [$: | Value] = case Rest of
+        [] ->
+            throw({bad_request, <<"bad part header">>});
+        Res ->
+            Res
+    end,
     [{string:to_lower(string:strip(Name)),
      mochiweb_util:parse_header(Value)}].
 


[28/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Fix couch_server handling of downed db updaters

The return from ets:lookup/2 is the full object which we need to pattern
match against to get the DbName to remove.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/bc4ec1b3
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/bc4ec1b3
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/bc4ec1b3

Branch: refs/heads/windsor-merge-300
Commit: bc4ec1b37194fd69ad32f7353828f3bf520cae38
Parents: f1e1638
Author: Paul J. Davis <pa...@gmail.com>
Authored: Fri Jun 14 19:42:03 2013 -0500
Committer: Robert Newson <rn...@apache.org>
Committed: Tue Aug 5 16:22:06 2014 +0100

----------------------------------------------------------------------
 src/couch_server.erl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/bc4ec1b3/src/couch_server.erl
----------------------------------------------------------------------
diff --git a/src/couch_server.erl b/src/couch_server.erl
index 441290f..2199ce9 100644
--- a/src/couch_server.erl
+++ b/src/couch_server.erl
@@ -502,7 +502,7 @@ handle_info({'EXIT', _Pid, config_change}, Server) ->
     {stop, config_change, Server};
 handle_info({'EXIT', Pid, Reason}, Server) ->
     case ets:lookup(couch_dbs_pid_to_name, Pid) of
-    [DbName] ->
+    [{Pid, DbName}] ->
         [#db{compactor_pid=Froms}=Db] = ets:lookup(couch_dbs, DbName),
         if Reason /= snappy_nif_not_loaded -> ok; true ->
             Msg = io_lib:format("To open the database `~s`, Apache CouchDB "


[02/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Update all view/search index shards on ddoc update

This update is part of the fix to trigger view/search index updates for
all shards when a ddoc is updated. In the past, we only triggered
indexing jobs for shards which contained the ddoc. Other parts of the
fix are in ken and fabric. This particular commit contains the
following:

- A new couch_db_update event, which will be used in ken to spawn
  indexing jobs.

- A new function clause for the local _changes feed couch_db_update
  handler. This function clause will cause the _changes feed handler to
  ignore the aforementioned new couch_db_update event.

BugzID: 10979


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/088ded69
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/088ded69
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/088ded69

Branch: refs/heads/windsor-merge-300
Commit: 088ded691a04faa1c679ce5df1038df79ff8f89a
Parents: a74e7a0
Author: Benjamin Bastian <be...@gmail.com>
Authored: Wed Mar 13 17:10:05 2013 -0700
Committer: Robert Newson <rn...@apache.org>
Committed: Mon Aug 4 14:16:55 2014 +0100

----------------------------------------------------------------------
 src/couch_changes.erl    | 2 +-
 src/couch_db_updater.erl | 2 ++
 2 files changed, 3 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/088ded69/src/couch_changes.erl
----------------------------------------------------------------------
diff --git a/src/couch_changes.erl b/src/couch_changes.erl
index d8b5320..fd14f3d 100644
--- a/src/couch_changes.erl
+++ b/src/couch_changes.erl
@@ -79,7 +79,7 @@ handle_changes(Args1, Req, Db0) ->
             {Callback, UserAcc} = get_callback_acc(CallbackAcc),
             Self = self(),
             {ok, Notify} = couch_db_update_notifier:start_link(
-                fun({_, DbName}) when  Db0#db.name == DbName ->
+                fun({updated, DbName}) when Db0#db.name == DbName ->
                     Self ! db_updated;
                 (_) ->
                     ok

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/088ded69/src/couch_db_updater.erl
----------------------------------------------------------------------
diff --git a/src/couch_db_updater.erl b/src/couch_db_updater.erl
index 330f8cb..1b20544 100644
--- a/src/couch_db_updater.erl
+++ b/src/couch_db_updater.erl
@@ -738,6 +738,8 @@ update_docs_int(Db, DocsList, NonRepDocs, MergeConflicts, FullCommit) ->
     % funs if we did.
     Db4 = case length(UpdatedDDocIds) > 0 of
         true ->
+            couch_db_update_notifier:notify(
+                {ddoc_updated, Db3#db.name}),
             ddoc_cache:evict(Db3#db.name, UpdatedDDocIds),
             refresh_validate_doc_funs(Db3);
         false ->


[14/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Add config for couch_server LRU updates on read

This commit adds the ability for couch_server to ignore update_lru
messages. This should reduce load on couch_server when few entries are
being expired.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/f2b3162c
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/f2b3162c
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/f2b3162c

Branch: refs/heads/windsor-merge-300
Commit: f2b3162cd99a17d4d57fce035c3d326140124ae1
Parents: 1d6c18e
Author: Benjamin Bastian <be...@gmail.com>
Authored: Fri May 31 13:18:32 2013 -0700
Committer: Robert Newson <rn...@apache.org>
Committed: Mon Aug 4 16:41:04 2014 +0100

----------------------------------------------------------------------
 src/couch_server.erl | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/f2b3162c/src/couch_server.erl
----------------------------------------------------------------------
diff --git a/src/couch_server.erl b/src/couch_server.erl
index a10fcd0..441290f 100644
--- a/src/couch_server.erl
+++ b/src/couch_server.erl
@@ -34,6 +34,7 @@
     max_dbs_open=?MAX_DBS_OPEN,
     dbs_open=0,
     start_time="",
+    update_lru_on_read=true,
     lru = couch_lru:new()
     }).
 
@@ -213,6 +214,10 @@ terminate(_Reason, _Srv) ->
 handle_config_change("couchdb", "database_dir", _, _, _) ->
     exit(whereis(couch_server), config_change),
     remove_handler;
+handle_config_change("couchdb", "update_lru_on_read", "true", _, _) ->
+    {ok, gen_server:call(couch_server,{set_update_lru_on_read,true})};
+handle_config_change("couchdb", "update_lru_on_read", _, _, _) ->
+    {ok, gen_server:call(couch_server,{set_update_lru_on_read,false})};
 handle_config_change("couchdb", "max_dbs_open", Max, _, _) when is_list(Max) ->
     {ok, gen_server:call(couch_server,{set_max_dbs_open,list_to_integer(Max)})};
 handle_config_change("couchdb", "max_dbs_open", _, _, _) ->
@@ -324,6 +329,8 @@ handle_call(close_lru, _From, #server{lru=Lru} = Server) ->
     end;
 handle_call(open_dbs_count, _From, Server) ->
     {reply, Server#server.dbs_open, Server};
+handle_call({set_update_lru_on_read, UpdateOnRead}, _From, Server) ->
+    {reply, ok, Server#server{update_lru_on_read=UpdateOnRead}};
 handle_call({set_max_dbs_open, Max}, _From, Server) ->
     {reply, ok, Server#server{max_dbs_open=Max}};
 handle_call(get_server, _From, Server) ->
@@ -481,8 +488,10 @@ handle_call({db_updated, #db{name = DbName} = Db}, _From, Server) ->
     end,
     {reply, ok, Server#server{lru = Lru}}.
 
-handle_cast({update_lru, DbName}, #server{lru = Lru} = Server) ->
+handle_cast({update_lru, DbName}, #server{lru = Lru, update_lru_on_read=true} = Server) ->
     {noreply, Server#server{lru = couch_lru:update(DbName, Lru)}};
+handle_cast({update_lru, _DbName}, Server) ->
+    {noreply, Server};
 handle_cast(Msg, Server) ->
     {stop, {unknown_cast_message, Msg}, Server}.
 
@@ -494,8 +503,7 @@ handle_info({'EXIT', _Pid, config_change}, Server) ->
 handle_info({'EXIT', Pid, Reason}, Server) ->
     case ets:lookup(couch_dbs_pid_to_name, Pid) of
     [DbName] ->
-        [#db{compactor_pid=Froms}=Db] =
-            ets:match_object(couch_dbs, #db{name=DbName, _='_'}),
+        [#db{compactor_pid=Froms}=Db] = ets:lookup(couch_dbs, DbName),
         if Reason /= snappy_nif_not_loaded -> ok; true ->
             Msg = io_lib:format("To open the database `~s`, Apache CouchDB "
                 "must be built with Erlang OTP R13B04 or higher.", [DbName]),


[04/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Send 400 bad request rather than 500 for invalid base64 in auth header

Along with a useful error message.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/7e8dca72
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/7e8dca72
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/7e8dca72

Branch: refs/heads/windsor-merge-300
Commit: 7e8dca72bf156a6f207904f943751ac039144c9f
Parents: c4ce490
Author: Michael Rhodes <mi...@gmail.com>
Authored: Tue May 14 12:40:25 2013 +0100
Committer: Robert Newson <rn...@apache.org>
Committed: Mon Aug 4 14:16:55 2014 +0100

----------------------------------------------------------------------
 src/couch_httpd_auth.erl | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/7e8dca72/src/couch_httpd_auth.erl
----------------------------------------------------------------------
diff --git a/src/couch_httpd_auth.erl b/src/couch_httpd_auth.erl
index 6c1d037..483e4cd 100644
--- a/src/couch_httpd_auth.erl
+++ b/src/couch_httpd_auth.erl
@@ -48,7 +48,7 @@ basic_name_pw(Req) ->
     AuthorizationHeader = header_value(Req, "Authorization"),
     case AuthorizationHeader of
     "Basic " ++ Base64Value ->
-        case re:split(base64:decode(Base64Value), ":",
+        try re:split(base64:decode(Base64Value), ":",
                       [{return, list}, {parts, 2}]) of
         ["_", "_"] ->
             % special name and pass to be logged out
@@ -57,6 +57,9 @@ basic_name_pw(Req) ->
             {User, Pass};
         _ ->
             nil
+        catch
+        error:function_clause ->
+            throw({bad_request, "Authorization header has invalid base64 value"})
         end;
     _ ->
         nil


[24/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Switch to using the new couch_db_header module

This removes all direct access to db_header records and ensures that we
don't accidentally start using it by removing it from couch_db.hrl.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/2f3a1fd5
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/2f3a1fd5
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/2f3a1fd5

Branch: refs/heads/windsor-merge-300
Commit: 2f3a1fd5e9c58eaf268628fc7cbc6c0aa24b7669
Parents: f704acf
Author: Paul J. Davis <pa...@gmail.com>
Authored: Thu Jun 13 15:14:52 2013 -0500
Committer: Robert Newson <rn...@apache.org>
Committed: Tue Aug 5 16:01:10 2014 +0100

----------------------------------------------------------------------
 include/couch_db.hrl     |  29 +-------
 src/couch_db.erl         |  28 ++++---
 src/couch_db_header.erl  |   8 +-
 src/couch_db_updater.erl | 167 ++++++++++++------------------------------
 4 files changed, 70 insertions(+), 162 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/2f3a1fd5/include/couch_db.hrl
----------------------------------------------------------------------
diff --git a/include/couch_db.hrl b/include/couch_db.hrl
index bbf74c3..d77a2aa 100644
--- a/include/couch_db.hrl
+++ b/include/couch_db.hrl
@@ -129,40 +129,13 @@
     handler
     }).
 
-% This should be updated anytime a header change happens that requires more
-% than filling in new defaults.
-%
-% As long the changes are limited to new header fields (with inline
-% defaults) added to the end of the record, then there is no need to increment
-% the disk revision number.
-%
-% 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, 6).
-
--record(db_header,
-    {disk_version = ?LATEST_DISK_VERSION,
-     update_seq = 0,
-     unused = 0,
-     id_tree_state = nil,
-     seq_tree_state = nil,
-     local_tree_state = nil,
-     purge_seq = 0,
-     purged_docs = nil,
-     security_ptr = nil,
-     revs_limit = 1000,
-     uuid = nil,
-     epochs = nil
-    }).
-
 -record(db,
     {main_pid = nil,
     compactor_pid = nil,
     instance_start_time, % number of microsecs since jan 1 1970 as a binary string
     fd,
     fd_monitor,
-    header = #db_header{},
+    header = couch_db_header:new(),
     committed_update_seq,
     id_tree,
     seq_tree,

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/2f3a1fd5/src/couch_db.erl
----------------------------------------------------------------------
diff --git a/src/couch_db.erl b/src/couch_db.erl
index ab5e3da..1487681 100644
--- a/src/couch_db.erl
+++ b/src/couch_db.erl
@@ -283,27 +283,30 @@ get_committed_update_seq(#db{committed_update_seq=Seq}) ->
 get_update_seq(#db{update_seq=Seq})->
     Seq.
 
-get_purge_seq(#db{header=#db_header{purge_seq=PurgeSeq}})->
-    PurgeSeq.
-
-get_last_purged(#db{header=#db_header{purged_docs=nil}}) ->
-    {ok, []};
-get_last_purged(#db{fd=Fd, header=#db_header{purged_docs=PurgedPointer}}) ->
-    couch_file:pread_term(Fd, PurgedPointer).
+get_purge_seq(#db{}=Db) ->
+    couch_db_header:purge_seq(Db#db.header).
+
+get_last_purged(#db{}=Db) ->
+    case couch_db_header:purged_docs(Db#db.header) of
+        nil ->
+            {ok, []};
+        Pointer ->
+            couch_file:pread_term(Db#db.fd, Pointer)
+    end.
 
 get_doc_count(Db) ->
     {ok, {Count, _, _}} = couch_btree:full_reduce(Db#db.id_tree),
     {ok, Count}.
 
-get_uuid(#db{header=Header}) ->
-    Header#header.uuid.
+get_uuid(#db{}=Db) ->
+    couch_db_header:uuid(Db#db.header).
 
-get_epochs(#db{header=Header}) ->
-    Header#header.epochs.
+get_epochs(#db{}=Db) ->
+    couch_db_header:epochs(Db#db.header).
 
 get_db_info(Db) ->
     #db{fd=Fd,
-        header=#db_header{disk_version=DiskVersion},
+        header=Header,
         compactor_pid=Compactor,
         update_seq=SeqNum,
         name=Name,
@@ -315,6 +318,7 @@ get_db_info(Db) ->
     } = Db,
     {ok, Size} = couch_file:bytes(Fd),
     {ok, DbReduction} = couch_btree:full_reduce(IdBtree),
+    DiskVersion = couch_db_header:disk_version(Header),
     Uuid = case get_uuid(Db) of
         undefined -> null;
         Uuid0 -> Uuid0

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/2f3a1fd5/src/couch_db_header.erl
----------------------------------------------------------------------
diff --git a/src/couch_db_header.erl b/src/couch_db_header.erl
index e0d7f4b..ff771d1 100644
--- a/src/couch_db_header.erl
+++ b/src/couch_db_header.erl
@@ -187,12 +187,14 @@ upgrade_tuple(Old) when is_tuple(Old) ->
     end,
     New.
 
+-define(OLD_DISK_VERSION_ERROR,
+    "Database files from versions smaller than 0.10.0 are no longer supported").
 
 upgrade_disk_version(#db_header{}=Header) ->
     case element(2, Header) of
-        1 -> Header#db_header{unused = 0, security_ptr = nil}; % 0.9]
-        2 -> Header#db_header{unused = 0, security_ptr = nil}; % (0.9 - 0.10)
-        3 -> Header#db_header{security_ptr = nil}; % (0.9 - 0.10)
+        1 -> throw({database_disk_version_error, ?OLD_DISK_VERSION_ERROR});
+        2 -> throw({database_disk_version_error, ?OLD_DISK_VERSION_ERROR});
+        3 -> throw({database_disk_version_error, ?OLD_DISK_VERSION_ERROR});
         4 -> Header#db_header{security_ptr = nil}; % [0.10 - 0.11)
         5 -> Header; % pre 1.2
         ?LATEST_DISK_VERSION -> Header;

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/2f3a1fd5/src/couch_db_updater.erl
----------------------------------------------------------------------
diff --git a/src/couch_db_updater.erl b/src/couch_db_updater.erl
index eadd6c7..22384dc 100644
--- a/src/couch_db_updater.erl
+++ b/src/couch_db_updater.erl
@@ -37,7 +37,7 @@ init({DbName, Filepath, Fd, Options}) ->
     case lists:member(create, Options) of
     true ->
         % create a new header and writes it to the file
-        Header =  #db_header{},
+        Header =  couch_db_header:new(),
         ok = couch_file:write_header(Fd, Header),
         % delete any old compaction files that might be hanging around
         RootDir = config:get("couchdb", "database_dir", "."),
@@ -50,7 +50,7 @@ init({DbName, Filepath, Fd, Options}) ->
             ok;
         no_valid_header ->
             % create a new header and writes it to the file
-            Header =  #db_header{},
+            Header =  couch_db_header:new(),
             ok = couch_file:write_header(Fd, Header),
             % delete any old compaction files that might be hanging around
             file:delete(Filepath ++ ".compact"),
@@ -133,7 +133,7 @@ handle_call({purge_docs, IdRevs}, _From, Db) ->
         id_tree = DocInfoByIdBTree,
         seq_tree = DocInfoBySeqBTree,
         update_seq = LastSeq,
-        header = Header = #db_header{purge_seq=PurgeSeq},
+        header = Header,
         compression = Comp
         } = Db,
     DocLookups = couch_btree:lookup(DocInfoByIdBTree,
@@ -181,16 +181,20 @@ handle_call({purge_docs, IdRevs}, _From, Db) ->
     {ok, Pointer, _} = couch_file:append_term(
             Fd, IdRevsPurged, [{compression, Comp}]),
 
+    NewHeader = couch_db_header:set(Header, [
+        {purge_seq, couch_db_header:purge_seq(Header) + 1},
+        {purged_docs, Pointer}
+    ]),
     Db2 = commit_data(
         Db#db{
             id_tree = DocInfoByIdBTree2,
             seq_tree = DocInfoBySeqBTree2,
             update_seq = NewSeq + 1,
-            header=Header#db_header{purge_seq=PurgeSeq+1, purged_docs=Pointer}}),
+            header=NewHeader}),
 
     ok = gen_server:call(couch_server, {db_updated, Db2}, infinity),
     couch_event:notify(Db#db.name, updated),
-    {reply, {ok, (Db2#db.header)#db_header.purge_seq, IdRevsPurged}, Db2}.
+    {reply, {ok, couch_db_header:purge_seq(NewHeader), IdRevsPurged}, Db2}.
 
 
 handle_cast({load_validation_funs, ValidationFuns}, Db) ->
@@ -444,34 +448,8 @@ btree_by_seq_reduce(reduce, DocInfos) ->
 btree_by_seq_reduce(rereduce, Reds) ->
     lists:sum(Reds).
 
-simple_upgrade_record(Old, New) when tuple_size(Old) < tuple_size(New) ->
-    OldSz = tuple_size(Old),
-    NewValuesTail =
-        lists:sublist(tuple_to_list(New), OldSz + 1, tuple_size(New) - OldSz),
-    list_to_tuple(tuple_to_list(Old) ++ NewValuesTail);
-simple_upgrade_record(Old, _New) ->
-    Old.
-
--define(OLD_DISK_VERSION_ERROR,
-    "Database files from versions smaller than 0.10.0 are no longer supported").
-
 init_db(DbName, Filepath, Fd, Header0, Options) ->
-    Header1 = simple_upgrade_record(Header0, #db_header{}),
-    Header2 =
-    case element(2, Header1) of
-    1 -> throw({database_disk_version_error, ?OLD_DISK_VERSION_ERROR});
-    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,
-
-    Header = Header2#db_header{
-        uuid = initialize_uuid(Header2),
-        epochs = initialize_epochs(Header2)
-    },
+    Header = couch_db_header:upgrade(Header0),
 
     {ok, FsyncOptions} = couch_util:parse_term(
             config:get("couchdb", "fsync_options",
@@ -484,19 +462,22 @@ init_db(DbName, Filepath, Fd, Header0, Options) ->
 
     Compression = couch_compress:get_compression_method(),
 
-    {ok, IdBtree} = couch_btree:open(Header#db_header.id_tree_state, Fd,
+    IdTreeState = couch_db_header:id_tree_state(Header),
+    SeqTreeState = couch_db_header:seq_tree_state(Header),
+    LocalTreeState = couch_db_header:local_tree_state(Header),
+    {ok, IdBtree} = couch_btree:open(IdTreeState, Fd,
         [{split, fun ?MODULE:btree_by_id_split/1},
         {join, fun ?MODULE:btree_by_id_join/2},
         {reduce, fun ?MODULE:btree_by_id_reduce/2},
         {compression, Compression}]),
-    {ok, SeqBtree} = couch_btree:open(Header#db_header.seq_tree_state, Fd,
+    {ok, SeqBtree} = couch_btree:open(SeqTreeState, Fd,
             [{split, fun ?MODULE:btree_by_seq_split/1},
             {join, fun ?MODULE:btree_by_seq_join/2},
             {reduce, fun ?MODULE:btree_by_seq_reduce/2},
             {compression, Compression}]),
-    {ok, LocalDocsBtree} = couch_btree:open(Header#db_header.local_tree_state, Fd,
+    {ok, LocalDocsBtree} = couch_btree:open(LocalTreeState, Fd,
         [{compression, Compression}]),
-    case Header#db_header.security_ptr of
+    case couch_db_header:security_ptr(Header) of
     nil ->
         Security = [],
         SecurityPtr = nil;
@@ -515,27 +496,27 @@ init_db(DbName, Filepath, Fd, Header0, Options) ->
         id_tree = IdBtree,
         seq_tree = SeqBtree,
         local_tree = LocalDocsBtree,
-        committed_update_seq = Header#db_header.update_seq,
-        update_seq = Header#db_header.update_seq,
+        committed_update_seq = couch_db_header:update_seq(Header),
+        update_seq = couch_db_header:update_seq(Header),
         name = DbName,
         filepath = Filepath,
         security = Security,
         security_ptr = SecurityPtr,
         instance_start_time = StartTime,
-        revs_limit = Header#db_header.revs_limit,
+        revs_limit = couch_db_header:revs_limit(Header),
         fsync_options = FsyncOptions,
         options = Options,
         compression = Compression,
         before_doc_update = couch_util:get_value(before_doc_update, Options, nil),
         after_doc_read = couch_util:get_value(after_doc_read, Options, nil)
-        },
+    },
 
     % If we just created a new UUID while upgrading a
     % database then we want to flush that to disk or
     % we risk sending out the uuid and having the db
     % crash which would result in it generating a new
     % uuid each time it was reopened.
-    case Header /= Header2 of
+    case Header /= Header0 of
         true ->
             sync_header(Db, Header);
         false ->
@@ -815,13 +796,14 @@ update_local_docs(#db{local_tree=Btree}=Db, Docs) ->
     {ok, Db#db{local_tree = Btree2}}.
 
 db_to_header(Db, Header) ->
-    Header#db_header{
-        update_seq = Db#db.update_seq,
-        seq_tree_state = couch_btree:get_state(Db#db.seq_tree),
-        id_tree_state = couch_btree:get_state(Db#db.id_tree),
-        local_tree_state = couch_btree:get_state(Db#db.local_tree),
-        security_ptr = Db#db.security_ptr,
-        revs_limit = Db#db.revs_limit}.
+    couch_db_header:set(Header, [
+        {update_seq, Db#db.update_seq},
+        {seq_tree_state, couch_btree:get_state(Db#db.seq_tree)},
+        {id_tree_state, couch_btree:get_state(Db#db.id_tree)},
+        {local_tree_state, couch_btree:get_state(Db#db.local_tree)},
+        {security_ptr, Db#db.security_ptr},
+        {revs_limit, Db#db.revs_limit}
+    ]).
 
 commit_data(Db) ->
     commit_data(Db, false).
@@ -842,7 +824,7 @@ commit_data(Db, _) ->
         NewHeader -> sync_header(Db, NewHeader)
     end.
 
-sync_header(Db, NewHeader0) ->
+sync_header(Db, NewHeader) ->
     #db{
         fd = Fd,
         filepath = FilePath,
@@ -850,8 +832,6 @@ sync_header(Db, NewHeader0) ->
         waiting_delayed_commit = Timer
     } = Db,
 
-    NewHeader = update_epochs(NewHeader0),
-
     if is_reference(Timer) -> erlang:cancel_timer(Timer); true -> ok end,
 
     Before = lists:member(before_header, FsyncOptions),
@@ -1072,34 +1052,28 @@ start_copy_compact(#db{}=Db) ->
     gen_server:cast(Db#db.main_pid, {compact_done, DName}).
 
 
-open_compaction_files(DbName, Header, DbFilePath, Options) ->
+open_compaction_files(DbName, SrcHdr, DbFilePath, Options) ->
     DataFile = DbFilePath ++ ".compact.data",
     MetaFile = DbFilePath ++ ".compact.meta",
     {ok, DataFd, DataHdr} = open_compaction_file(DataFile),
     {ok, MetaFd, MetaHdr} = open_compaction_file(MetaFile),
+    DataHdrIsDbHdr = couch_db_header:is_header(DataHdr),
     case {DataHdr, MetaHdr} of
         {#comp_header{}=A, #comp_header{}=A} ->
             DbHeader = A#comp_header.db_header,
             Db0 = init_db(DbName, DataFile, DataFd, DbHeader, Options),
             Db1 = bind_emsort(Db0, MetaFd, A#comp_header.meta_state),
             {ok, Db1, DataFile, DataFd, MetaFd, Db0#db.id_tree};
-        {#db_header{}, _} ->
-            NewHeader = #db_header{
-                uuid = DataHdr#db_header.uuid,
-                epochs = DataHdr#db_header.epochs
-            },
-            ok = reset_compaction_file(MetaFd, NewHeader),
+        _ when DataHdrIsDbHdr ->
+            ok = reset_compaction_file(MetaFd, couch_db_header:from(SrcHdr)),
             Db0 = init_db(DbName, DataFile, DataFd, DataHdr, Options),
             Db1 = bind_emsort(Db0, MetaFd, nil),
             {ok, Db1, DataFile, DataFd, MetaFd, Db0#db.id_tree};
         _ ->
-            NewHeader = #db_header{
-                uuid = Header#db_header.uuid,
-                epochs = Header#db_header.epochs
-            },
-            ok = reset_compaction_file(DataFd, NewHeader),
-            ok = reset_compaction_file(MetaFd, NewHeader),
-            Db0 = init_db(DbName, DataFile, DataFd, NewHeader, Options),
+            Header = couch_db_header:from(SrcHdr),
+            ok = reset_compaction_file(DataFd, Header),
+            ok = reset_compaction_file(MetaFd, Header),
+            Db0 = init_db(DbName, DataFile, DataFd, Header, Options),
             Db1 = bind_emsort(Db0, MetaFd, nil),
             {ok, Db1, DataFile, DataFd, MetaFd, nil}
     end.
@@ -1126,16 +1100,16 @@ reset_compaction_file(Fd, Header) ->
 copy_purge_info(OldDb, NewDb) ->
     OldHdr = OldDb#db.header,
     NewHdr = NewDb#db.header,
-    if OldHdr#db_header.purge_seq > 0 ->
+    OldPurgeSeq = couch_db_header:purge_seq(OldHdr),
+    if OldPurgeSeq > 0 ->
         {ok, PurgedIdsRevs} = couch_db:get_last_purged(OldDb),
         Opts = [{compression, NewDb#db.compression}],
         {ok, Ptr, _} = couch_file:append_term(NewDb#db.fd, PurgedIdsRevs, Opts),
-        NewDb#db{
-            header=NewHdr#db_header{
-                purge_seq=OldHdr#db_header.purge_seq,
-                purged_docs=Ptr
-            }
-        };
+        NewNewHdr = couch_db_header:set(NewHdr, [
+            {purge_seq, OldPurgeSeq},
+            {purged_docs, Ptr}
+        ]),
+        NewDb#db{header = NewNewHdr};
     true ->
         NewDb
     end.
@@ -1154,7 +1128,7 @@ commit_compaction_data(#db{header=OldHeader}=Db0, Fd) ->
     % replace the logic to commit and fsync to a specific
     % fd instead of the Filepath stuff that commit_data/2
     % does.
-    DataState = OldHeader#db_header.id_tree_state,
+    DataState = couch_db_header:id_tree_state(OldHeader),
     MetaFd = couch_emsort:get_fd(Db0#db.id_tree),
     MetaState = couch_emsort:get_state(Db0#db.id_tree),
     Db1 = bind_id_tree(Db0, Db0#db.fd, DataState),
@@ -1197,7 +1171,7 @@ sort_meta_data(Db0) ->
 
 copy_meta_data(#db{fd=Fd, header=Header}=Db) ->
     Src = Db#db.id_tree,
-    DstState = Header#db_header.id_tree_state,
+    DstState = couch_db_header:id_tree_state(Header),
     {ok, IdTree0} = couch_btree:open(DstState, Fd, [
         {split, fun ?MODULE:btree_by_id_split/1},
         {join, fun ?MODULE:btree_by_id_join/2},
@@ -1299,48 +1273,3 @@ make_doc_summary(#db{compression = Comp}, {Body0, Atts0}) ->
     end,
     SummaryBin = ?term_to_bin({Body, Atts}),
     couch_file:assemble_file_chunk(SummaryBin, couch_util:md5(SummaryBin)).
-
-initialize_uuid(#db_header{uuid=nil}) ->
-    couch_uuids:random();
-initialize_uuid(#db_header{uuid=Uuid}) ->
-    Uuid.
-
-initialize_epochs(#db_header{epochs=nil}=Db) ->
-    [{node(), Db#db_header.update_seq}];
-initialize_epochs(#db_header{epochs=Epochs}) ->
-    Epochs.
-
-update_epochs(Db) ->
-    case Db#db.epochs of
-        [{Node, _} | _] when Node =:= node() ->
-            Db#db.epochs;
-        Epochs when is_list(Epochs) ->
-            %% Mark the sequence where this node took over.
-            [{node(), Db#db.update_seq} | Epochs]
-    end.
-
--ifdef(TEST).
--include_lib("eunit/include/eunit.hrl").
-
-initialize_uuid_is_stable_test() ->
-    %% UUID was initialized.
-    ?assertMatch(<<_:32/binary>>, initialize_uuid(#db_header{})),
-    %% UUID was preserved.
-    ?assertEqual(foo, initialize_uuid(#db_header{uuid=foo})).
-
-initialize_epochs_is_stable_test() ->
-    %% Epochs were initialized.
-    ?assertMatch([{'nonode@nohost', 0}], initialize_epochs(#db_header{})),
-    %% Epochs are preserved.
-    ?assertMatch(foo, initialize_epochs(#db_header{epochs=foo})).
-
-update_epochs_test() ->
-    %% Epochs are not extended if node stays the same.
-    ?assertMatch([{'nonode@nohost', 0}],
-                 update_epochs(#db{epochs=[{'nonode@nohost', 0}]})),
-
-    %% Epochs are extended if node changes.
-    ?assertMatch([{'nonode@nohost', 1}, {foo, 0}],
-                 update_epochs(#db{update_seq=1, epochs=[{foo, 0}]})).
-
--endif.


[29/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Silence log spam when couch_file closes

If a couch_file closes the couch_db_updater will go along with it with a
great deal of fanfare when the actual interesting error message is from
couch_file. Rather than blow up trying to close a dead process we just
exit more quietly (albeit still with a log message).

The observed cause of this is when a database is opened before
couch_stats_collector boots and starts tracking processes. A minute
after the node boots we get a number of couch_db_updaters that die
because their fd closed due to a lack of monitor from
couch_stats_collector.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/ef1af372
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/ef1af372
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/ef1af372

Branch: refs/heads/windsor-merge-300
Commit: ef1af372268318dca1b092e9cb20ddc6152580ab
Parents: bc4ec1b
Author: Paul J. Davis <pa...@gmail.com>
Authored: Fri Jun 14 19:46:14 2013 -0500
Committer: Robert Newson <rn...@apache.org>
Committed: Tue Aug 5 16:40:37 2014 +0100

----------------------------------------------------------------------
 src/couch_db_updater.erl | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/ef1af372/src/couch_db_updater.erl
----------------------------------------------------------------------
diff --git a/src/couch_db_updater.erl b/src/couch_db_updater.erl
index 22384dc..5cdb876 100644
--- a/src/couch_db_updater.erl
+++ b/src/couch_db_updater.erl
@@ -66,13 +66,10 @@ init({DbName, Filepath, Fd, Options}) ->
 
 
 terminate(_Reason, Db) ->
-    % If the reason we died is becuase our fd disappeared
+    % If the reason we died is because our fd disappeared
     % then we don't need to try closing it again.
-    case Db#db.fd of
-        Pid when is_pid(Pid) ->
-            ok = couch_file:close(Db#db.fd);
-        _ ->
-            ok
+    if Db#db.fd_monitor == closed -> ok; true ->
+        ok = couch_file:close(Db#db.fd)
     end,
     couch_util:shutdown_sync(Db#db.compactor_pid),
     couch_util:shutdown_sync(Db#db.fd),
@@ -317,7 +314,7 @@ handle_info({'EXIT', _Pid, Reason}, Db) ->
     {stop, Reason, Db};
 handle_info({'DOWN', Ref, _, _, Reason}, #db{fd_monitor=Ref, name=Name} = Db) ->
     ?LOG_ERROR("DB ~s shutting down - Fd ~p", [Name, Reason]),
-    {stop, normal, Db#db{fd=undefined, fd_monitor=undefined}}.
+    {stop, normal, Db#db{fd=undefined, fd_monitor=closed}}.
 
 code_change(_OldVsn, State, _Extra) ->
     {ok, State}.


[40/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Remember compacted_seq

BugzID: 22917


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/5be71f77
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/5be71f77
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/5be71f77

Branch: refs/heads/windsor-merge-300
Commit: 5be71f77a9c58f195a16b2319de94ad64edb53a5
Parents: 1313221
Author: Robert Newson <ro...@cloudant.com>
Authored: Mon Sep 9 21:40:33 2013 +0100
Committer: Robert Newson <rn...@apache.org>
Committed: Wed Aug 6 11:57:41 2014 +0100

----------------------------------------------------------------------
 src/couch_db.erl         | 10 +++++++++-
 src/couch_db_header.erl  | 25 +++++++++++++++++++++----
 src/couch_db_updater.erl |  5 ++++-
 3 files changed, 34 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/5be71f77/src/couch_db.erl
----------------------------------------------------------------------
diff --git a/src/couch_db.erl b/src/couch_db.erl
index 4adb128..836b468 100644
--- a/src/couch_db.erl
+++ b/src/couch_db.erl
@@ -21,7 +21,7 @@
 -export([open_doc/2,open_doc/3,open_doc_revs/4]).
 -export([set_revs_limit/2,get_revs_limit/1]).
 -export([get_missing_revs/2,name/1,get_update_seq/1,get_committed_update_seq/1]).
--export([get_uuid/1, get_epochs/1]).
+-export([get_uuid/1, get_epochs/1, get_compacted_seq/1]).
 -export([enum_docs/4,enum_docs_since/5]).
 -export([enum_docs_since_reduce_to_count/1,enum_docs_reduce_to_count/1]).
 -export([increment_update_seq/1,get_purge_seq/1,purge_docs/2,get_last_purged/1]).
@@ -304,6 +304,9 @@ get_uuid(#db{}=Db) ->
 get_epochs(#db{}=Db) ->
     couch_db_header:epochs(Db#db.header).
 
+get_compacted_seq(#db{}=Db) ->
+    couch_db_header:compacted_seq(Db#db.header).
+
 get_db_info(Db) ->
     #db{fd=Fd,
         header=Header,
@@ -323,6 +326,10 @@ get_db_info(Db) ->
         undefined -> null;
         Uuid0 -> Uuid0
     end,
+    CompactedSeq = case get_compacted_seq(Db) of
+        undefined -> null;
+        Else1 -> Else1
+    end,
     InfoList = [
         {db_name, Name},
         {doc_count, element(1, DbReduction)},
@@ -335,6 +342,7 @@ get_db_info(Db) ->
         {instance_start_time, StartTime},
         {disk_format_version, DiskVersion},
         {committed_update_seq, CommittedUpdateSeq},
+        {compacted_seq, CompactedSeq},
         {uuid, Uuid}
         ],
     {ok, InfoList}.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/5be71f77/src/couch_db_header.erl
----------------------------------------------------------------------
diff --git a/src/couch_db_header.erl b/src/couch_db_header.erl
index c23cf38..b7c93d9 100644
--- a/src/couch_db_header.erl
+++ b/src/couch_db_header.erl
@@ -32,7 +32,8 @@
     security_ptr/1,
     revs_limit/1,
     uuid/1,
-    epochs/1
+    epochs/1,
+    compacted_seq/1
 ]).
 
 
@@ -60,7 +61,8 @@
     security_ptr = nil,
     revs_limit = 1000,
     uuid,
-    epochs
+    epochs,
+    compacted_seq
 }).
 
 
@@ -75,7 +77,8 @@ from(Header0) ->
     Header = upgrade(Header0),
     #db_header{
         uuid = Header#db_header.uuid,
-        epochs = Header#db_header.epochs
+        epochs = Header#db_header.epochs,
+        compacted_seq = Header#db_header.compacted_seq
     }.
 
 
@@ -93,7 +96,8 @@ upgrade(Header) ->
         fun upgrade_tuple/1,
         fun upgrade_disk_version/1,
         fun upgrade_uuid/1,
-        fun upgrade_epochs/1
+        fun upgrade_epochs/1,
+        fun upgrade_compacted_seq/1
     ],
     lists:foldl(fun(F, HdrAcc) ->
         F(HdrAcc)
@@ -154,6 +158,10 @@ epochs(Header) ->
     get_field(Header, epochs).
 
 
+compacted_seq(Header) ->
+    get_field(Header, compacted_seq).
+
+
 get_field(Header, Field) ->
     Idx = index(Field),
     case Idx > tuple_size(Header) of
@@ -267,6 +275,15 @@ remove_dup_epochs([{N1, S1}, {N2, S2} | Rest]) ->
     remove_dup_epochs([{N1, S1} | remove_dup_epochs([{N2, S2} | Rest])]).
 
 
+upgrade_compacted_seq(#db_header{}=Header) ->
+    case Header#db_header.compacted_seq of
+        undefined ->
+            Header#db_header{compacted_seq=0};
+        _ ->
+            Header
+    end.
+
+
 -ifdef(TEST).
 -include_lib("eunit/include/eunit.hrl").
 

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/5be71f77/src/couch_db_updater.erl
----------------------------------------------------------------------
diff --git a/src/couch_db_updater.erl b/src/couch_db_updater.erl
index e01df83..6979256 100644
--- a/src/couch_db_updater.erl
+++ b/src/couch_db_updater.erl
@@ -212,7 +212,10 @@ handle_cast(start_compact, Db) ->
     end;
 handle_cast({compact_done, CompactFilepath}, #db{filepath=Filepath}=Db) ->
     {ok, NewFd} = couch_file:open(CompactFilepath),
-    {ok, NewHeader} = couch_file:read_header(NewFd),
+    {ok, NewHeader0} = couch_file:read_header(NewFd),
+    NewHeader = couch_db_header:set(NewHeader0, [
+        {compacted_seq, Db#db.update_seq}
+    ]),
     #db{update_seq=NewSeq} = NewDb =
         init_db(Db#db.name, Filepath, NewFd, NewHeader, Db#db.options),
     unlink(NewFd),


[45/48] couch commit: updated refs/heads/windsor-merge-300 to 2c2f53e

Posted by rn...@apache.org.
Use the new b64url NIF

This is uses the new b64url NIF for performaing Base64 URL encoding and
decoding.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/aa35d4f9
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/aa35d4f9
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/aa35d4f9

Branch: refs/heads/windsor-merge-300
Commit: aa35d4f90205474fc0c5bc817227223f1ba53f6d
Parents: dfa15da
Author: Paul J. Davis <pa...@gmail.com>
Authored: Thu Nov 21 13:47:32 2013 -0600
Committer: Robert Newson <rn...@apache.org>
Committed: Wed Aug 6 12:49:04 2014 +0100

----------------------------------------------------------------------
 src/couch.app.src  | 2 +-
 src/couch_util.erl | 9 ++-------
 2 files changed, 3 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/aa35d4f9/src/couch.app.src
----------------------------------------------------------------------
diff --git a/src/couch.app.src b/src/couch.app.src
index 8007278..30f3b2a 100644
--- a/src/couch.app.src
+++ b/src/couch.app.src
@@ -29,5 +29,5 @@
     ]},
     {mod, {couch_app, []}},
     {applications, [kernel, stdlib, crypto, sasl, inets, oauth, ibrowse,
-        mochiweb, ssl, couch_log, couch_event]}
+        mochiweb, ssl, couch_log, couch_event, b64url]}
 ]}.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/aa35d4f9/src/couch_util.erl
----------------------------------------------------------------------
diff --git a/src/couch_util.erl b/src/couch_util.erl
index 20dfdaf..6f84ac7 100644
--- a/src/couch_util.erl
+++ b/src/couch_util.erl
@@ -307,15 +307,10 @@ should_flush(MemThreshHold) ->
     true -> false end.
 
 encodeBase64Url(Url) ->
-    Url1 = re:replace(base64:encode(Url), ["=+", $$], ""),
-    Url2 = re:replace(Url1, "/", "_", [global]),
-    re:replace(Url2, "\\+", "-", [global, {return, binary}]).
+    b64url:encode(Url).
 
 decodeBase64Url(Url64) ->
-    Url1 = re:replace(Url64, "-", "+", [global]),
-    Url2 = re:replace(Url1, "_", "/", [global]),
-    Padding = lists:duplicate((4 - iolist_size(Url2) rem 4) rem 4, $=),
-    base64:decode(iolist_to_binary([Url2, Padding])).
+    b64url:decode(Url64).
 
 dict_find(Key, Dict, DefaultValue) ->
     case dict:find(Key, Dict) of