summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Vatamaniuc <vatamane@apache.org>2020-02-28 13:30:44 -0500
committerNick Vatamaniuc <vatamane@apache.org>2020-02-28 15:25:53 -0500
commit6f90214a4c38fc9c61d9cfd05f387710d60ac360 (patch)
treeb6824163a151a44bd640ce8a629404d0a8c5fea5
parent657420716dd5bcd57becde682ae89e6d80909f86 (diff)
downloadcouchdb-fix-concurrent-db-creation-2613-3.0.x.tar.gz
Handle race condition during concurrent db creationfix-concurrent-db-creation-2613-3.0.x
Previously concurrent db creation requests could get past db_exists/1 and try to create the shard document. The first one would succeed but the others would fail with a `500 {"error":"error", "reason":"conflict"}` error instead of `412`. Handle the race condition by checking if db exists after getting a conflict error and return a 412 response. Fixes: #2613
-rw-r--r--src/fabric/src/fabric_db_create.erl6
-rw-r--r--src/fabric/test/eunit/fabric_db_create_tests.erl53
2 files changed, 59 insertions, 0 deletions
diff --git a/src/fabric/src/fabric_db_create.erl b/src/fabric/src/fabric_db_create.erl
index 03fabb4ea..1048cb0bb 100644
--- a/src/fabric/src/fabric_db_create.erl
+++ b/src/fabric/src/fabric_db_create.erl
@@ -37,6 +37,12 @@ go(DbName, Options) ->
case {CreateShardResult, create_shard_db_doc(Doc)} of
{ok, {ok, Status}} ->
Status;
+ {ok, {error, conflict} = ShardDocError} ->
+ % Check if it is just a race to create the shard doc
+ case db_exists(DbName) of
+ true -> {error, file_exists};
+ false -> ShardDocError
+ end;
{file_exists, {ok, _}} ->
{error, file_exists};
{_, Error} ->
diff --git a/src/fabric/test/eunit/fabric_db_create_tests.erl b/src/fabric/test/eunit/fabric_db_create_tests.erl
new file mode 100644
index 000000000..8e5b1085e
--- /dev/null
+++ b/src/fabric/test/eunit/fabric_db_create_tests.erl
@@ -0,0 +1,53 @@
+% 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(fabric_db_create_tests).
+
+
+-include_lib("couch/include/couch_eunit.hrl").
+-include_lib("couch/include/couch_db.hrl").
+-include_lib("mem3/include/mem3.hrl").
+
+
+-define(TDEF(A), {atom_to_list(A), fun A/0}).
+
+
+main_test_() ->
+ {
+ setup,
+ fun setup/0,
+ fun teardown/1,
+ [
+ ?TDEF(t_handle_shard_doc_conflict)
+ ]
+ }.
+
+
+setup() ->
+ test_util:start_couch([fabric]).
+
+
+teardown(Ctx) ->
+ meck:unload(),
+ test_util:stop_couch(Ctx).
+
+
+t_handle_shard_doc_conflict() ->
+ DbName = ?tempdb(),
+ meck:new(mem3, [passthrough]),
+ meck:new(fabric_util, [passthrough]),
+ ok = meck:sequence(mem3, shards, 1, [
+ fun(_) -> meck:raise(error, database_does_not_exist) end,
+ [#shard{dbname = DbName}]
+ ]),
+ meck:expect(fabric_util, recv, 4, {error, conflict}),
+ ?assertEqual({error, file_exists}, fabric_db_create:go(DbName, [])).