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 <fdman...@apache.org> Authored: Sun Jan 22 13:31:09 2012 +0000 Committer: Filipe David Borba Manana <fdman...@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 ----------------------------------------------------------------------