summaryrefslogtreecommitdiff
path: root/1.1.x/test/etap/200-view-group-no-db-leaks.t
diff options
context:
space:
mode:
authorRobert Newson <rnewson@apache.org>2011-05-17 11:15:14 +0000
committerRobert Newson <rnewson@apache.org>2011-05-17 11:15:14 +0000
commite8e4b0d293021fe90326a85828f3cfb087bf18b7 (patch)
tree986f544eac623ec23b769b36828894f93a173aa3 /1.1.x/test/etap/200-view-group-no-db-leaks.t
parentda6a5322b0b8084f434752060caa8be214c6f4fa (diff)
tagging 1.1.0
git-svn-id: https://svn.apache.org/repos/asf/couchdb/tags/1.1.0@1104149 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to '1.1.x/test/etap/200-view-group-no-db-leaks.t')
-rwxr-xr-x1.1.x/test/etap/200-view-group-no-db-leaks.t262
1 files changed, 262 insertions, 0 deletions
diff --git a/1.1.x/test/etap/200-view-group-no-db-leaks.t b/1.1.x/test/etap/200-view-group-no-db-leaks.t
new file mode 100755
index 00000000..9c77f1a8
--- /dev/null
+++ b/1.1.x/test/etap/200-view-group-no-db-leaks.t
@@ -0,0 +1,262 @@
+#!/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.
+
+-record(user_ctx, {
+ name = null,
+ roles = [],
+ handler
+}).
+
+-define(LATEST_DISK_VERSION, 5).
+
+-record(db_header,
+ {disk_version = ?LATEST_DISK_VERSION,
+ update_seq = 0,
+ unused = 0,
+ fulldocinfo_by_id_btree_state = nil,
+ docinfo_by_seq_btree_state = nil,
+ local_docs_btree_state = nil,
+ purge_seq = 0,
+ purged_docs = nil,
+ security_ptr = nil,
+ revs_limit = 1000
+}).
+
+-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,
+ fd_ref_counter,
+ header = #db_header{},
+ 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 = [],
+ is_sys_db = false
+}).
+
+test_db_name() -> <<"couch_test_view_group_db_leaks">>.
+ddoc_name() -> <<"foo">>.
+
+main(_) ->
+ test_util:init_code_path(),
+
+ etap:plan(18),
+ 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()),
+ timer:sleep(1000),
+ put(addr, couch_config:get("httpd", "bind_address", "127.0.0.1")),
+ put(port, integer_to_list(mochiweb_socket_server:get(couch_httpd, port))),
+ application:start(inets),
+
+ delete_db(),
+ create_db(),
+
+ create_docs(),
+ create_design_doc(),
+
+ ViewGroup = couch_view:get_group_server(
+ test_db_name(), <<"_design/", (ddoc_name())/binary>>),
+ etap:is(is_pid(ViewGroup), true, "got view group pid"),
+ etap:is(is_process_alive(ViewGroup), true, "view group pid is alive"),
+
+ query_view(),
+ check_db_ref_count(),
+ etap:is(is_process_alive(ViewGroup), true, "view group pid is alive"),
+
+ create_new_doc(<<"doc1000">>),
+ query_view(),
+ check_db_ref_count(),
+ etap:is(is_process_alive(ViewGroup), true, "view group pid is alive"),
+
+ Ref1 = get_db_ref_counter(),
+ compact_db(),
+ check_db_ref_count(),
+ Ref2 = get_db_ref_counter(),
+ etap:isnt(Ref1, Ref2, "DB ref counter changed"),
+ etap:is(false, is_process_alive(Ref1), "old DB ref counter is not alive"),
+ etap:is(is_process_alive(ViewGroup), true, "view group pid is alive"),
+
+ compact_view_group(),
+ check_db_ref_count(),
+ Ref3 = get_db_ref_counter(),
+ etap:is(Ref3, Ref2, "DB ref counter didn't change"),
+ etap:is(is_process_alive(ViewGroup), true, "view group pid is alive"),
+
+ create_new_doc(<<"doc1001">>),
+ query_view(),
+ check_db_ref_count(),
+ etap:is(is_process_alive(ViewGroup), true, "view group pid is alive"),
+
+ MonRef = erlang:monitor(process, ViewGroup),
+ ok = couch_server:delete(test_db_name(), []),
+ receive
+ {'DOWN', MonRef, _, _, _} ->
+ etap:diag("view group is dead after DB deletion")
+ after 5000 ->
+ etap:bail("view group did not die after DB deletion")
+ end,
+
+ ok = timer:sleep(1000),
+ delete_db(),
+ couch_server_sup:stop(),
+ ok.
+
+admin_user_ctx() ->
+ {user_ctx, #user_ctx{roles=[<<"_admin">>]}}.
+
+create_db() ->
+ {ok, Db} = couch_db:create(test_db_name(), [admin_user_ctx()]),
+ ok = couch_db:close(Db).
+
+delete_db() ->
+ couch_server:delete(test_db_name(), [admin_user_ctx()]).
+
+compact_db() ->
+ {ok, Db} = couch_db:open_int(test_db_name(), []),
+ ok = couch_db:start_compact(Db),
+ ok = couch_db:close(Db),
+ wait_db_compact_done(10).
+
+wait_db_compact_done(0) ->
+ etap:bail("DB compaction failed to finish.");
+wait_db_compact_done(N) ->
+ {ok, Db} = couch_db:open_int(test_db_name(), []),
+ ok = couch_db:close(Db),
+ case is_pid(Db#db.compactor_pid) of
+ false ->
+ ok;
+ true ->
+ ok = timer:sleep(500),
+ wait_db_compact_done(N - 1)
+ end.
+
+compact_view_group() ->
+ ok = couch_view_compactor:start_compact(test_db_name(), ddoc_name()),
+ wait_view_compact_done(10).
+
+wait_view_compact_done(0) ->
+ etap:bail("View group compaction failed to finish.");
+wait_view_compact_done(N) ->
+ {ok, {{_, Code, _}, _Headers, Body}} = http:request(
+ get,
+ {db_url() ++ "/_design/" ++ binary_to_list(ddoc_name()) ++ "/_info", []},
+ [],
+ [{sync, true}]),
+ case Code of
+ 200 -> ok;
+ _ -> etap:bail("Invalid view group info.")
+ end,
+ {Info} = couch_util:json_decode(Body),
+ {IndexInfo} = couch_util:get_value(<<"view_index">>, Info),
+ CompactRunning = couch_util:get_value(<<"compact_running">>, IndexInfo),
+ case CompactRunning of
+ false ->
+ ok;
+ true ->
+ ok = timer:sleep(500),
+ wait_view_compact_done(N - 1)
+ end.
+
+get_db_ref_counter() ->
+ {ok, #db{fd_ref_counter = Ref} = Db} = couch_db:open_int(test_db_name(), []),
+ ok = couch_db:close(Db),
+ Ref.
+
+check_db_ref_count() ->
+ {ok, #db{fd_ref_counter = Ref} = Db} = couch_db:open_int(test_db_name(), []),
+ ok = couch_db:close(Db),
+ etap:is(couch_ref_counter:count(Ref), 2,
+ "DB ref counter is only held by couch_db and couch_db_updater"),
+ ok.
+
+create_docs() ->
+ {ok, Db} = couch_db:open(test_db_name(), [admin_user_ctx()]),
+ Doc1 = couch_doc:from_json_obj({[
+ {<<"_id">>, <<"doc1">>},
+ {<<"value">>, 1}
+ ]}),
+ Doc2 = couch_doc:from_json_obj({[
+ {<<"_id">>, <<"doc2">>},
+ {<<"value">>, 2}
+
+ ]}),
+ Doc3 = couch_doc:from_json_obj({[
+ {<<"_id">>, <<"doc3">>},
+ {<<"value">>, 3}
+ ]}),
+ {ok, _} = couch_db:update_docs(Db, [Doc1, Doc2, Doc3]),
+ couch_db:ensure_full_commit(Db),
+ couch_db:close(Db).
+
+create_design_doc() ->
+ {ok, Db} = couch_db:open(test_db_name(), [admin_user_ctx()]),
+ DDoc = couch_doc:from_json_obj({[
+ {<<"_id">>, <<"_design/", (ddoc_name())/binary>>},
+ {<<"language">>, <<"javascript">>},
+ {<<"views">>, {[
+ {<<"bar">>, {[
+ {<<"map">>, <<"function(doc) { emit(doc._id, null); }">>}
+ ]}}
+ ]}}
+ ]}),
+ {ok, _} = couch_db:update_docs(Db, [DDoc]),
+ couch_db:ensure_full_commit(Db),
+ couch_db:close(Db).
+
+create_new_doc(Id) ->
+ {ok, Db} = couch_db:open(test_db_name(), [admin_user_ctx()]),
+ Doc666 = couch_doc:from_json_obj({[
+ {<<"_id">>, Id},
+ {<<"value">>, 999}
+ ]}),
+ {ok, _} = couch_db:update_docs(Db, [Doc666]),
+ couch_db:ensure_full_commit(Db),
+ couch_db:close(Db).
+
+db_url() ->
+ "http://" ++ get(addr) ++ ":" ++ get(port) ++ "/" ++
+ binary_to_list(test_db_name()).
+
+query_view() ->
+ {ok, {{_, Code, _}, _Headers, _Body}} = http:request(
+ get,
+ {db_url() ++ "/_design/" ++ binary_to_list(ddoc_name()) ++
+ "/_view/bar", []},
+ [],
+ [{sync, true}]),
+ etap:is(Code, 200, "got view response"),
+ ok.