diff options
author | Filipe David Borba Manana <fdmanana@apache.org> | 2011-11-18 17:08:11 +0000 |
---|---|---|
committer | Filipe David Borba Manana <fdmanana@apache.org> | 2011-11-18 17:44:17 +0000 |
commit | 1cb7d885ff1a88a725e4b57105784584565ea470 (patch) | |
tree | ad3f340bbce386d1ee5d524513f55c4272630c68 | |
parent | 84084cb581b584cb559a88726f9cfe5f11e9ba81 (diff) | |
download | couchdb-1cb7d885ff1a88a725e4b57105784584565ea470.tar.gz |
Add test test/etap/074-doc-update-conflicts.t
Test motivated by COUCHDB-911.
-rwxr-xr-x | test/etap/074-doc-update-conflicts.t | 145 | ||||
-rw-r--r-- | test/etap/Makefile.am | 1 |
2 files changed, 146 insertions, 0 deletions
diff --git a/test/etap/074-doc-update-conflicts.t b/test/etap/074-doc-update-conflicts.t new file mode 100755 index 000000000..e1887cd37 --- /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">>]}}]). diff --git a/test/etap/Makefile.am b/test/etap/Makefile.am index 164c1e59f..b27404f30 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 \ |