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 2012/01/23 00:43:12 UTC
[5/50] git commit: Merge remote-tracking branch 'asf-write/master'
into COUCHDB-1342
Merge remote-tracking branch 'asf-write/master' into COUCHDB-1342
Conflicts:
src/couchdb/couch_db.erl
Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/62ce6101
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/62ce6101
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/62ce6101
Branch: refs/heads/COUCHDB-1342
Commit: 62ce610150ac84d79db0bbdfa6db691420439d17
Parents: 6e38856 6dba2e9
Author: Filipe David Borba Manana <fd...@apache.org>
Authored: Sun Jan 22 13:31:09 2012 +0000
Committer: Filipe David Borba Manana <fd...@apache.org>
Committed: Sun Jan 22 13:31:09 2012 +0000
----------------------------------------------------------------------
.gitignore | 4 +
AUTHORS | 2 +
CHANGES | 22 +
DEVELOPERS | 4 +
INSTALL.Unix | 10 +-
INSTALL.Windows | 2 +-
Makefile.am | 10 +-
NEWS | 11 +-
NOTICE | 2 +-
THANKS | 2 +-
bin/Makefile.am | 2 +
bootstrap | 2 +-
configure.ac | 81 +-
etc/couchdb/default.ini.tpl.in | 38 +-
etc/couchdb/local.ini | 5 +-
license.skip | 3 +
share/Makefile.am | 3 +
share/www/script/couch.js | 28 +-
share/www/script/couch_test_runner.js | 34 +-
share/www/script/couch_tests.js | 3 +
share/www/script/futon.js | 9 +-
share/www/script/jquery.couch.js | 32 +-
share/www/script/test/attachment_ranges.js | 29 +-
share/www/script/test/bulk_docs.js | 13 +
share/www/script/test/config.js | 4 +-
share/www/script/test/cookie_auth.js | 122 ++-
share/www/script/test/oauth.js | 5 +-
share/www/script/test/oauth_users_db.js | 161 +++
share/www/script/test/proxyauth.js | 1 -
share/www/script/test/reader_acl.js | 1 -
share/www/script/test/replicator_db.js | 104 ++-
share/www/script/test/replicator_db_security.js | 395 ++++++
share/www/script/test/users_db_security.js | 234 ++++
src/Makefile.am | 1 +
src/couch_index/src/couch_index_server.erl | 9 +-
src/couch_mrview/src/couch_mrview_http.erl | 15 +
src/couch_replicator/Makefile.am | 75 ++
src/couch_replicator/src/couch_replicator.app.src | 33 +
src/couch_replicator/src/couch_replicator.erl | 952 +++++++++++++++
src/couch_replicator/src/couch_replicator.hrl | 30 +
.../src/couch_replicator_api_wrap.erl | 778 ++++++++++++
.../src/couch_replicator_api_wrap.hrl | 36 +
.../src/couch_replicator_httpc.erl | 286 +++++
.../src/couch_replicator_httpc_pool.erl | 138 +++
.../src/couch_replicator_httpd.erl | 66 +
.../src/couch_replicator_job_sup.erl | 31 +
.../src/couch_replicator_js_functions.hrl | 151 +++
.../src/couch_replicator_manager.erl | 694 +++++++++++
.../src/couch_replicator_notifier.erl | 57 +
.../src/couch_replicator_utils.erl | 382 ++++++
.../src/couch_replicator_worker.erl | 515 ++++++++
src/couch_replicator/test/01-load.t | 37 +
src/couch_replicator/test/02-httpc-pool.t | 250 ++++
src/couch_replicator/test/03-replication-compact.t | 487 ++++++++
.../test/04-replication-large-atts.t | 267 ++++
.../test/05-replication-many-leaves.t | 292 +++++
src/couch_replicator/test/06-doc-missing-stubs.t | 304 +++++
src/couchdb/Makefile.am | 28 +-
src/couchdb/couch.app.tpl.in | 1 -
src/couchdb/couch_api_wrap.erl | 775 ------------
src/couchdb/couch_api_wrap.hrl | 36 -
src/couchdb/couch_api_wrap_httpc.erl | 286 -----
src/couchdb/couch_auth_cache.erl | 47 +-
src/couchdb/couch_changes.erl | 166 ++-
src/couchdb/couch_compaction_daemon.erl | 2 +-
src/couchdb/couch_db.erl | 145 ++-
src/couchdb/couch_db.hrl | 30 +-
src/couchdb/couch_db_updater.erl | 60 +-
src/couchdb/couch_doc.erl | 5 +
src/couchdb/couch_httpc_pool.erl | 138 ---
src/couchdb/couch_httpd.erl | 48 +-
src/couchdb/couch_httpd_auth.erl | 12 +-
src/couchdb/couch_httpd_db.erl | 45 +-
src/couchdb/couch_httpd_oauth.erl | 382 +++++--
src/couchdb/couch_httpd_replicator.erl | 66 -
src/couchdb/couch_httpd_rewrite.erl | 4 +-
src/couchdb/couch_js_functions.hrl | 176 +--
src/couchdb/couch_log.erl | 56 +-
src/couchdb/couch_primary_sup.erl | 6 +-
src/couchdb/couch_query_servers.erl | 2 +
src/couchdb/couch_rep_sup.erl | 31 -
src/couchdb/couch_replication_manager.erl | 625 ----------
src/couchdb/couch_replication_notifier.erl | 57 -
src/couchdb/couch_replicator.erl | 942 --------------
src/couchdb/couch_replicator.hrl | 30 -
src/couchdb/couch_replicator_utils.erl | 382 ------
src/couchdb/couch_replicator_worker.erl | 515 --------
src/couchdb/couch_server.erl | 29 +-
src/couchdb/couch_users_db.erl | 103 ++
src/couchdb/couch_util.erl | 6 +-
src/couchdb/priv/Makefile.am | 14 +-
.../priv/couch_ejson_compare/couch_ejson_compare.c | 21 +-
src/couchdb/priv/couch_js/util.c | 32 +-
src/couchdb/priv/couch_js/util.h | 1 +
src/erlang-oauth/oauth_uri.erl | 2 +
test/etap/001-load.t | 9 -
test/etap/073-changes.t | 96 ++-
test/etap/074-doc-update-conflicts.t | 218 ++++
test/etap/075-auth-cache.t | 274 +++++
test/etap/160-vhosts.t | 89 ++-
test/etap/172-os-daemon-errors.t | 1 -
test/etap/200-view-group-no-db-leaks.t | 4 +-
test/etap/201-view-group-shutdown.t | 4 +-
test/etap/230-httpc-pool.t | 250 ----
test/etap/240-replication-compact.t | 485 --------
test/etap/241-replication-large-atts.t | 267 ----
test/etap/242-replication-many-leaves.t | 216 ----
test/etap/Makefile.am | 8 +-
test/javascript/cli_runner.js | 1 +
test/javascript/run.tpl | 25 +-
110 files changed, 8656 insertions(+), 5861 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb/blob/62ce6101/src/couch_replicator/test/03-replication-compact.t
----------------------------------------------------------------------
diff --cc src/couch_replicator/test/03-replication-compact.t
index 0000000,7c4d38c..e3f59fc
mode 000000,100755..100755
--- a/src/couch_replicator/test/03-replication-compact.t
+++ b/src/couch_replicator/test/03-replication-compact.t
@@@ -1,0 -1,488 +1,487 @@@
+ #!/usr/bin/env escript
+ %% -*- erlang -*-
+ % 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.
+
+ % Verify that compacting databases that are being used as the source or
+ % target of a replication doesn't affect the replication and that the
+ % replication doesn't hold their reference counters forever.
+
+ -define(b2l(B), binary_to_list(B)).
+
+ -record(user_ctx, {
+ name = null,
+ roles = [],
+ handler
+ }).
+
+ -record(db, {
+ main_pid = nil,
+ update_pid = nil,
+ compactor_pid = nil,
+ instance_start_time, % number of microsecs since jan 1 1970 as a binary string
+ fd,
- updater_fd,
+ fd_ref_counter,
+ header = nil,
+ committed_update_seq,
+ fulldocinfo_by_id_btree,
+ docinfo_by_seq_btree,
+ local_docs_btree,
+ update_seq,
+ name,
+ filepath,
+ validate_doc_funs = [],
+ security = [],
+ security_ptr = nil,
+ user_ctx = #user_ctx{},
+ waiting_delayed_commit = nil,
+ revs_limit = 1000,
+ fsync_options = [],
+ options = [],
+ compression,
+ before_doc_update,
+ after_doc_read
+ }).
+
+ -record(rep, {
+ id,
+ source,
+ target,
+ options,
+ user_ctx,
+ doc_id
+ }).
+
+
+ source_db_name() -> <<"couch_test_rep_db_a">>.
+ target_db_name() -> <<"couch_test_rep_db_b">>.
+
+
+ main(_) ->
+ test_util:init_code_path(),
+
+ etap:plan(376),
+ case (catch test()) of
+ ok ->
+ etap:end_tests();
+ Other ->
+ etap:diag(io_lib:format("Test died abnormally: ~p", [Other])),
+ etap:bail(Other)
+ end,
+ ok.
+
+
+ test() ->
+ couch_server_sup:start_link(test_util:config_files()),
+ ibrowse:start(),
+
+ Pairs = [
+ {source_db_name(), target_db_name()},
+ {{remote, source_db_name()}, target_db_name()},
+ {source_db_name(), {remote, target_db_name()}},
+ {{remote, source_db_name()}, {remote, (target_db_name())}}
+ ],
+
+ lists:foreach(
+ fun({Source, Target}) ->
+ {ok, SourceDb} = create_db(source_db_name()),
+ etap:is(couch_db:is_idle(SourceDb), true,
+ "Source database is idle before starting replication"),
+
+ {ok, TargetDb} = create_db(target_db_name()),
+ etap:is(couch_db:is_idle(TargetDb), true,
+ "Target database is idle before starting replication"),
+
+ {ok, RepPid, RepId} = replicate(Source, Target),
+ check_active_tasks(RepPid, RepId, Source, Target),
+ {ok, DocsWritten} = populate_and_compact_test(
+ RepPid, SourceDb, TargetDb),
+
+ wait_target_in_sync(DocsWritten, TargetDb),
+ check_active_tasks(RepPid, RepId, Source, Target),
+ cancel_replication(RepId, RepPid),
+ compare_dbs(SourceDb, TargetDb),
+
+ delete_db(SourceDb),
+ delete_db(TargetDb),
+ couch_server_sup:stop(),
+ ok = timer:sleep(1000),
+ couch_server_sup:start_link(test_util:config_files())
+ end,
+ Pairs),
+
+ couch_server_sup:stop(),
+ ok.
+
+
+ populate_and_compact_test(RepPid, SourceDb0, TargetDb0) ->
+ etap:is(is_process_alive(RepPid), true, "Replication process is alive"),
+ check_db_alive("source", SourceDb0),
+ check_db_alive("target", TargetDb0),
+
+ Writer = spawn_writer(SourceDb0),
+
+ lists:foldl(
+ fun(_, {SourceDb, TargetDb, DocCount}) ->
+ pause_writer(Writer),
+
+ compact_db("source", SourceDb),
+ etap:is(is_process_alive(RepPid), true,
+ "Replication process is alive after source database compaction"),
+ check_db_alive("source", SourceDb),
+ check_ref_counter("source", SourceDb),
+
+ compact_db("target", TargetDb),
+ etap:is(is_process_alive(RepPid), true,
+ "Replication process is alive after target database compaction"),
+ check_db_alive("target", TargetDb),
+ check_ref_counter("target", TargetDb),
+
+ {ok, SourceDb2} = reopen_db(SourceDb),
+ {ok, TargetDb2} = reopen_db(TargetDb),
+
+ resume_writer(Writer),
+ wait_writer(Writer, DocCount),
+
+ compact_db("source", SourceDb2),
+ etap:is(is_process_alive(RepPid), true,
+ "Replication process is alive after source database compaction"),
+ check_db_alive("source", SourceDb2),
+ pause_writer(Writer),
+ check_ref_counter("source", SourceDb2),
+ resume_writer(Writer),
+
+ compact_db("target", TargetDb2),
+ etap:is(is_process_alive(RepPid), true,
+ "Replication process is alive after target database compaction"),
+ check_db_alive("target", TargetDb2),
+ pause_writer(Writer),
+ check_ref_counter("target", TargetDb2),
+ resume_writer(Writer),
+
+ {ok, SourceDb3} = reopen_db(SourceDb2),
+ {ok, TargetDb3} = reopen_db(TargetDb2),
+ {SourceDb3, TargetDb3, DocCount + 50}
+ end,
+ {SourceDb0, TargetDb0, 50}, lists:seq(1, 5)),
+
+ DocsWritten = stop_writer(Writer),
+ {ok, DocsWritten}.
+
+
+ check_db_alive(Type, #db{main_pid = Pid}) ->
+ etap:is(is_process_alive(Pid), true,
+ "Local " ++ Type ++ " database main pid is alive").
+
+
+ compact_db(Type, #db{name = Name}) ->
+ {ok, Db} = couch_db:open_int(Name, []),
+ {ok, CompactPid} = couch_db:start_compact(Db),
+ MonRef = erlang:monitor(process, CompactPid),
+ receive
+ {'DOWN', MonRef, process, CompactPid, normal} ->
+ ok;
+ {'DOWN', MonRef, process, CompactPid, Reason} ->
+ etap:bail("Error compacting " ++ Type ++ " database " ++ ?b2l(Name) ++
+ ": " ++ couch_util:to_list(Reason))
+ after 30000 ->
+ etap:bail("Compaction for " ++ Type ++ " database " ++ ?b2l(Name) ++
+ " didn't finish")
+ end,
+ ok = couch_db:close(Db).
+
+
+ check_ref_counter(Type, #db{name = Name, fd_ref_counter = OldRefCounter}) ->
+ MonRef = erlang:monitor(process, OldRefCounter),
+ receive
+ {'DOWN', MonRef, process, OldRefCounter, _} ->
+ etap:diag("Old " ++ Type ++ " database ref counter terminated")
+ after 30000 ->
+ etap:bail("Old " ++ Type ++ " database ref counter didn't terminate")
+ end,
+ {ok, #db{fd_ref_counter = NewRefCounter} = Db} = couch_db:open_int(Name, []),
+ ok = couch_db:close(Db),
+ etap:isnt(
+ NewRefCounter, OldRefCounter, Type ++ " database has new ref counter").
+
+
+ reopen_db(#db{name = Name}) ->
+ {ok, Db} = couch_db:open_int(Name, []),
+ ok = couch_db:close(Db),
+ {ok, Db}.
+
+
+ wait_target_in_sync(DocCount, #db{name = TargetName}) ->
+ wait_target_in_sync_loop(DocCount, TargetName, 300).
+
+
+ wait_target_in_sync_loop(_DocCount, _TargetName, 0) ->
+ etap:bail("Could not get source and target databases in sync");
+ wait_target_in_sync_loop(DocCount, TargetName, RetriesLeft) ->
+ {ok, Target} = couch_db:open_int(TargetName, []),
+ {ok, TargetInfo} = couch_db:get_db_info(Target),
+ ok = couch_db:close(Target),
+ TargetDocCount = couch_util:get_value(doc_count, TargetInfo),
+ case TargetDocCount == DocCount of
+ true ->
+ etap:diag("Source and target databases are in sync");
+ false ->
+ ok = timer:sleep(100),
+ wait_target_in_sync_loop(DocCount, TargetName, RetriesLeft - 1)
+ end.
+
+
+ compare_dbs(#db{name = SourceName}, #db{name = TargetName}) ->
+ {ok, SourceDb} = couch_db:open_int(SourceName, []),
+ {ok, TargetDb} = couch_db:open_int(TargetName, []),
+ Fun = fun(FullDocInfo, _, Acc) ->
+ {ok, Doc} = couch_db:open_doc(SourceDb, FullDocInfo),
+ {Props} = DocJson = couch_doc:to_json_obj(Doc, [attachments]),
+ DocId = couch_util:get_value(<<"_id">>, Props),
+ DocTarget = case couch_db:open_doc(TargetDb, DocId) of
+ {ok, DocT} ->
+ DocT;
+ Error ->
+ etap:bail("Error opening document '" ++ ?b2l(DocId) ++
+ "' from target: " ++ couch_util:to_list(Error))
+ end,
+ DocTargetJson = couch_doc:to_json_obj(DocTarget, [attachments]),
+ case DocTargetJson of
+ DocJson ->
+ ok;
+ _ ->
+ etap:bail("Content from document '" ++ ?b2l(DocId) ++
+ "' differs in target database")
+ end,
+ {ok, Acc}
+ end,
+ {ok, _, _} = couch_db:enum_docs(SourceDb, Fun, [], []),
+ etap:diag("Target database has the same documents as the source database"),
+ ok = couch_db:close(SourceDb),
+ ok = couch_db:close(TargetDb).
+
+
+ check_active_tasks(RepPid, {BaseId, Ext} = _RepId, Src, Tgt) ->
+ Source = case Src of
+ {remote, NameSrc} ->
+ <<(db_url(NameSrc))/binary, $/>>;
+ _ ->
+ Src
+ end,
+ Target = case Tgt of
+ {remote, NameTgt} ->
+ <<(db_url(NameTgt))/binary, $/>>;
+ _ ->
+ Tgt
+ end,
+ FullRepId = list_to_binary(BaseId ++ Ext),
+ Pid = list_to_binary(pid_to_list(RepPid)),
+ [RepTask] = couch_task_status:all(),
+ etap:is(couch_util:get_value(pid, RepTask), Pid,
+ "_active_tasks entry has correct pid property"),
+ etap:is(couch_util:get_value(replication_id, RepTask), FullRepId,
+ "_active_tasks entry has right replication id"),
+ etap:is(couch_util:get_value(continuous, RepTask), true,
+ "_active_tasks entry has continuous property set to true"),
+ etap:is(couch_util:get_value(source, RepTask), Source,
+ "_active_tasks entry has correct source property"),
+ etap:is(couch_util:get_value(target, RepTask), Target,
+ "_active_tasks entry has correct target property"),
+ etap:is(is_integer(couch_util:get_value(docs_read, RepTask)), true,
+ "_active_tasks entry has integer docs_read property"),
+ etap:is(is_integer(couch_util:get_value(docs_written, RepTask)), true,
+ "_active_tasks entry has integer docs_written property"),
+ etap:is(is_integer(couch_util:get_value(doc_write_failures, RepTask)), true,
+ "_active_tasks entry has integer doc_write_failures property"),
+ etap:is(is_integer(couch_util:get_value(revisions_checked, RepTask)), true,
+ "_active_tasks entry has integer revisions_checked property"),
+ etap:is(is_integer(couch_util:get_value(missing_revisions_found, RepTask)), true,
+ "_active_tasks entry has integer missing_revisions_found property"),
+ etap:is(is_integer(couch_util:get_value(checkpointed_source_seq, RepTask)), true,
+ "_active_tasks entry has integer checkpointed_source_seq property"),
+ etap:is(is_integer(couch_util:get_value(source_seq, RepTask)), true,
+ "_active_tasks entry has integer source_seq property"),
+ Progress = couch_util:get_value(progress, RepTask),
+ etap:is(is_integer(Progress), true,
+ "_active_tasks entry has an integer progress property"),
+ etap:is(Progress =< 100, true, "Progress is not greater than 100%").
+
+
+ wait_writer(Pid, NumDocs) ->
+ case get_writer_num_docs_written(Pid) of
+ N when N >= NumDocs ->
+ ok;
+ _ ->
+ wait_writer(Pid, NumDocs)
+ end.
+
+
+ spawn_writer(Db) ->
+ Parent = self(),
+ Pid = spawn(fun() -> writer_loop(Db, Parent, 0) end),
+ etap:diag("Started source database writer"),
+ Pid.
+
+
+ pause_writer(Pid) ->
+ Ref = make_ref(),
+ Pid ! {pause, Ref},
+ receive
+ {paused, Ref} ->
+ ok
+ after 30000 ->
+ etap:bail("Failed to pause source database writer")
+ end.
+
+
+ resume_writer(Pid) ->
+ Ref = make_ref(),
+ Pid ! {continue, Ref},
+ receive
+ {ok, Ref} ->
+ ok
+ after 30000 ->
+ etap:bail("Failed to unpause source database writer")
+ end.
+
+
+ get_writer_num_docs_written(Pid) ->
+ Ref = make_ref(),
+ Pid ! {get_count, Ref},
+ receive
+ {count, Ref, Count} ->
+ Count
+ after 30000 ->
+ etap:bail("Timeout getting number of documents written from "
+ "source database writer")
+ end.
+
+
+ stop_writer(Pid) ->
+ Ref = make_ref(),
+ Pid ! {stop, Ref},
+ receive
+ {stopped, Ref, DocsWritten} ->
+ MonRef = erlang:monitor(process, Pid),
+ receive
+ {'DOWN', MonRef, process, Pid, _Reason} ->
+ etap:diag("Stopped source database writer"),
+ DocsWritten
+ after 30000 ->
+ etap:bail("Timeout stopping source database writer")
+ end
+ after 30000 ->
+ etap:bail("Timeout stopping source database writer")
+ end.
+
+
+ writer_loop(#db{name = DbName}, Parent, Counter) ->
+ maybe_pause(Parent, Counter),
+ Doc = couch_doc:from_json_obj({[
+ {<<"_id">>, list_to_binary(integer_to_list(Counter + 1))},
+ {<<"value">>, Counter + 1},
+ {<<"_attachments">>, {[
+ {<<"icon1.png">>, {[
+ {<<"data">>, base64:encode(att_data())},
+ {<<"content_type">>, <<"image/png">>}
+ ]}},
+ {<<"icon2.png">>, {[
+ {<<"data">>, base64:encode(iolist_to_binary(
+ [att_data(), att_data()]))},
+ {<<"content_type">>, <<"image/png">>}
+ ]}}
+ ]}}
+ ]}),
+ maybe_pause(Parent, Counter),
+ {ok, Db} = couch_db:open_int(DbName, []),
+ {ok, _} = couch_db:update_doc(Db, Doc, []),
+ ok = couch_db:close(Db),
+ receive
+ {get_count, Ref} ->
+ Parent ! {count, Ref, Counter + 1},
+ writer_loop(Db, Parent, Counter + 1);
+ {stop, Ref} ->
+ Parent ! {stopped, Ref, Counter + 1}
+ after 0 ->
+ ok = timer:sleep(500),
+ writer_loop(Db, Parent, Counter + 1)
+ end.
+
+
+ maybe_pause(Parent, Counter) ->
+ receive
+ {get_count, Ref} ->
+ Parent ! {count, Ref, Counter};
+ {pause, Ref} ->
+ Parent ! {paused, Ref},
+ receive {continue, Ref2} -> Parent ! {ok, Ref2} end
+ after 0 ->
+ ok
+ end.
+
+
+ db_url(DbName) ->
+ iolist_to_binary([
+ "http://", couch_config:get("httpd", "bind_address", "127.0.0.1"),
+ ":", integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
+ "/", DbName
+ ]).
+
+
+ create_db(DbName) ->
+ {ok, Db} = couch_db:create(
+ DbName,
+ [{user_ctx, #user_ctx{roles = [<<"_admin">>]}}, overwrite]),
+ couch_db:close(Db),
+ {ok, Db}.
+
+
+ delete_db(#db{name = DbName, main_pid = Pid}) ->
+ ok = couch_server:delete(
+ DbName, [{user_ctx, #user_ctx{roles = [<<"_admin">>]}}]),
+ MonRef = erlang:monitor(process, Pid),
+ receive
+ {'DOWN', MonRef, process, Pid, _Reason} ->
+ ok
+ after 30000 ->
+ etap:bail("Timeout deleting database")
+ end.
+
+
+ replicate({remote, Db}, Target) ->
+ replicate(db_url(Db), Target);
+
+ replicate(Source, {remote, Db}) ->
+ replicate(Source, db_url(Db));
+
+ replicate(Source, Target) ->
+ RepObject = {[
+ {<<"source">>, Source},
+ {<<"target">>, Target},
+ {<<"continuous">>, true}
+ ]},
+ {ok, Rep} = couch_replicator_utils:parse_rep_doc(
+ RepObject, #user_ctx{roles = [<<"_admin">>]}),
+ {ok, Pid} = couch_replicator:async_replicate(Rep),
+ {ok, Pid, Rep#rep.id}.
+
+
+ cancel_replication(RepId, RepPid) ->
+ {ok, _} = couch_replicator:cancel_replication(RepId),
+ etap:is(is_process_alive(RepPid), false,
+ "Replication process is no longer alive after cancel").
+
+
+ att_data() ->
+ {ok, Data} = file:read_file(
+ test_util:source_file("share/www/image/logo.png")),
+ Data.
http://git-wip-us.apache.org/repos/asf/couchdb/blob/62ce6101/src/couchdb/couch_db.erl
----------------------------------------------------------------------
diff --cc src/couchdb/couch_db.erl
index 9d1ca0f,ae21bfa..73d16b3
--- a/src/couchdb/couch_db.erl
+++ b/src/couchdb/couch_db.erl
@@@ -702,8 -704,8 +704,8 @@@ update_docs(Db, Docs, Options, replicat
DocErrors = [],
DocBuckets3 = DocBuckets
end,
- DocBuckets4 = [[doc_flush_atts(check_dup_atts(Doc), Db#db.fd)
- || Doc <- Bucket] || Bucket <- DocBuckets3],
- DocBuckets4 = [[{doc_flush_atts(check_dup_atts(Doc), Db#db.updater_fd), Ref}
++ DocBuckets4 = [[{doc_flush_atts(check_dup_atts(Doc), Db#db.fd), Ref}
+ || {Doc, Ref} <- Bucket] || Bucket <- DocBuckets3],
{ok, []} = write_and_commit(Db, DocBuckets4, [], [merge_conflicts | Options]),
{ok, DocErrors};
@@@ -757,9 -766,9 +766,9 @@@ update_docs(Db, Docs, Options, interact
Options2 = if AllOrNothing -> [merge_conflicts];
true -> [] end ++ Options,
DocBuckets3 = [[
- doc_flush_atts(set_new_att_revpos(
- check_dup_atts(Doc)), Db#db.fd)
- || Doc <- B] || B <- DocBuckets2],
+ {doc_flush_atts(set_new_att_revpos(
- check_dup_atts(Doc)), Db#db.updater_fd), Ref}
++ check_dup_atts(Doc)), Db#db.fd), Ref}
+ || {Doc, Ref} <- B] || B <- DocBuckets2],
{DocBuckets4, IdRevs} = new_revs(DocBuckets3, [], []),
{ok, CommitResults} = write_and_commit(Db, DocBuckets4, NonRepDocs, Options2),
@@@ -832,7 -841,7 +841,7 @@@ write_and_commit(#db{update_pid=UpdateP
% compaction. Retry by reopening the db and writing to the current file
{ok, Db2} = open_ref_counted(Db#db.main_pid, self()),
DocBuckets2 = [
- [doc_flush_atts(Doc, Db2#db.fd) || Doc <- Bucket] ||
- [{doc_flush_atts(Doc, Db2#db.updater_fd), Ref} || {Doc, Ref} <- Bucket] ||
++ [{doc_flush_atts(Doc, Db2#db.fd), Ref} || {Doc, Ref} <- Bucket] ||
Bucket <- DocBuckets1
],
% We only retry once
http://git-wip-us.apache.org/repos/asf/couchdb/blob/62ce6101/src/couchdb/couch_db.hrl
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb/blob/62ce6101/src/couchdb/couch_db_updater.erl
----------------------------------------------------------------------
diff --cc src/couchdb/couch_db_updater.erl
index a4d4202,54531db..357524d
--- a/src/couchdb/couch_db_updater.erl
+++ b/src/couchdb/couch_db_updater.erl
@@@ -477,9 -481,20 +477,11 @@@ init_db(DbName, Filepath, Fd, Header0,
revs_limit = Header#db_header.revs_limit,
fsync_options = FsyncOptions,
options = Options,
- compression = Compression
+ 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)
}.
-open_reader_fd(Filepath, Options) ->
- {ok, Fd} = case lists:member(sys_db, Options) of
- true ->
- couch_file:open(Filepath, [read_only, sys_db]);
- false ->
- couch_file:open(Filepath, [read_only])
- end,
- unlink(Fd),
- Fd.
close_db(#db{fd_ref_counter = RefCntr}) ->
couch_ref_counter:drop(RefCntr).
http://git-wip-us.apache.org/repos/asf/couchdb/blob/62ce6101/test/etap/200-view-group-no-db-leaks.t
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb/blob/62ce6101/test/etap/201-view-group-shutdown.t
----------------------------------------------------------------------