You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by fd...@apache.org on 2011/11/18 20:31:27 UTC

git commit: Add test test/etap/074-doc-update-conflicts.t

Updated Branches:
  refs/heads/master 84084cb58 -> 1cb7d885f


Add test test/etap/074-doc-update-conflicts.t

Test motivated by COUCHDB-911.


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

Branch: refs/heads/master
Commit: 1cb7d885ff1a88a725e4b57105784584565ea470
Parents: 84084cb
Author: Filipe David Borba Manana <fd...@apache.org>
Authored: Fri Nov 18 17:08:11 2011 +0000
Committer: Filipe David Borba Manana <fd...@apache.org>
Committed: Fri Nov 18 17:44:17 2011 +0000

----------------------------------------------------------------------
 test/etap/074-doc-update-conflicts.t |  145 +++++++++++++++++++++++++++++
 test/etap/Makefile.am                |    1 +
 2 files changed, 146 insertions(+), 0 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/1cb7d885/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
new file mode 100755
index 0000000..e1887cd
--- /dev/null
+++ b/test/etap/074-doc-update-conflicts.t
@@ -0,0 +1,145 @@
+#!/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.
+
+-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(10),
+    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()),
+
+    lists:foreach(
+        fun(NumClients) -> test_concurrent_doc_update(NumClients) end,
+        [100, 500, 1000, 2000, 5000]),
+
+    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, Doc2} = couch_db:open_doc(Db2, <<"foobar">>, []),
+    ok = couch_db:close(Db2),
+    {JsonDoc} = couch_doc:to_json_obj(Doc2, []),
+    etap:is(
+        couch_util:get_value(<<"value">>, JsonDoc),
+        SavedValue,
+        "Persisted doc has the right value"),
+
+    delete_db(Db).
+
+
+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/1cb7d885/test/etap/Makefile.am
----------------------------------------------------------------------
diff --git a/test/etap/Makefile.am b/test/etap/Makefile.am
index 164c1e5..b27404f 100644
--- a/test/etap/Makefile.am
+++ b/test/etap/Makefile.am
@@ -56,6 +56,7 @@ EXTRA_DIST = \
     070-couch-db.t \
     072-cleanup.t \
     073-changes.t \
+    074-doc-update-conflicts.t \
     080-config-get-set.t \
     081-config-override.1.ini \
     081-config-override.2.ini \