You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by kx...@apache.org on 2014/06/03 06:50:36 UTC

[17/31] couchdb commit: updated refs/heads/1963-eunit to 16528f8

Port 074-doc-update-conflicts.t etap test suite to eunit

Timeout decreased, added 10K clients case


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

Branch: refs/heads/1963-eunit
Commit: 5b09c8d12b1475203c2c92c823c1fd86a9e734b2
Parents: 1ed2ea2
Author: Alexander Shorin <kx...@gmail.com>
Authored: Wed May 21 18:58:12 2014 +0400
Committer: Alexander Shorin <kx...@gmail.com>
Committed: Tue Jun 3 03:46:07 2014 +0400

----------------------------------------------------------------------
 test/couchdb/Makefile.am                        |   1 +
 test/couchdb/couchdb_update_conflicts_tests.erl | 237 +++++++++++++++++++
 test/etap/074-doc-update-conflicts.t            | 218 -----------------
 test/etap/Makefile.am                           |   1 -
 4 files changed, 238 insertions(+), 219 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/5b09c8d1/test/couchdb/Makefile.am
----------------------------------------------------------------------
diff --git a/test/couchdb/Makefile.am b/test/couchdb/Makefile.am
index 58429e7..2c6dd64 100644
--- a/test/couchdb/Makefile.am
+++ b/test/couchdb/Makefile.am
@@ -30,6 +30,7 @@ eunit_files = \
     couch_db_tests.erl \
     couchdb_views_tests.erl \
     couch_changes_tests.erl \
+    couchdb_update_conflicts_tests.erl \
     test_request.erl \
     couchdb_tests.hrl
 

http://git-wip-us.apache.org/repos/asf/couchdb/blob/5b09c8d1/test/couchdb/couchdb_update_conflicts_tests.erl
----------------------------------------------------------------------
diff --git a/test/couchdb/couchdb_update_conflicts_tests.erl b/test/couchdb/couchdb_update_conflicts_tests.erl
new file mode 100644
index 0000000..860b728
--- /dev/null
+++ b/test/couchdb/couchdb_update_conflicts_tests.erl
@@ -0,0 +1,237 @@
+% 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.
+
+-module(couchdb_update_conflicts_tests).
+
+-include_lib("../../src/couchdb/couch_db.hrl").
+-include_lib("couchdb_tests.hrl").
+
+-define(i2l(I), integer_to_list(I)).
+-define(ADMIN_USER, {#user_ctx{roles=[<<"_admin">>]}}).
+-define(DOC_ID, <<"foobar">>).
+-define(NUM_CLIENTS, [100, 500, 1000, 2000, 5000, 10000]).
+-define(TIMEOUT, 10000).
+
+
+start() ->
+    couch_server_sup:start_link(?CONFIG_CHAIN),
+    couch_config:set("couchdb", "delayed_commits", "true", false),
+    ok.
+
+stop(_) ->
+    couch_server_sup:stop(),
+    ok.
+
+setup() ->
+    DbName = ?tempdb(),
+    {ok, Db} = couch_db:create(DbName, [?ADMIN_USER, overwrite]),
+    Doc = couch_doc:from_json_obj({[{<<"_id">>, ?DOC_ID},
+                                    {<<"value">>, 0}]}),
+    {ok, Rev} = couch_db:update_doc(Db, Doc, []),
+    ok = couch_db:close(Db),
+    RevStr = couch_doc:rev_to_str(Rev),
+    {DbName, RevStr}.
+setup(_) ->
+    setup().
+
+teardown({DbName, _}) ->
+    ok = couch_server:delete(DbName, []),
+    ok.
+teardown(_, {DbName, _RevStr}) ->
+    teardown({DbName, _RevStr}).
+
+
+view_indexes_cleanup_test_() ->
+    {
+        "Update conflicts",
+        {
+            setup,
+            fun start/0, fun stop/1,
+            [
+                concurrent_updates(),
+                couchdb_188()
+            ]
+        }
+    }.
+
+concurrent_updates()->
+    {
+        "Concurrent updates",
+        {
+            foreachx,
+            fun setup/1, fun teardown/2,
+            [{NumClients, fun should_concurrently_update_doc/2}
+             || NumClients <- ?NUM_CLIENTS]
+        }
+    }.
+
+couchdb_188()->
+    {
+        "COUCHDB-188",
+        {
+            foreach,
+            fun setup/0, fun teardown/1,
+            [fun should_bulk_create_delete_doc/1]
+        }
+    }.
+
+
+should_concurrently_update_doc(NumClients, {DbName, InitRev})->
+     {?i2l(NumClients) ++ " clients",
+      {inorder,
+       [{"update doc",
+         {timeout, ?TIMEOUT div 1000,
+          ?_test(concurrent_doc_update(NumClients, DbName, InitRev))}},
+        {"ensure in single leaf",
+         ?_test(ensure_in_single_revision_leaf(DbName))}]}}.
+
+should_bulk_create_delete_doc({DbName, InitRev})->
+    ?_test(bulk_delete_create(DbName, InitRev)).
+
+
+concurrent_doc_update(NumClients, DbName, InitRev) ->
+    Clients = lists:map(
+        fun(Value) ->
+            ClientDoc = couch_doc:from_json_obj({[
+                {<<"_id">>, ?DOC_ID},
+                {<<"_rev">>, InitRev},
+                {<<"value">>, Value}
+            ]}),
+            Pid = spawn_client(DbName, ClientDoc),
+            {Value, Pid, erlang:monitor(process, Pid)}
+        end,
+        lists:seq(1, NumClients)),
+
+    lists:foreach(fun({_, Pid, _}) -> Pid ! go end, Clients),
+
+    {NumConflicts, SavedValue} = lists:foldl(
+        fun({Value, Pid, MonRef}, {AccConflicts, AccValue}) ->
+            receive
+                {'DOWN', MonRef, process, Pid, {ok, _NewRev}} ->
+                    {AccConflicts, Value};
+                {'DOWN', MonRef, process, Pid, conflict} ->
+                    {AccConflicts + 1, AccValue};
+                {'DOWN', MonRef, process, Pid, Error} ->
+                    erlang:error({assertion_failed,
+                         [{module, ?MODULE},
+                          {line, ?LINE},
+                          {reason, "Client " ++ ?i2l(Value)
+                                             ++ " got update error: "
+                                             ++ couch_util:to_list(Error)}]})
+            after ?TIMEOUT div 2 ->
+                 erlang:error({assertion_failed,
+                         [{module, ?MODULE},
+                          {line, ?LINE},
+                          {reason, "Timeout waiting for client "
+                                   ++ ?i2l(Value) ++ " to die"}]})
+            end
+        end, {0, nil}, Clients),
+    ?assertEqual(NumClients - 1, NumConflicts),
+
+    {ok, Db} = couch_db:open_int(DbName, []),
+    {ok, Leaves} = couch_db:open_doc_revs(Db, ?DOC_ID, all, []),
+    ok = couch_db:close(Db),
+    ?assertEqual(1, length(Leaves)),
+
+    [{ok, Doc2}] = Leaves,
+    {JsonDoc} = couch_doc:to_json_obj(Doc2, []),
+    ?assertEqual(SavedValue, couch_util:get_value(<<"value">>, JsonDoc)).
+
+ensure_in_single_revision_leaf(DbName) ->
+    {ok, Db} = couch_db:open_int(DbName, []),
+    {ok, Leaves} = couch_db:open_doc_revs(Db, ?DOC_ID, all, []),
+    ok = couch_db:close(Db),
+    [{ok, Doc}] = Leaves,
+
+    stop(ok),
+    start(),
+    
+    {ok, Db2} = couch_db:open_int(DbName, []),
+    {ok, Leaves2} = couch_db:open_doc_revs(Db2, ?DOC_ID, all, []),
+    ok = couch_db:close(Db2),
+    ?assertEqual(1, length(Leaves2)),
+
+    [{ok, Doc2}] = Leaves,
+    ?assertEqual(Doc, Doc2).
+    
+bulk_delete_create(DbName, InitRev) ->
+    {ok, Db} = couch_db:open_int(DbName, []),
+    
+    DeletedDoc = couch_doc:from_json_obj({[
+        {<<"_id">>, ?DOC_ID},
+        {<<"_rev">>, InitRev},
+        {<<"_deleted">>, true}
+    ]}),
+    NewDoc = couch_doc:from_json_obj({[
+        {<<"_id">>, ?DOC_ID},
+        {<<"value">>, 666}
+    ]}),
+
+    {ok, Results} = couch_db:update_docs(Db, [DeletedDoc, NewDoc], []),
+    ok = couch_db:close(Db),
+
+    ?assertEqual(2, length([ok || {ok, _} <- Results])),
+    [{ok, Rev1}, {ok, Rev2}] = Results,
+    
+    {ok, Db2} = couch_db:open_int(DbName, []),
+    {ok, [{ok, Doc1}]} = couch_db:open_doc_revs(
+        Db2, ?DOC_ID, [Rev1], [conflicts, deleted_conflicts]),
+    {ok, [{ok, Doc2}]} = couch_db:open_doc_revs(
+        Db2, ?DOC_ID, [Rev2], [conflicts, deleted_conflicts]),
+    ok = couch_db:close(Db2),
+
+    {Doc1Props} = couch_doc:to_json_obj(Doc1, []),
+    {Doc2Props} = couch_doc:to_json_obj(Doc2, []),
+
+    %% Document was deleted
+    ?assert(couch_util:get_value(<<"_deleted">>, Doc1Props)),
+    %% New document not flagged as deleted
+    ?assertEqual(undefined, couch_util:get_value(<<"_deleted">>,
+                                                 Doc2Props)),
+    %% New leaf revision has the right value
+    ?assertEqual(666, couch_util:get_value(<<"value">>,
+                                           Doc2Props)),
+    %% Deleted document has no conflicts
+    ?assertEqual(undefined, couch_util:get_value(<<"_conflicts">>,
+                                                 Doc1Props)),
+    %% Deleted document has no deleted conflicts
+    ?assertEqual(undefined, couch_util:get_value(<<"_deleted_conflicts">>,
+                                                 Doc1Props)),
+    %% New leaf revision doesn't have conflicts
+    ?assertEqual(undefined, couch_util:get_value(<<"_conflicts">>,
+                                                 Doc1Props)),
+    %% New leaf revision doesn't have deleted conflicts
+    ?assertEqual(undefined, couch_util:get_value(<<"_deleted_conflicts">>,
+                                                 Doc1Props)),
+
+    %% Deleted revision has position 2
+    ?assertEqual(2, element(1, Rev1)),
+    %% New leaf revision has position 1
+    ?assertEqual(1, element(1, Rev2)).
+
+
+spawn_client(DbName, Doc) ->
+    spawn(fun() ->
+        {ok, Db} = couch_db:open_int(DbName, []),
+        receive
+            go -> ok
+        end,
+        erlang:yield(),
+        Result = try
+            couch_db:update_doc(Db, Doc, [])
+        catch _:Error ->
+            Error
+        end,
+        ok = couch_db:close(Db),
+        exit(Result)
+    end).
+

http://git-wip-us.apache.org/repos/asf/couchdb/blob/5b09c8d1/test/etap/074-doc-update-conflicts.t
----------------------------------------------------------------------
diff --git a/test/etap/074-doc-update-conflicts.t b/test/etap/074-doc-update-conflicts.t
deleted file mode 100755
index 09d0633..0000000
--- a/test/etap/074-doc-update-conflicts.t
+++ /dev/null
@@ -1,218 +0,0 @@
-#!/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(i2l(I), integer_to_list(I)).
-
-test_db_name() -> <<"couch_test_update_conflicts">>.
-
-
-main(_) ->
-    test_util:init_code_path(),
-
-    etap:plan(35),
-    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()),
-    couch_config:set("couchdb", "delayed_commits", "true", false),
-
-    lists:foreach(
-        fun(NumClients) -> test_concurrent_doc_update(NumClients) end,
-        [100, 500, 1000, 2000, 5000]),
-
-    test_bulk_delete_create(),
-
-    couch_server_sup:stop(),
-    ok.
-
-
-% Verify that if multiple clients try to update the same document
-% simultaneously, only one of them will get success response and all
-% the other ones will get a conflict error. Also validate that the
-% client which got the success response got its document version
-% persisted into the database.
-test_concurrent_doc_update(NumClients) ->
-    {ok, Db} = create_db(test_db_name()),
-    Doc = couch_doc:from_json_obj({[
-        {<<"_id">>, <<"foobar">>},
-        {<<"value">>, 0}
-    ]}),
-    {ok, Rev} = couch_db:update_doc(Db, Doc, []),
-    ok = couch_db:close(Db),
-    RevStr = couch_doc:rev_to_str(Rev),
-    etap:diag("Created first revision of test document"),
-
-    etap:diag("Spawning " ++ ?i2l(NumClients) ++
-        " clients to update the document"),
-    Clients = lists:map(
-        fun(Value) ->
-            ClientDoc = couch_doc:from_json_obj({[
-                {<<"_id">>, <<"foobar">>},
-                {<<"_rev">>, RevStr},
-                {<<"value">>, Value}
-            ]}),
-            Pid = spawn_client(ClientDoc),
-            {Value, Pid, erlang:monitor(process, Pid)}
-        end,
-        lists:seq(1, NumClients)),
-
-    lists:foreach(fun({_, Pid, _}) -> Pid ! go end, Clients),
-    etap:diag("Waiting for clients to finish"),
-
-    {NumConflicts, SavedValue} = lists:foldl(
-        fun({Value, Pid, MonRef}, {AccConflicts, AccValue}) ->
-            receive
-            {'DOWN', MonRef, process, Pid, {ok, _NewRev}} ->
-                {AccConflicts, Value};
-            {'DOWN', MonRef, process, Pid, conflict} ->
-                {AccConflicts + 1, AccValue};
-            {'DOWN', MonRef, process, Pid, Error} ->
-                etap:bail("Client " ++ ?i2l(Value) ++
-                    " got update error: " ++ couch_util:to_list(Error))
-            after 60000 ->
-                etap:bail("Timeout waiting for client " ++ ?i2l(Value) ++ " to die")
-            end
-        end,
-        {0, nil},
-        Clients),
-
-    etap:diag("Verifying client results"),
-    etap:is(
-        NumConflicts,
-        NumClients - 1,
-        "Got " ++ ?i2l(NumClients - 1) ++ " client conflicts"),
-
-    {ok, Db2} = couch_db:open_int(test_db_name(), []),
-    {ok, Leaves} = couch_db:open_doc_revs(Db2, <<"foobar">>, all, []),
-    ok = couch_db:close(Db2),
-    etap:is(length(Leaves), 1, "Only one document revision was persisted"),
-    [{ok, Doc2}] = Leaves,
-    {JsonDoc} = couch_doc:to_json_obj(Doc2, []),
-    etap:is(
-        couch_util:get_value(<<"value">>, JsonDoc),
-        SavedValue,
-        "Persisted doc has the right value"),
-
-    ok = timer:sleep(1000),
-    etap:diag("Restarting the server"),
-    couch_server_sup:stop(),
-    ok = timer:sleep(1000),
-    couch_server_sup:start_link(test_util:config_files()),
-
-    {ok, Db3} = couch_db:open_int(test_db_name(), []),
-    {ok, Leaves2} = couch_db:open_doc_revs(Db3, <<"foobar">>, all, []),
-    ok = couch_db:close(Db3),
-    etap:is(length(Leaves2), 1, "Only one document revision was persisted"),
-    [{ok, Doc3}] = Leaves,
-    etap:is(Doc3, Doc2, "Got same document after server restart"),
-
-    delete_db(Db3).
-
-
-% COUCHDB-188
-test_bulk_delete_create() ->
-    {ok, Db} = create_db(test_db_name()),
-    Doc = couch_doc:from_json_obj({[
-        {<<"_id">>, <<"foobar">>},
-        {<<"value">>, 0}
-    ]}),
-    {ok, Rev} = couch_db:update_doc(Db, Doc, []),
-
-    DeletedDoc = couch_doc:from_json_obj({[
-        {<<"_id">>, <<"foobar">>},
-        {<<"_rev">>, couch_doc:rev_to_str(Rev)},
-        {<<"_deleted">>, true}
-    ]}),
-    NewDoc = couch_doc:from_json_obj({[
-        {<<"_id">>, <<"foobar">>},
-        {<<"value">>, 666}
-    ]}),
-
-    {ok, Results} = couch_db:update_docs(Db, [DeletedDoc, NewDoc], []),
-    ok = couch_db:close(Db),
-
-    etap:is(length([ok || {ok, _} <- Results]), 2,
-        "Deleted and non-deleted versions got an ok reply"),
-
-    [{ok, Rev1}, {ok, Rev2}] = Results,
-    {ok, Db2} = couch_db:open_int(test_db_name(), []),
-
-    {ok, [{ok, Doc1}]} = couch_db:open_doc_revs(
-        Db2, <<"foobar">>, [Rev1], [conflicts, deleted_conflicts]),
-    {ok, [{ok, Doc2}]} = couch_db:open_doc_revs(
-        Db2, <<"foobar">>, [Rev2], [conflicts, deleted_conflicts]),
-    ok = couch_db:close(Db2),
-
-    {Doc1Props} = couch_doc:to_json_obj(Doc1, []),
-    {Doc2Props} = couch_doc:to_json_obj(Doc2, []),
-
-    etap:is(couch_util:get_value(<<"_deleted">>, Doc1Props), true,
-        "Document was deleted"),
-    etap:is(couch_util:get_value(<<"_deleted">>, Doc2Props), undefined,
-        "New document not flagged as deleted"),
-    etap:is(couch_util:get_value(<<"value">>, Doc2Props), 666,
-        "New leaf revision has the right value"),
-    etap:is(couch_util:get_value(<<"_conflicts">>, Doc1Props), undefined,
-        "Deleted document has no conflicts"),
-    etap:is(couch_util:get_value(<<"_deleted_conflicts">>, Doc1Props), undefined,
-        "Deleted document has no deleted conflicts"),
-    etap:is(couch_util:get_value(<<"_conflicts">>, Doc2Props), undefined,
-        "New leaf revision doesn't have conflicts"),
-    etap:is(couch_util:get_value(<<"_deleted_conflicts">>, Doc2Props), undefined,
-        "New leaf revision doesn't have deleted conflicts"),
-
-    etap:is(element(1, Rev1), 2, "Deleted revision has position 2"),
-    etap:is(element(1, Rev2), 1, "New leaf revision has position 1"),
-
-    delete_db(Db2).
-
-
-spawn_client(Doc) ->
-    spawn(fun() ->
-        {ok, Db} = couch_db:open_int(test_db_name(), []),
-        receive go -> ok end,
-        erlang:yield(),
-        Result = try
-            couch_db:update_doc(Db, Doc, [])
-        catch _:Error ->
-            Error
-        end,
-        ok = couch_db:close(Db),
-        exit(Result)
-    end).
-
-
-create_db(DbName) ->
-    couch_db:create(
-        DbName,
-        [{user_ctx, #user_ctx{roles = [<<"_admin">>]}}, overwrite]).
-
-
-delete_db(Db) ->
-    ok = couch_server:delete(
-        couch_db:name(Db), [{user_ctx, #user_ctx{roles = [<<"_admin">>]}}]).

http://git-wip-us.apache.org/repos/asf/couchdb/blob/5b09c8d1/test/etap/Makefile.am
----------------------------------------------------------------------
diff --git a/test/etap/Makefile.am b/test/etap/Makefile.am
index 517705f..446a3f3 100644
--- a/test/etap/Makefile.am
+++ b/test/etap/Makefile.am
@@ -36,7 +36,6 @@ fixture_files = \
     fixtures/test.couch
 
 tap_files = \
-    074-doc-update-conflicts.t \
     075-auth-cache.t \
     076-file-compression.t \
     077-couch-db-fast-db-delete-create.t \