You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ko...@apache.org on 2011/10/26 20:05:33 UTC

[41/50] git commit: Add test 201-view-group-shutdown.t (COUCHDB-1283)

Add test 201-view-group-shutdown.t (COUCHDB-1283)


git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@1173442 13f79535-47bb-0310-9956-ffa450edef68


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/148f6850
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/148f6850
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/148f6850

Branch: refs/heads/1319-large-headers-are-corrupted
Commit: 148f6850b62225ea058428121882cf7f3753223b
Parents: a04adda
Author: Filipe David Borba Manana <fd...@apache.org>
Authored: Wed Sep 21 00:40:19 2011 +0000
Committer: Filipe David Borba Manana <fd...@apache.org>
Committed: Wed Sep 21 00:40:19 2011 +0000

----------------------------------------------------------------------
 test/etap/201-view-group-shutdown.t |  291 ++++++++++++++++++++++++++++++
 test/etap/Makefile.am               |    1 +
 2 files changed, 292 insertions(+), 0 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/148f6850/test/etap/201-view-group-shutdown.t
----------------------------------------------------------------------
diff --git a/test/etap/201-view-group-shutdown.t b/test/etap/201-view-group-shutdown.t
new file mode 100755
index 0000000..2fabef6
--- /dev/null
+++ b/test/etap/201-view-group-shutdown.t
@@ -0,0 +1,291 @@
+#!/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
+}).
+
+-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
+}).
+
+main_db_name() -> <<"couch_test_view_group_shutdown">>.
+
+
+main(_) ->
+    test_util:init_code_path(),
+
+    etap:plan(17),
+    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()),
+    ok = couch_config:set("couchdb", "max_dbs_open", "3", false),
+    ok = couch_config:set("couchdb", "delayed_commits", "false", false),
+    crypto:start(),
+
+    % Test that while a view group is being compacted its database can not
+    % be closed by the database LRU system.
+    test_view_group_compaction(),
+
+    couch_server_sup:stop(),
+    ok.
+
+
+test_view_group_compaction() ->
+    {ok, DbWriter3} = create_db(<<"couch_test_view_group_shutdown_w3">>),
+    ok = couch_db:close(DbWriter3),
+
+    {ok, MainDb} = create_main_db(),
+    ok = couch_db:close(MainDb),
+
+    {ok, DbWriter1} = create_db(<<"couch_test_view_group_shutdown_w1">>),
+    ok = couch_db:close(DbWriter1),
+
+    {ok, DbWriter2} = create_db(<<"couch_test_view_group_shutdown_w2">>),
+    ok = couch_db:close(DbWriter2),
+
+    Writer1 = spawn_writer(DbWriter1#db.name),
+    Writer2 = spawn_writer(DbWriter2#db.name),
+    etap:is(is_process_alive(Writer1), true, "Spawned writer 1"),
+    etap:is(is_process_alive(Writer2), true, "Spawned writer 2"),
+
+    etap:is(get_writer_status(Writer1), ok, "Writer 1 opened his database"),
+    etap:is(get_writer_status(Writer2), ok, "Writer 2 opened his database"),
+
+    {ok, MonRef} = couch_mrview:compact(MainDb#db.name, <<"_design/foo">>, [monitor]),
+
+    % Add some more docs to database and trigger view update
+    {ok, MainDb2} = couch_db:open_int(MainDb#db.name, []),
+    ok = populate_main_db(MainDb2, 3, 3),
+    update_view(MainDb2#db.name, <<"_design/foo">>, <<"foo">>),
+    ok = couch_db:close(MainDb2),
+
+    % Assuming the view compaction takes more than 50ms to complete
+    ok = timer:sleep(50),
+    Writer3 = spawn_writer(DbWriter3#db.name),
+    etap:is(is_process_alive(Writer3), true, "Spawned writer 3"),
+
+    etap:is(get_writer_status(Writer3), {error, all_dbs_active},
+        "Writer 3 got {error, all_dbs_active} when opening his database"),
+
+    etap:is(is_process_alive(Writer1), true, "Writer 1 still alive"),
+    etap:is(is_process_alive(Writer2), true, "Writer 2 still alive"),
+    etap:is(is_process_alive(Writer3), true, "Writer 3 still alive"),
+
+    receive
+    {'DOWN', MonRef, process, _, normal} ->
+         etap:diag("View group compaction successful"),
+         ok;
+    {'DOWN', MonRef, process, _, _Reason} ->
+         etap:bail("Failure compacting view group")
+    end,
+
+    ok = timer:sleep(2000),
+
+    etap:is(writer_try_again(Writer3), ok,
+        "Told writer 3 to try open his database again"),
+    etap:is(get_writer_status(Writer3), ok,
+        "Writer 3 was able to open his database"),
+
+    etap:is(is_process_alive(Writer1), true, "Writer 1 still alive"),
+    etap:is(is_process_alive(Writer2), true, "Writer 2 still alive"),
+    etap:is(is_process_alive(Writer3), true, "Writer 3 still alive"),
+
+    etap:is(stop_writer(Writer1), ok, "Stopped writer 1"),
+    etap:is(stop_writer(Writer2), ok, "Stopped writer 2"),
+    etap:is(stop_writer(Writer3), ok, "Stopped writer 3"),
+
+    delete_db(MainDb),
+    delete_db(DbWriter1),
+    delete_db(DbWriter2),
+    delete_db(DbWriter3).
+
+
+create_main_db() ->
+    {ok, Db} = create_db(main_db_name()),
+    DDoc = couch_doc:from_json_obj({[
+        {<<"_id">>, <<"_design/foo">>},
+        {<<"language">>, <<"javascript">>},
+        {<<"views">>, {[
+            {<<"foo">>, {[
+                {<<"map">>, <<"function(doc) { emit(doc._id, doc); }">>}
+            ]}},
+            {<<"foo2">>, {[
+                {<<"map">>, <<"function(doc) { emit(doc._id, doc); }">>}
+            ]}},
+            {<<"foo3">>, {[
+                {<<"map">>, <<"function(doc) { emit(doc._id, doc); }">>}
+            ]}},
+            {<<"foo4">>, {[
+                {<<"map">>, <<"function(doc) { emit(doc._id, doc); }">>}
+            ]}},
+            {<<"foo5">>, {[
+                {<<"map">>, <<"function(doc) { emit(doc._id, doc); }">>}
+            ]}}
+        ]}}
+    ]}),
+    {ok, _} = couch_db:update_doc(Db, DDoc, []),
+    ok = populate_main_db(Db, 1000, 20000),
+    update_view(Db#db.name, <<"_design/foo">>, <<"foo">>),
+    {ok, Db}.
+
+
+populate_main_db(Db, BatchSize, N) when N > 0 ->
+    Docs = lists:map(
+        fun(_) ->
+            couch_doc:from_json_obj({[
+                {<<"_id">>, couch_uuids:new()},
+                {<<"value">>, base64:encode(crypto:rand_bytes(1000))}
+            ]})
+        end,
+        lists:seq(1, BatchSize)),
+    {ok, _} = couch_db:update_docs(Db, Docs, []),
+    populate_main_db(Db, BatchSize, N - length(Docs));
+populate_main_db(_Db, _, _) ->
+    ok.
+
+
+update_view(DbName, DDocName, ViewName) ->
+    {ok, Db} = couch_db:open_int(DbName, []),
+    {ok, DDoc} = couch_db:open_doc(Db, DDocName, [ejson_body]),
+    couch_mrview:query_view(Db, DDoc, ViewName, [{stale, false}]),
+    ok = couch_db:close(Db),
+    etap:diag("View group updated").
+
+
+create_db(DbName) ->
+    {ok, Db} = couch_db:create(
+        DbName,
+        [{user_ctx, #user_ctx{roles = [<<"_admin">>]}}, overwrite]),
+    {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.
+
+
+spawn_writer(DbName) ->
+    Parent = self(),
+    spawn(fun() ->
+        process_flag(priority, high),
+        writer_loop(DbName, Parent)
+    end).
+
+
+get_writer_status(Writer) ->
+    Ref = make_ref(),
+    Writer ! {get_status, Ref},
+    receive
+    {db_open, Ref} ->
+        ok;
+    {db_open_error, Error, Ref} ->
+        Error
+    after 5000 ->
+        timeout
+    end.
+
+
+writer_try_again(Writer) ->
+    Ref = make_ref(),
+    Writer ! {try_again, Ref},
+    receive
+    {ok, Ref} ->
+        ok
+    after 5000 ->
+        timeout
+    end.
+
+
+stop_writer(Writer) ->
+    Ref = make_ref(),
+    Writer ! {stop, Ref},
+    receive
+    {ok, Ref} ->
+        ok
+    after 5000 ->
+        etap:bail("Timeout stopping writer process")
+    end.
+
+
+% Just keep the database open, no need to actually do something on it.
+writer_loop(DbName, Parent) ->
+    case couch_db:open_int(DbName, []) of
+    {ok, Db} ->
+        writer_loop_1(Db, Parent);
+    Error ->
+        writer_loop_2(DbName, Parent, Error)
+    end.
+
+writer_loop_1(Db, Parent) ->
+    receive
+    {get_status, Ref} ->
+        Parent ! {db_open, Ref},
+        writer_loop_1(Db, Parent);
+    {stop, Ref} ->
+        ok = couch_db:close(Db),
+        Parent ! {ok, Ref}
+    end.
+
+writer_loop_2(DbName, Parent, Error) ->
+    receive
+    {get_status, Ref} ->
+        Parent ! {db_open_error, Error, Ref},
+        writer_loop_2(DbName, Parent, Error);
+    {try_again, Ref} ->
+        Parent ! {ok, Ref},
+        writer_loop(DbName, Parent)
+    end.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/148f6850/test/etap/Makefile.am
----------------------------------------------------------------------
diff --git a/test/etap/Makefile.am b/test/etap/Makefile.am
index 0f64631..8d3cbfc 100644
--- a/test/etap/Makefile.am
+++ b/test/etap/Makefile.am
@@ -86,6 +86,7 @@ EXTRA_DIST = \
     180-http-proxy.t \
     190-json-stream-parse.t \
     200-view-group-no-db-leaks.t \
+    201-view-group-shutdown.t \
     210-os-proc-pool.t \
     220-compaction-daemon.t \
     230-httpc-pool.t \