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.