summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Kocoloski <kocolosk@apache.org>2009-08-15 05:11:45 +0000
committerAdam Kocoloski <kocolosk@apache.org>2009-08-15 05:11:45 +0000
commit165531bcf223f1c05c4c2eaef7cd2f2943c10584 (patch)
tree1e1231481683d27940000578f715f4490dcc14d1
parentf56afb1ea2dbd59262947ed27bb1913394aeb5c6 (diff)
better failure modes in replication. See COUCHDB-193, COUCHDB-416
If you try to replicate a DB to itself, the replication will proceed, but no checkpoints will be saved, and the logs will say "checkpoint failure: conflict (are you replicating to yourself?)" If you try to specify a non-existent DB as source or target, replication will fail immediately with a 404. The response body will indicate which DB could not be opened. git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@804436 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--src/couchdb/couch_httpd_misc_handlers.erl5
-rw-r--r--src/couchdb/couch_rep.erl24
-rw-r--r--src/couchdb/couch_rep_httpc.erl2
3 files changed, 23 insertions, 8 deletions
diff --git a/src/couchdb/couch_httpd_misc_handlers.erl b/src/couchdb/couch_httpd_misc_handlers.erl
index c4f28308..e72f9d2f 100644
--- a/src/couchdb/couch_httpd_misc_handlers.erl
+++ b/src/couchdb/couch_httpd_misc_handlers.erl
@@ -79,13 +79,16 @@ handle_task_status_req(Req) ->
handle_replicate_req(#httpd{method='POST'}=Req) ->
PostBody = couch_httpd:json_body_obj(Req),
- case couch_rep:replicate(PostBody, Req#httpd.user_ctx) of
+ try couch_rep:replicate(PostBody, Req#httpd.user_ctx) of
{ok, {JsonResults}} ->
send_json(Req, {[{ok, true} | JsonResults]});
{error, {Type, Details}} ->
send_json(Req, 500, {[{error, Type}, {reason, Details}]});
{error, Reason} ->
send_json(Req, 500, {[{error, Reason}]})
+ catch
+ throw:{db_not_found, Msg} ->
+ send_json(Req, 404, {[{error, db_not_found}, {reason, Msg}]})
end;
handle_replicate_req(Req) ->
send_method_not_allowed(Req, "POST").
diff --git a/src/couchdb/couch_rep.erl b/src/couchdb/couch_rep.erl
index c5a07685..cde0a85e 100644
--- a/src/couchdb/couch_rep.erl
+++ b/src/couchdb/couch_rep.erl
@@ -78,7 +78,11 @@ replicate(PostBody, UserCtx) ->
replicate(PostBody, UserCtx)
end.
-init([RepId, {PostProps}, UserCtx] = InitArgs) ->
+init(InitArgs) ->
+ try do_init(InitArgs)
+ catch throw:{db_not_found, DbUrl} -> {stop, {db_not_found, DbUrl}} end.
+
+do_init([RepId, {PostProps}, UserCtx] = InitArgs) ->
process_flag(trap_exit, true),
SourceProps = proplists:get_value(<<"source">>, PostProps),
@@ -177,7 +181,6 @@ handle_info({'EXIT', Pid, Reason}, State) ->
{stop, Reason, State}.
terminate(normal, State) ->
- % ?LOG_DEBUG("replication terminating normally", []),
#state{
checkpoint_history = CheckpointHistory,
committed_seq = NewSeq,
@@ -252,7 +255,9 @@ start_replication_server(Replicator) ->
end;
{error, {already_started, Pid}} ->
?LOG_DEBUG("replication ~p already running at ~p", [RepId, Pid]),
- Pid
+ Pid;
+ {error, {{db_not_found, DbUrl}, _}} ->
+ throw({db_not_found, <<"could not open ", DbUrl/binary>>})
end.
compare_replication_logs(SrcDoc, TgtDoc) ->
@@ -389,7 +394,7 @@ open_db({Props}, _UserCtx) ->
},
case couch_rep_httpc:db_exists(Db) of
true -> Db;
- false -> throw({db_not_found, Url})
+ false -> throw({db_not_found, ?l2b(Url)})
end;
open_db(<<"http://",_/binary>>=Url, _) ->
open_db({[{<<"url">>,Url}]}, []);
@@ -446,17 +451,22 @@ do_checkpoint(State) ->
{<<"source_last_seq">>, RecordSeqNum},
{<<"history">>, lists:sublist([NewHistoryEntry | OldHistory], 50)}
]},
- % ?LOG_DEBUG("updating src doc ~p", [SourceLog]),
+
+ try
{SrcRevPos,SrcRevId} =
update_doc(Source, SourceLog#doc{body=NewRepHistory}, []),
- % ?LOG_DEBUG("updating tgt doc ~p", [TargetLog]),
{TgtRevPos,TgtRevId} =
update_doc(Target, TargetLog#doc{body=NewRepHistory}, []),
State#state{
checkpoint_history = NewRepHistory,
source_log = SourceLog#doc{revs={SrcRevPos, [SrcRevId]}},
target_log = TargetLog#doc{revs={TgtRevPos, [TgtRevId]}}
- }.
+ }
+ catch throw:conflict ->
+ ?LOG_ERROR("checkpoint failure: conflict (are you replicating to yourself?)",
+ []),
+ State
+ end.
commit_to_both(Source, Target) ->
% commit the src async
diff --git a/src/couchdb/couch_rep_httpc.erl b/src/couchdb/couch_rep_httpc.erl
index 3e3b5f05..3d0696e4 100644
--- a/src/couchdb/couch_rep_httpc.erl
+++ b/src/couchdb/couch_rep_httpc.erl
@@ -96,6 +96,8 @@ process_response({ok, Status, Headers, Body}, Req) ->
MochiHeaders = mochiweb_headers:make(Headers),
RedirectUrl = mochiweb_headers:get_value("Location", MochiHeaders),
do_request(Req#http_db{url = RedirectUrl});
+ Code =:= 409 ->
+ throw(conflict);
Code >= 400, Code < 500 ->
?JSON_DECODE(maybe_decompress(Headers, Body));
Code =:= 500; Code =:= 502 ->