You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by fd...@apache.org on 2011/05/31 22:32:56 UTC

svn commit: r1129906 - in /couchdb/trunk: share/www/script/test/replication.js src/couchdb/couch_api_wrap.erl src/couchdb/couch_replicator_doc_copier.erl

Author: fdmanana
Date: Tue May 31 20:32:56 2011
New Revision: 1129906

URL: http://svn.apache.org/viewvc?rev=1129906&view=rev
Log:
Improve error logging on replication write failures

If an error happens when writing a document to the target endpoint,
log it's ID, revision, error and error reason. Also added a few tests
to cover the cases where the target has validate_doc_update functions.


Modified:
    couchdb/trunk/share/www/script/test/replication.js
    couchdb/trunk/src/couchdb/couch_api_wrap.erl
    couchdb/trunk/src/couchdb/couch_replicator_doc_copier.erl

Modified: couchdb/trunk/share/www/script/test/replication.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/replication.js?rev=1129906&r1=1129905&r2=1129906&view=diff
==============================================================================
--- couchdb/trunk/share/www/script/test/replication.js (original)
+++ couchdb/trunk/share/www/script/test/replication.js Tue May 31 20:32:56 2011
@@ -504,6 +504,53 @@ couchTests.replication = function(debug)
   }
 
 
+  // test errors due to doc validate_doc_update functions in the target endpoint
+  docs = makeDocs(1, 8);
+  docs[2]["_attachments"] = {
+    "hello.txt": {
+      "content_type": "text/plain",
+      "data": "aGVsbG8gd29ybGQ="  // base64:encode("hello world")
+    }
+  };
+  var ddoc = {
+    _id: "_design/test",
+    language: "javascript",
+    validate_doc_update: (function(newDoc, oldDoc, userCtx, secObj) {
+      if ((newDoc.integer % 2) !== 0) {
+        throw {forbidden: "I only like multiples of 2."};
+      }
+    }).toString()
+  };
+
+  for (i = 0; i < dbPairs.length; i++) {
+    populateDb(sourceDb, docs);
+    populateDb(targetDb, [ddoc]);
+
+    repResult = CouchDB.replicate(
+      dbPairs[i].source,
+      dbPairs[i].target
+    );
+    TEquals(true, repResult.ok);
+    TEquals(7, repResult.history[0].missing_checked);
+    TEquals(7, repResult.history[0].missing_found);
+    TEquals(7, repResult.history[0].docs_read);
+    TEquals(3, repResult.history[0].docs_written);
+    TEquals(4, repResult.history[0].doc_write_failures);
+
+    for (j = 0; j < docs.length; j++) {
+      doc = docs[j];
+      copy = targetDb.open(doc._id);
+
+      if (doc.integer % 2 === 0) {
+        T(copy !== null);
+        TEquals(copy.integer, doc.integer);
+      } else {
+        T(copy === null);
+      }
+    }
+  }
+
+
   // test create_target option
   docs = makeDocs(1, 2);
 

Modified: couchdb/trunk/src/couchdb/couch_api_wrap.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_api_wrap.erl?rev=1129906&r1=1129905&r2=1129906&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_api_wrap.erl (original)
+++ couchdb/trunk/src/couchdb/couch_api_wrap.erl Tue May 31 20:32:56 2011
@@ -236,6 +236,8 @@ update_doc(#httpdb{} = HttpDb, #doc{id =
                 case {Code, get_value(<<"error">>, Props)} of
                 {401, <<"unauthorized">>} ->
                     throw({unauthorized, get_value(<<"reason">>, Props)});
+                {403, <<"forbidden">>} ->
+                    throw({forbidden, get_value(<<"reason">>, Props)});
                 {412, <<"missing_stub">>} ->
                     throw({missing_stub, get_value(<<"reason">>, Props)});
                 {_, Error} ->
@@ -665,9 +667,10 @@ bulk_results_to_errors(Docs, {ok, Result
     lists:reverse(lists:foldl(
         fun({_, {ok, _}}, Acc) ->
             Acc;
-        ({#doc{id = Id}, Error}, Acc) ->
-            {_, Error, _Reason} = couch_httpd:error_info(Error),
-            [ {[{<<"id">>, Id}, {<<"error">>, Error}]} | Acc ]
+        ({#doc{id = Id, revs = {Pos, [RevId | _]}}, Error}, Acc) ->
+            {_, Error, Reason} = couch_httpd:error_info(Error),
+            [ {[{id, Id}, {rev, rev_to_str({Pos, RevId})},
+                {error, Error}, {reason, Reason}]} | Acc ]
         end,
         [], lists:zip(Docs, Results)));
 
@@ -676,9 +679,9 @@ bulk_results_to_errors(Docs, {ok, Result
 
 bulk_results_to_errors(_Docs, {aborted, Results}, interactive_edit) ->
     lists:map(
-        fun({{Id, _Rev}, Err}) ->
-            {_, Error, _Reason} = couch_httpd:error_info(Err),
-            {[{<<"id">>, Id}, {<<"error">>, Error}]}
+        fun({{Id, Rev}, Err}) ->
+            {_, Error, Reason} = couch_httpd:error_info(Err),
+            {[{id, Id}, {rev, rev_to_str(Rev)}, {error, Error}, {reason, Reason}]}
         end,
         Results);
 
@@ -690,12 +693,21 @@ bulk_results_to_errors(_Docs, Results, r
                 Acc;
             Error ->
                 Id = get_value(<<"id">>, Props, get_value(id, Props)),
-                [ {[{<<"id">>, Id}, {<<"error">>, Error}]} | Acc ]
+                Rev = get_value(<<"rev">>, Props, get_value(rev, Props)),
+                Reason = get_value(<<"reason">>, Props, get_value(reason, Props)),
+                [ {[{id, Id}, {rev, rev_to_str(Rev)},
+                    {error, Error}, {reason, Reason}]} | Acc ]
             end
         end,
         [], Results)).
 
 
+rev_to_str({_Pos, _Id} = Rev) ->
+    couch_doc:rev_to_str(Rev);
+rev_to_str(Rev) ->
+    Rev.
+
+
 stream_doc({JsonBytes, Atts, Boundary, Len}) ->
     case erlang:erase({doc_streamer, Boundary}) of
     Pid when is_pid(Pid) ->

Modified: couchdb/trunk/src/couchdb/couch_replicator_doc_copier.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_replicator_doc_copier.erl?rev=1129906&r1=1129905&r2=1129906&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_replicator_doc_copier.erl (original)
+++ couchdb/trunk/src/couchdb/couch_replicator_doc_copier.erl Tue May 31 20:32:56 2011
@@ -36,6 +36,12 @@
     start_db_compaction_notifier/2,
     stop_db_compaction_notifier/1
 ]).
+-import(couch_util, [
+    to_binary/1,
+    get_value/2,
+    get_value/3
+]).
+
 
 -record(batch, {
     docs = [],
@@ -490,15 +496,15 @@ flush_docs(Target, DocList) ->
         Target, DocList, [delay_commit], replicated_changes),
     DbUri = couch_api_wrap:db_uri(Target),
     lists:foreach(
-        fun({[ {<<"id">>, Id}, {<<"error">>, <<"unauthorized">>} ]}) ->
-                ?LOG_ERROR("Replicator: unauthorized to write document"
-                    " `~s` to `~s`", [Id, DbUri]);
-            (_) ->
-                ok
+        fun({Props}) ->
+            ?LOG_ERROR("Replicator: couldn't write document `~s`, revision `~s`,"
+                " to target database `~s`. Error: `~s`, reason: `~s`.",
+                [get_value(id, Props, ""), get_value(rev, Props, ""), DbUri,
+                    get_value(error, Props, ""), get_value(reason, Props, "")])
         end, Errors),
     {length(DocList) - length(Errors), length(Errors)}.
 
-flush_doc(Target, #doc{id = Id} = Doc) ->
+flush_doc(Target, #doc{id = Id, revs = {Pos, [RevId | _]}} = Doc) ->
     try couch_api_wrap:update_doc(Target, Doc, [], replicated_changes) of
     {ok, _} ->
         ok;
@@ -507,8 +513,16 @@ flush_doc(Target, #doc{id = Id} = Doc) -
             [Id, couch_api_wrap:db_uri(Target), couch_util:to_binary(Error)]),
         Error
     catch
-    throw:{unauthorized, _} ->
-        ?LOG_ERROR("Replicator: unauthorized to write document `~s` to `~s`",
-            [Id, couch_api_wrap:db_uri(Target)]),
-        {error, unauthorized}
+    throw:{Error, Reason} ->
+        ?LOG_ERROR("Replicator: couldn't write document `~s`, revision `~s`,"
+            " to target database `~s`. Error: `~s`, reason: `~s`.",
+            [Id, couch_doc:rev_to_str({Pos, RevId}),
+                couch_api_wrap:db_uri(Target), to_binary(Error), to_binary(Reason)]),
+        {error, Error};
+    Tag:Err ->
+        ?LOG_ERROR("Replicator: couldn't write document `~s`, revision `~s`,"
+            " to target database `~s`. Error: `~s`.",
+            [Id, couch_doc:rev_to_str({Pos, RevId}),
+                couch_api_wrap:db_uri(Target), to_binary({Tag, Err})]),
+        {error, Err}
     end.