diff options
author | Volker Mische <volker.mische@gmail.com> | 2013-07-16 00:26:03 +0200 |
---|---|---|
committer | Robert Newson <rnewson@apache.org> | 2013-12-14 11:31:32 +0100 |
commit | 03bc1641639667f26e219cc36885118448c24158 (patch) | |
tree | 81dbaf2b12aacff419452c2a0501ca648c04a184 | |
parent | 7c23a6ed848ecec2518b352462a8c7f0c98369cf (diff) | |
download | couchdb-03bc1641639667f26e219cc36885118448c24158.tar.gz |
Tests for replicator use_checkpoints option
-rw-r--r-- | src/couch_replicator/Makefile.am | 3 | ||||
-rwxr-xr-x | src/couch_replicator/test/07-use-checkpoints.t | 256 |
2 files changed, 258 insertions, 1 deletions
diff --git a/src/couch_replicator/Makefile.am b/src/couch_replicator/Makefile.am index 71d370280..2dcd47dfd 100644 --- a/src/couch_replicator/Makefile.am +++ b/src/couch_replicator/Makefile.am @@ -41,7 +41,8 @@ test_files = \ test/03-replication-compact.t \ test/04-replication-large-atts.t \ test/05-replication-many-leaves.t \ - test/06-doc-missing-stubs.t + test/06-doc-missing-stubs.t \ + test/07-use-checkpoints.t compiled_files = \ ebin/couch_replicator_api_wrap.beam \ diff --git a/src/couch_replicator/test/07-use-checkpoints.t b/src/couch_replicator/test/07-use-checkpoints.t new file mode 100755 index 000000000..cefc1a7e3 --- /dev/null +++ b/src/couch_replicator/test/07-use-checkpoints.t @@ -0,0 +1,256 @@ +#!/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(doc, { + id = <<"">>, + revs = {0, []}, + body = {[]}, + atts = [], + deleted = false, + meta = [] +}). + +-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(16), + 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(), + + test_use_checkpoints(false), + test_use_checkpoints(true), + + couch_server_sup:stop(), + ok. + + +test_use_checkpoints(UseCheckpoints) -> + 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())}} + ], + + ListenerFun = case UseCheckpoints of + false -> + fun({finished, _, {CheckpointHistory}}) -> + etap:is(CheckpointHistory, + [{<<"use_checkpoints">>,false}], + "No checkpoints found"); + (_) -> + ok + end; + true -> + fun({finished, _, {CheckpointHistory}}) -> + SessionId = lists:keyfind( + <<"session_id">>, 1, CheckpointHistory), + etap:isnt(SessionId, false, "There's a checkpoint"); + (_) -> + ok + end + end, + {ok, Listener} = couch_replicator_notifier:start_link(ListenerFun), + + lists:foreach( + fun({Source, Target}) -> + {ok, SourceDb} = create_db(source_db_name()), + etap:diag("Populating source database"), + populate_db(SourceDb, 100), + ok = couch_db:close(SourceDb), + + etap:diag("Creating target database"), + {ok, TargetDb} = create_db(target_db_name()), + ok = couch_db:close(TargetDb), + + etap:diag("Setup replicator notifier listener"), + + etap:diag("Triggering replication"), + replicate(Source, Target, UseCheckpoints), + + etap:diag("Replication finished, comparing source and target databases"), + compare_dbs(SourceDb, TargetDb), + + etap:diag("Deleting databases"), + delete_db(TargetDb), + delete_db(SourceDb), + + ok = timer:sleep(1000) + end, + Pairs), + + couch_replicator_notifier:stop(Listener). + + +populate_db(Db, DocCount) -> + Docs = lists:foldl( + fun(DocIdCounter, Acc) -> + Id = iolist_to_binary(["doc", integer_to_list(DocIdCounter)]), + Value = iolist_to_binary(["val", integer_to_list(DocIdCounter)]), + Doc = #doc{ + id = Id, + body = {[ {<<"value">>, Value} ]} + }, + [Doc | Acc] + end, + [], lists:seq(1, DocCount)), + {ok, _} = couch_db:update_docs(Db, Docs, []). + + +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). + + +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, UseCheckpoints) -> + replicate(db_url(Db), Target, UseCheckpoints); + +replicate(Source, {remote, Db}, UseCheckpoints) -> + replicate(Source, db_url(Db), UseCheckpoints); + +replicate(Source, Target, UseCheckpoints) -> + RepObject = {[ + {<<"source">>, Source}, + {<<"target">>, Target}, + {<<"use_checkpoints">>, UseCheckpoints} + ]}, + {ok, Rep} = couch_replicator_utils:parse_rep_doc( + RepObject, #user_ctx{roles = [<<"_admin">>]}), + {ok, Pid} = couch_replicator:async_replicate(Rep), + MonRef = erlang:monitor(process, Pid), + receive + {'DOWN', MonRef, process, Pid, Reason} -> + etap:is(Reason, normal, "Replication finished successfully") + after 300000 -> + etap:bail("Timeout waiting for replication to finish") + end. |