You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by jc...@apache.org on 2009/03/15 18:47:30 UTC
svn commit: r754704 - in /couchdb/trunk:
share/www/script/test/replication.js src/couchdb/couch_rep.erl
Author: jchris
Date: Sun Mar 15 17:47:29 2009
New Revision: 754704
URL: http://svn.apache.org/viewvc?rev=754704&view=rev
Log:
Streaming attachment replication now follows redirects and checks for error codes. Includes tests that design doc attachments are replicated.
Modified:
couchdb/trunk/share/www/script/test/replication.js
couchdb/trunk/src/couchdb/couch_rep.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=754704&r1=754703&r2=754704&view=diff
==============================================================================
--- couchdb/trunk/share/www/script/test/replication.js (original)
+++ couchdb/trunk/share/www/script/test/replication.js Sun Mar 15 17:47:29 2009
@@ -150,6 +150,16 @@
}
}
});
+ // make sure on design docs as well
+ dbA.save({
+ _id:"_design/with_bin",
+ _attachments:{
+ "foo.txt": {
+ "type":"base64",
+ "data": "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
+ }
+ }
+ });
};
this.afterAB1 = function(dbA, dbB) {
@@ -158,6 +168,13 @@
xhr = CouchDB.request("GET", "/test_suite_db_b/bin_doc/foo.txt");
T(xhr.responseText == "This is a base64 encoded text")
+
+ // and the design-doc
+ xhr = CouchDB.request("GET", "/test_suite_db_a/_design/with_bin/foo.txt");
+ T(xhr.responseText == "This is a base64 encoded text")
+
+ xhr = CouchDB.request("GET", "/test_suite_db_b/_design/with_bin/foo.txt");
+ T(xhr.responseText == "This is a base64 encoded text")
};
},
Modified: couchdb/trunk/src/couchdb/couch_rep.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_rep.erl?rev=754704&r1=754703&r2=754704&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_rep.erl (original)
+++ couchdb/trunk/src/couchdb/couch_rep.erl Sun Mar 15 17:47:29 2009
@@ -327,9 +327,30 @@
couch_util:should_flush(),
receive
{From, {set_req_id, NewId}} ->
+ %% we learn the ReqId to listen for
From ! {self(), {ok, NewId}},
attachment_loop(NewId);
- {ibrowse_async_headers, ReqId, _Status, _Headers} ->
+ {ibrowse_async_headers, ReqId, Status, Headers} ->
+ %% we got header, give the controlling process a chance to react
+ receive
+ {From, gimme_status} ->
+ %% send status/headers to controller
+ From ! {self(), {status, Status, Headers}},
+ receive
+ {From, continue} ->
+ %% normal case
+ attachment_loop(ReqId);
+ {From, fail} ->
+ %% error, failure code
+ ?LOG_ERROR(
+ "streaming attachment failed with status ~p",
+ [Status]),
+ exit(attachment_request_failed);
+ {From, stop_ok} ->
+ %% stop looping, controller will start a new loop
+ stop_ok
+ end
+ end,
attachment_loop(ReqId);
{ibrowse_async_response, ReqId, {chunk_start,_}} ->
attachment_loop(ReqId);
@@ -349,7 +370,19 @@
% TODO worry about revisions
Url = DbUrl ++ url_encode(Id) ++ "/" ++ ?b2l(Name),
?LOG_DEBUG("Attachment URL ~p", [Url]),
+ {ok, RcvFun} = make_attachment_stub_receiver(Url, Headers, Name,
+ Type, Length),
+ {Name, {Type, {RcvFun, Length}}}.
+
+make_attachment_stub_receiver(Url, Headers, Name, Type, Length) ->
+ make_attachment_stub_receiver(Url, Headers, Name, Type, Length, 10).
+
+make_attachment_stub_receiver(Url, _Headers, _Name, _Type, _Length, 0) ->
+ ?LOG_ERROR("streaming attachment request failed after 10 retries: ~s",
+ [Url]),
+ exit(attachment_request_failed);
+make_attachment_stub_receiver(Url, Headers, Name, Type, Length, Retries) ->
%% start the process that receives attachment data from ibrowse
Pid = spawn_link(fun() -> attachment_loop(nil) end),
@@ -364,14 +397,46 @@
Pid ! {self(), {set_req_id, ReqId}},
receive {Pid, {ok, ReqId}} -> ok end,
- %% this is the function that goes into the streaming attachment code.
- %% It gets executed by the replication gen_server, so it can't
- %% be the one to actually receive the ibrowse data.
- RcvFun = fun() ->
- Pid ! {self(), gimme_data},
- receive {Pid, Data} -> Data end
- end,
- {Name, {Type, {RcvFun, Length}}}.
+ %% wait for headers to ensure that we have a 200 status code
+ %% this is where we follow redirects etc
+ Pid ! {self(), gimme_status},
+ receive {Pid, {status, StreamStatus, StreamHeaders}} ->
+ ?LOG_DEBUG("streaming attachment Status ~p Headers ~p",
+ [StreamStatus, StreamHeaders]),
+
+ ResponseCode = list_to_integer(StreamStatus),
+ if
+ ResponseCode >= 200, ResponseCode < 300 ->
+ % the normal case
+ Pid ! {self(), continue},
+ %% this function goes into the streaming attachment code.
+ %% It gets executed by the replication gen_server, so it can't
+ %% be the one to actually receive the ibrowse data.
+ {ok, fun() ->
+ Pid ! {self(), gimme_data},
+ receive {Pid, Data} -> Data end
+ end};
+ ResponseCode >= 300, ResponseCode < 400 ->
+ % follow the redirect
+ Pid ! {self(), stop_ok},
+ RedirectUrl = mochiweb_headers:get_value("Location",
+ mochiweb_headers:make(StreamHeaders)),
+ make_attachment_stub_receiver(RedirectUrl, Headers, Name, Type, Length,
+ Retries - 1);
+ ResponseCode >= 400, ResponseCode < 500 ->
+ % an error... log and fail
+ ?LOG_ERROR("streaming attachment failed with code ~p: ~s",
+ [ResponseCode, Url]),
+ Pid ! {self(), fail},
+ exit(attachment_request_failed);
+ ResponseCode == 500 ->
+ % an error... log and retry
+ ?LOG_INFO("retrying streaming attachment request due to 500 error: ~s", [Url]),
+ Pid ! {self(), fail},
+ make_attachment_stub_receiver(Url, Headers, Name, Type, Length,
+ Retries - 1)
+ end
+ end.
open_db({remote, Url, Headers})->